MongoDB在索引列上选择count(distinct x) - 计算大型数据集的唯一结果

Era*_*dan 79 mongodb

我已经阅读了几篇文章和示例,并且尚未找到在MongoDB中执行此SQL查询的有效方法(其中有数百万个 文件)

第一次尝试

(例如,从这个几乎重复的问题 - Mongo相当于SQL的SELECT DISTINCT?)

db.myCollection.distinct("myIndexedNonUniqueField").length
Run Code Online (Sandbox Code Playgroud)

显然我得到了这个错误,因为我的数据集非常庞大

Thu Aug 02 12:55:24 uncaught exception: distinct failed: {
        "errmsg" : "exception: distinct too big, 16mb cap",
        "code" : 10044,
        "ok" : 0
}
Run Code Online (Sandbox Code Playgroud)

第二次尝试

我决定尝试做一组

db.myCollection.group({key: {myIndexedNonUniqueField: 1},
                initial: {count: 0}, 
                 reduce: function (obj, prev) { prev.count++;} } );
Run Code Online (Sandbox Code Playgroud)

但我收到此错误消息:

exception: group() can't handle more than 20000 unique keys
Run Code Online (Sandbox Code Playgroud)

第三次尝试

我还没有尝试,但有几个涉及的建议 mapReduce

例如

似乎有一个关于GitHub的拉取请求修复.distinct方法提到它应该只返回一个计数,但它仍然是开放的:https://github.com/mongodb/mongo/pull/34

但是在这一点上我觉得值得问一下,这个主题的最新内容是什么?我是否应该转移到SQL或其他NoSQL DB以获取不同的计数?还是有效率的方法?

更新:

对MongoDB官方文档的评论并不令人鼓舞,这是否准确?

http://www.mongodb.org/display/DOCS/Aggregation#comment-430445808

UPDATE2:

似乎新的聚合框架回答了上述评论...(MongoDB 2.1/2.2及更高版本,开发预览可用,不适用于生产)

http://docs.mongodb.org/manual/applications/aggregation/

Wil*_*m Z 74

1)最简单的方法是通过聚合框架.这需要两个"$ group"命令:第一个按不同的值分组,第二个命令计算所有不同的值

pipeline = [ 
    { $group: { _id: "$myIndexedNonUniqueField"}  },
    { $group: { _id: 1, count: { $sum: 1 } } }
];

//
// Run the aggregation command
//
R = db.runCommand( 
    {
    "aggregate": "myCollection" , 
    "pipeline": pipeline
    }
);
printjson(R);
Run Code Online (Sandbox Code Playgroud)

2)如果你想用Map/Reduce做到这一点,你可以.这也是一个两阶段的过程:在第一阶段,我们构建一个新的集合,其中包含密钥的每个不同值的列表.在第二个中,我们对新集合执行count().

var SOURCE = db.myCollection;
var DEST = db.distinct
DEST.drop();


map = function() {
  emit( this.myIndexedNonUniqueField , {count: 1});
}

reduce = function(key, values) {
  var count = 0;

  values.forEach(function(v) {
    count += v['count'];        // count each distinct value for lagniappe
  });

  return {count: count};
};

//
// run map/reduce
//
res = SOURCE.mapReduce( map, reduce, 
    { out: 'distinct', 
     verbose: true
    }
    );

print( "distinct count= " + res.counts.output );
print( "distinct count=", DEST.count() );
Run Code Online (Sandbox Code Playgroud)

请注意,您无法返回map/reduce内联的结果,因为这可能会超出16MB的文档大小限制.您可以将计算保存在集合中,然后count()集合的大小,或者您可以从mapReduce()的返回值中获取结果数.

  • 我下载了Mongo 2.2 RC0,并使用了你的第一个建议,它的确有效!而且快!谢谢(做得好10gen ...)在这里创建了一个要点(使用快捷方式聚合命令并将其放在一行)https://gist.github.com/3241616 (5认同)

Sta*_*007 37

db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}});
Run Code Online (Sandbox Code Playgroud)

直接结果:

db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}})
   .result[0].count;
Run Code Online (Sandbox Code Playgroud)

  • 类似,但我喜欢它在一条线上的事实.我收到一个错误:"无法读取未定义的属性'0'"删除最后一行,它的工作非常精彩. (2认同)