【问题标题】:MongoDB select count(distinct x) on an indexed column - count unique results for large data setsMongoDB select count(distinct x) on an indexed column - 计算大型数据集的唯一结果
【发布时间】:2012-07-31 17:24:54
【问题描述】:

我浏览了几篇文章和示例,但尚未找到在 MongoDB 中执行此 SQL 查询的有效方法(其中有数百万 rows 文档)

第一次尝试

(例如,来自这个几乎重复的问题 - Mongo equivalent of SQL's SELECT DISTINCT?

db.myCollection.distinct("myIndexedNonUniqueField").length

显然我收到了这个错误,因为我的数据集很大

Thu Aug 02 12:55:24 uncaught exception: distinct failed: {
        "errmsg" : "exception: distinct too big, 16mb cap",
        "code" : 10044,
        "ok" : 0
}

第二次尝试

我决定尝试做一个小组

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

但我收到了这个错误消息:

exception: group() can't handle more than 20000 unique keys

第三次尝试

我还没试过但是有几个建议涉及mapReduce

例如

还有

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

但在这一点上,我认为值得在这里问一下,关于这个主题的最新消息是什么?我应该转移到 SQL 还是其他 NoSQL DB 来进行不同的计数?还是有什么有效的方法?

更新:

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

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

更新2:

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

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

【问题讨论】:

  • 我认为您需要经常这样做,否则性能不会那么重要。在这种情况下,我会将不同的值存储在一个单独的集合中,当您插入新文档时会更新该集合,而不是尝试对这么大的集合执行不同的操作。要么这样,要么我会重新评估我对 MongoDb 的使用,并可能转向其他东西。如您所见,MongoDb 目前并不擅长您正在尝试做的事情。
  • @TimGautier 谢谢,我很害怕,插入所有这些值需要几个小时,我之前应该想到的 :) 我想我现在会花时间将它插入 MySQL对于这些统计数据...
  • 你也可以做一个增量MR,基本上模拟聚合数据的增量索引。我的意思是这取决于你什么时候需要你使用的结果。我可以想象 MySQL 会得到很多 IO 并且不会这样做(我可以杀死一个小型服务器,在索引上仅区分 100k 内联文档),但我认为它在查询这类东西时更灵活.
  • 我不同意 mongo 不擅长这种事情。这种东西是 Mongo 擅长的。
  • 不幸的是,版主删除了我在重复问题上也发布的答案。我不能在那里删除它并在此处重新发布,因此链接:stackoverflow.com/a/33418582/226895

标签: mongodb


【解决方案1】:

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);

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() );

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

【讨论】:

  • 我下载了 Mongo 2.2 RC0,并使用了你的第一个建议,它有效!而且快!谢谢(干得好 10gen...)在这里创建了一个要点(使用了快捷聚合命令并将其放在一行中)gist.github.com/3241616
  • @EranMedan 我应该警告你,我没有建议聚合框架,因为 2.2 rc0 还没有真正准备好进行全面部署,只是要记住一些事情,我会等到完整发布2.2 之前推荐部署聚合框架。
  • @Sammaye 是的,谢谢,我知道了,还不会投入生产,我需要它用于内部统计,并希望尽可能避免将数据移动到 SQL(并消除我的好奇心)
  • 为什么 Mongo 不接受 :this.plugins.X-Powered-By.string ?我将如何摆脱这种情况?
  • 我想知道这个答案对于分片环境是否可靠。据我了解,分片将各自进行自己的聚合,然后返回结果,然后将结果进行聚合。因此,在这种情况下,由于在第二个 $group 语句中丢失了不同的值,然后被传递回 mongos,我们难道没有机会存在重复项吗?
【解决方案2】:
db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}});

直截了当:

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

【讨论】:

  • 对,这样更好。但这不是威廉已经提供的相同答案吗?
  • 类似,但我喜欢它在一条线上的事实。我得到了一个错误:“无法读取未定义的属性'0'”删除最后一行,它工作得很好。
  • 如果我们谈论真正巨大的数据库,不要忘记 { allowDiskUse: true } 所以,db.myCollection.aggregate( [ {$group ..}, {$group:}], { allowDiskUse: true }).result[0].count;
【解决方案3】:

以下解决方案对我有用

db.test.distinct('user'); [“亚历克斯”、“英格兰”、“法国”、“澳大利亚”]

db.countries.distinct('country').length 4

【讨论】:

  • 当然它适用于小型套装。问题是关于具有数百万条记录的数据集,您的解决方案可能会抛出以下错误:“MongoError: distinct too big, 16mb cap”
猜你喜欢
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-14
  • 2015-01-28
  • 1970-01-01
  • 2014-01-19
相关资源
最近更新 更多