是否可以在 mongodb 中按四分位数有效聚合



例如,假设我有 10,000 个要aggregate()的已排序文档。但我想将它们分为四分位数:前 25%,25% - 50%,50% - 75%,后 25%。有没有办法在一个管道中做到这一点,而不是必须为每个四分位数做 4 个单独的管道?

像这样:

aggregate()
- Transform into {quartile1: [list of docs], quartile2: [list of docs], ...}
- Run other pipeline commands

还是需要运行 4 个单独的aggregate()管道?

谢谢!

对于你所问的,"聚合框架可以解决这个问题吗?",那么答案是否定的,它不能。另一方面,你可以用mapReduce做这样的事情。但真正我想介绍的是它的可靠性,以及"有什么意义?

在这里表达怀疑的最好方法是充分解释事情。

聚合框架不能做这种事情,因为它在处理 10,000 个文档的过程中没有"当前位置"的概念。为此,您需要某种"变量",该变量随着每个"排序"项目的处理而递增。

您可以使用该方法根据要"排序"的值"标记"项目。但问题仍然是"你怎么知道"特定值在整个结果集中的排名。因此,除非有明确的方法来做到这一点,否则您无法投影这样的字段。

只有当您准备使用不一定是所有结果的"四分之一除法"的"设置范围"时,您才能使用 .aggregate() 来做到这一点:

db.collection.aggregate([
    { "$project": {
        "grouping": {
            "$cond": [
                { "$lt": [ "$score", 25 ]  },
                3,
                { "$cond": [
                    { "$lt": [ "$score", 50 ] },
                    2,
                    { "$cond": [
                        { "$lt": [ "$score", 75 ] },
                        1,
                        0
                    ]}
                ]}
            ]
        },
        "score": 1,
        "otherField": 1
    }},
    { "$sort": { "grouping"  1, "score": -1 }
])

另一方面,.mapReduce()确实可以访问这样的全局变量。所以基本上可以检查计数器,以查看它是否在您预期的分组中。基本形式:

db.collection.mapReduce(
    function() {
        counter++;
        if ( counter % ( total / 4 ) == 0 )
            grouping++;
        var id = this._id;
        delete this._id;
        emit({ "grouping": grouping, "_id": id },this);
    },
    function() {}, // no need for a reducer
    {
        "out": { "replace": "results" },
        "scope": { "counter": 0, "grouping": 0, "total": 10000 },
        "sort": { "score": -1 }
    }
)

它基本上可以做你想要的。但不是以真正灵活的方式或非常可靠的方式。主要是因为在大多数现实世界中,无法保证始终有 10,000 个结果,通常特别是如果运行一个查询的条件来获取计数,另一个查询将结果"标记"到它们的分组中。

因此,考虑到这里根本没有真正的"聚合"发生,那么最好的方法可能是简单地查询数据并将其列出:

var cursor = db.collection.find({}).sort({ "score": -1 });
var total = cursor.count();
var counter = 0,
    grouping = 0;
cursor.forEach(function(doc) {
    counter++;
    if ( counter % ( total / 4 ) == 0 )
        grouping++;
    doc._id = { "grouping": grouping, "_id": doc._id };
    // Do something with "doc"
});

不是很优雅,但指出了基本技术。

还要注意的是,按照您的建议[]数组并不是一个非常好的主意。即使在 10,000 个文档场景中,生成的 2,500 个元素数组和单个文档响应中的 10,000 个项目,也可能会"炸毁"16MB BSON 限制。至少它不是很易于管理,最好用光标处理。

因此,您可以选择服务器来"标记"这些项目,也可以在读取它们时"标记"它们。至少在后一种情况下,您可以访问结果的"光标"

我认为需要 4 个管道,就像在 MONGO 文档中找到的那样。

db.articles.aggregate( [
                        { $match : { score : { $gt : 70, $lte : 90 } } },
                        { $group: { _id: null, count: { $sum: 1 } } }
                       ] );

但与所有正常的数据库规则相反,请考虑双重浸入或输入数据两次。 一次用于基数字段,一次用于四分位数字段。 这种方法很模糊,但允许快速读取; 可以在索引字段上进行简单的查找,并执行单个AGG。

{name: cartman, score: 56, quartile: 3 }
{name: kenny, score: 36, quartile: 2 }
{name: kyle, score: 76, quartile: 4 }
db.scores.find( {"quartile" : 3 });
db.scores.aggregate( [
                        { $group: { _id: null, count: { $quartile: 1 } } }
                       ] );

相关内容

  • 没有找到相关文章

最新更新