使用聚合框架在PHP / MongoDB中计算数组中的字符串数量

3

我从小就用MySQL,现在因为种种原因必须转向MongoDB。

我写了一个日志,将每个php错误保存在MongoDB集合中。读取错误并不是问题,使用简单的find()非常容易,并且可以使用php数组来获取数据,这一点很棒。

现在我想要一些关于错误的统计数据。我的集合看起来像:

 {
   "_id": ObjectId("51af10ca0e5e723c0a000000"),
   "Errors": {
     "2048": {
       "0": {
         "Message": "Declaration of ADODB_mysqli::MetaIndexes() should be compatible with ADOConnection::MetaIndexes($table, $primary = false, $owner = false)",
         "File": "File.php",
         "Line": NumberInt(29),
         "Time": NumberInt(1370427591)
      }
    },
     "2": {
       "0": {
         "Message": "Error",
         "File": "File.php",
         "Line": NumberInt(29),
         "Time": NumberInt(1370427591)
      },
      "1": {
         "Message": "Error",
         "File": "File.php",
         "Line": NumberInt(29),
         "Time": NumberInt(1370427591)
      }
    },
    "8": {
       "0": {
         "Message": "Undefined index: PluginLastAdded",
         "File": "File.php",
         "Line": NumberInt(36),
         "Time": NumberInt(1370427594)   
      },
       "1": {
         "Message": "Undefined index: PluginLastAdded",
         "File": "File.php",
         "Line": NumberInt(36),
         "Time": NumberInt(1370427594)   
      }
    }
  }
}

现在我想知道此条目中每个错误发生的频率。最好能将其分成2048、2和8的区别列表,然后统计每个错误的数量。
是否可以使用MongoDB的聚合来完成这项工作,而不需要太多的php代码?
任何帮助都将是极好的。在我看来,MongoDB与MySQL完全不同,转换相当困难。

一个问题是你的架构,如果不用 map reduce 的话,就无法轻松地展开和聚合该架构。我个人建议你将其更改为错误代码不是键,而是文档中的字段。 - Sammaye
谢谢,我会尝试这种方法。也许现在我可以找到一个解决方案。 :) - Moe
1个回答

1

为了进一步说明Sammaye上面提到的内容,以下带有真实数组的模式更加适合:

{
    "_id": ObjectId("51af10ca0e5e723c0a000000"),
    "errors": [
        {
            "code": 2048,
            "message": "Declaration of ADODB_mysqli::MetaIndexes() should be compatible with ADOConnection::MetaIndexes($table, $primary = false, $owner = false)",
            "file": "File.php",
            "line": NumberInt(29),
            "time": NumberInt(1370427591)
        },
        {
            "code": 2,
            "message": "Error",
            "file": "File.php",
            "line": NumberInt(29),
            "time": NumberInt(1370427591)
        },
        {
            "code": 2,
            "message": "Error",
            "file": "File.php",
            "line": NumberInt(29),
            "time": NumberInt(1370427591)
        },
        {
            "code": 8,
            "message": "Undefined index: PluginLastAdded",
            "file": "File.php",
            "line": NumberInt(36),
            "time": NumberInt(1370427594)
        },
        {
            "code": 8,
            "message": "Undefined index: PluginLastAdded",
            "file": "File.php",
            "line": NumberInt(36),
            "time": NumberInt(1370427594)
        }
    ]
}

数组结构还使索引和查询更加直观。索引能够索引数组值,而且MongoDB也可以轻松地对数组进行查询。例如,您可以灵活地使用$elemMatch查询特定的错误(可能是代码和文件的组合)。此外,由于errors是一个真正的数组,因此您可以使用各种更新运算符,例如$push$pull
需要考虑的一件事是,嵌套对象限制了索引和编写查询的方式。在您之前的示例中,只有通过Errors.2048.0.Message才能查询第一个错误消息,但使用上面的模式将允许在errors.message上查询。

数组也使得聚合框架成为一个可行的选择,特别是因为它允许您使用其$unwind运算符遍历数组,然后在数组元素内的值上进行$group。除了MongoDB聚合框架文档外,您可能会发现这个演示文稿有所帮助,以视觉方式展示了不同的操作符。

针对您之前关于获取每个代码错误数量的计数问题,以下聚合框架管道将在整个集合中计算:

db.foo.aggregate([
    { $unwind: "$errors" },
    { $group: {
        _id: "$errors.code",
        num: { $sum: 1 }
    }}
]);

最后,我建议将time字段存储为BSON日期(在PHP中是MongoDate),而不是整数。这打开了在聚合框架中使用date operators的选项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接