mongodb - 使用聚合在mongoDB中限制和排序每个组

标签 mongodb sorting mongodb-query aggregation-framework

如何在 mongoDB 中对每个组进行排序和限制。

考虑以下数据:

Country:USA,name:xyz,rating:10,id:x
Country:USA,name:xyz,rating:10,id:y
Country:USA,name:xyz,rating:10,id:z
Country:USA,name:abc,rating:5,id:x
Country:India,name:xyz,rating:5,id:x
Country:India,name:xyz,rating:5,id:y
Country:India,name:abc,rating:10,id:z
Country:India,name:abc,rating:10,id:x

现在说我将按国家分组并按评分排序,并将每组的数据限制为 2。

所以答案是:

Country:USA
name:xyz,rating:10,id:x
name:xyz,rating:10,id:y
Country:India
name:abc,rating:10,id:x
name:abc,rating:10,id:z

我只想使用聚合框架来完成此操作。

我尝试在聚合中包含排序以进行评级,但简单的查询在处理后没有结果。

最佳答案

您最好的选择是为每个“国家/地区”运行单独的查询(最好是并行)并返回组合结果。查询非常简单,只需对评分值进行排序后返回前 2 个值,即使您需要执行多个查询以获得完整结果,也会很快执行。

现在甚至在不久的将来,聚合框架都不太适合这种情况。问题是没有这样的运算符以任何方式“限制”任何分组的结果。因此,为了做到这一点,您基本上需要将所有内容 $push 放入一个数组中并从中提取“top n”值。

当前需要执行的操作非常糟糕,核心问题是结果可能会超过大多数真实数据源上每个文档 16MB 的 BSON 限制。

还有一个 n 复杂性,因为您现在必须这样做。但只是为了演示 2 个项目:

db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        },
        "first": { 
            "$first": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},

    // Unwind the array
    { "$unwind": "results" },

    // Remove the seen result from the array
    { "$redact": {
        "$cond": {
            "if": { "$eq": [ "$results.id", "$first.id" ] },
            "then": "$$PRUNE",
            "else": "$$KEEP"
        }
    }},

    // Group to return the second result which is now first on stack
    { "$group": {
        "_id": "$_id",
        "first": { "$first": "$first" },
        "second": { 
            "$first": {
                "name": "$results.name", 
                "rating": "$results.rating",
                "id": "$results.id"
            }
        }
    }},

    // Optionally put these in an array format
    { "$project": {
        "results": { 
            "$map": {
                "input": ["A","B"],
                "as": "el",
                "in": {
                    "$cond": {
                        "if": { "$eq": [ "$$el", "A" ] },
                        "then": "$first",
                        "else": "$second"
                    }
                }
            }
        }
    }}
])

这得到了结果,但它不是一个很好的方法,并且在迭代更高的限制时变得更加复杂,甚至在某些情况下分组返回的结果可能少于 n 个。

目前的开发系列( 3.1.x )在编写时有一个 $slice 运算符,这使得这更简单一些,但仍然有相同的“大小”陷阱:

db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},
    { "$project": {
        "results": { "$slice": [ "$results", 2 ] }
    }}
])

但基本上直到聚合框架有某种方式“限制”由 $push 或类似的分组“限制”运算符产生的项目数量之前,聚合框架并不是真正的最佳解决方案对于这类问题。

这样的简单查询:

db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)

为每个不同的国家/地区运行,理想情况下,通过线程的事件循环并行处理并结合结果产生目前最优化的方法。他们只获取需要的内容,这是聚合框架在此类分组中尚无法处理的大问题。

因此,请寻求支持以针对您选择的语言以最佳方式执行此“组合查询结果”,因为它比将其扔到聚合框架中要简单得多,性能要高得多。

关于mongodb - 使用聚合在mongoDB中限制和排序每个组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33458107/

相关文章:

mongodb - 如何对数组字段中的子文档进行排序?

java - Vava 查询 MongoDB : find event in a specific date range

mongodb - 文档数据库中的多对多

javascript - 在 jQuery 中设置无默认排序 ("aaSorting": []) for specific ID or entire project

excel - 根据每个条目的最后两个字符使用 VBA 对表进行排序

java - Spring 数据: MongoDB: Aggregation: group by nested object

java - 从上到下遍历具有两个以上子节点的树以获取java中的值,然后自下而上遍历以再次调整值

java - 在mongodb中使用AND查询嵌入对象

javascript - Mongo $ 和选择器

javascript - jqgrid如何设置排序规则?