按筛选的子文档数组元素计数排序

Ordering by count of filtered subdocument array elements

本文关键字:排序 数组元素 文档 筛选      更新时间:2023-09-26

我目前有一个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中可能吗?它不需要是一个快速算法,因为它将被缓存相当长的一段时间,并且它将异步运行。

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

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 更好的过滤数组内容的方法,这会大大减慢速度,除非您打算跨文档聚合数组内容,否则不应使用。

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

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

Mongo 查找对象内最长数组的查询

公认的答案似乎完全符合您的要求。

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

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

[编辑:] 您可能不希望结果限制为 25,因此您也应该摆脱{ $limit : 25 }