【问题标题】:Ordering by count of filtered subdocument array elements按过滤的子文档数组元素的计数排序
【发布时间】:2016-01-26 06:18:57
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

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

    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 }

    【讨论】:

    • 这不是很有效,因为有更好的方法来计算数组,例如$size。您也没有解决要求特定“模式”值的“过滤计数”的问题。还有更有效的方法,例如在添加项目时维护文档本身中每个“模式”的计数。
    • 更关键的方面是按模式过滤。你能把它应用到这个解决方案中吗?
    • @Redback93 评论的重点是这不是一个解决方案,实际上当时还有另一个答案显示如何通过两种有效的方法获得你想要的东西。跨度>
    【解决方案2】:

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

    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 好得多,这会显着减慢处理速度,除非您打算跨文档聚合数组内容,否则不应使用它。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-07
      • 1970-01-01
      • 1970-01-01
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      相关资源
      最近更新 更多