按计数过滤的子文档数组元素排序

时间:2015-10-27 00:09:09

标签: javascript mongodb mongoose mongodb-query aggregation-framework

我目前有一个类似的MongoDB集合:

{
    {
        "_id": ObjectId,
        "user_id": Number,
        "updates": [
            {
                "_id": ObjectId,
                "mode": Number,
                "score": Number
            },
            {
                "_id": ObjectId,
                "mode": Number,
                "score": Number
            },
            {
                "_id": ObjectId,
                "mode": Number,
                "score": Number
            }
        ]
    }
}

我希望找到一种方法来查找每种模式下更新次数最多的用户。例如,如果我指定模式0,我希望它以mode: 0的最大更新次数加载用户。

这在MongoDB中是否可行?它不需要是一个快速算法,因为它将被缓存很长一段时间,它将异步运行。

2 个答案:

答案 0 :(得分:1)

最快的方法是将文档中每个“模式”的计数存储为另一个字段,然后您可以对其进行排序:

var update = { 
   "$push": { "updates": updateDoc },
};

var countDoc = {};
countDoc["counts." + updateDoc.mode] = 1;

update["$inc"] = countDoc;

Model.update(
    { "_id": id },
    update,
    function(err,numAffected) {

    }
);

哪个会使用$inc为每个“模式”值递增“计数”字段,作为推送到“更新”数组的每个“模式”的键。所有计算都在更新时进行,因此速度很快,可以对该值进行排序的查询也是如此:

Model.find({ "updates.mode": 0 }).sort({ "counts.0": -1 }).exec(function(err,users) {

});

如果您不想或不能存储此类字段,则另一个选项是在查询时使用.aggregate()进行计算:

Model.aggregate(
    [
        { "$match": { "updates.mode": 0 } },
        { "$project": {
            "user_id": 1,
            "updates": 1,
            "count": {
                "$size": {
                    "$setDifference": [
                        { "$map": {
                            "input": "$updates",
                            "as": "el",
                            "in": {
                                "$cond": [
                                    { "$eq": [ "$$el.mode", 0 ] },
                                    "$$el",
                                    false
                                ]
                            }
                        }},
                        [false]
                    ]
                }
            }
        }},
        { "$sort": { "count": -1 } }
    ],
    function(err,results) {

    }
);

这不错,因为过滤数组并获得$size是相当有效的,但它没有使用存储值那么快。

$map运算符允许对$cond测试的数组元素进行内联处理,以查看它是返回匹配还是false。然后$setDifference删除所有错误值。比使用$unwind更好的过滤数组内容的方法,这可以显着降低速度,除非您打算在文档之间聚合数组内容,否则不应该使用它。

但更好的方法是存储计数的值,因为这不需要运行时计算,甚至可以使用索引

答案 1 :(得分:0)

我认为这是这个问题的重复:

Mongo find query for longest arrays inside object

接受的答案似乎完全符合你的要求。

db.collection.aggregate( [
  { $unwind : "$l" },
  { $group : { _id : "$_id", len : { $sum : 1 } } },
  { $sort : { len : -1 } },
  { $limit : 25 }
] )

只需将"$l"替换为"$updates"

[edit:]你可能不希望结果限制为25,所以你也应该摆脱{ $limit : 25 }