如何在MongoDB中进行分页和分组?

时间:2015-04-09 15:45:09

标签: mongodb

我的对象具有以下结构:

{id: 1234,  ownerId: 1,  typeId: 3456, date:...}
{id: 1235,  ownerId: 1,  typeId: 3456, date:...}
{id: 1236,  ownerId: 1,  typeId: 12, date:...}

我想查询数据库,以便它返回属于给定ownerId的所有项目,但只返回给定typeId的第一项。 IE typeId字段在结果中是唯一的。我还希望能够使用skiplimit

在SQL中,查询类似于:

SELECT * FROM table WHERE ownerId=1 SORT BY date GROUP BY typeId LIMIT 10 OFFSET 300

我目前有以下查询(使用pymongo)但是我在使用$sort$limit$skip时出错:

 search_dict['ownerId'] = 1
 search_dict['$sort'] = {'date': -1}
 search_dict['$limit'] = 10
 search_dict['$skip'] = 200

 collectionName.group(['typeId'], search_dict, {'list': []}, 'function(obj, prev) {prev.list.push(obj)}')

-

我也尝试过汇总路线,但据我所知,分组会触及集合中的所有项目,将它们分组,然后限制并跳过。这将是计算上太昂贵和缓慢的。我需要一个迭代分组算法。

search_dict = {'ownerId':1}
collectionName.aggregate([
            {
                '$match': search_dict
            },
            {
                '$sort': {'date': -1}
            },
            {
                '$group': {'_id': "$typeId"}
            },
            {
                '$skip': skip
            },
            {
                '$limit': 10
            }
        ])

2 个答案:

答案 0 :(得分:0)

您的汇总看起来是正确的。您需要使用$group$first阶段的输出中包含所需的字段。

  

分组将触摸集合中的所有项目,对它们进行分组,然后限制并跳过。这将是计算上太昂贵和缓慢的。

它不会触及集合中的所有项目。如果匹配+排序已编入索引({ "ownerId" : 1, "date" : -1 }),则索引将用于匹配+排序,并且该组将仅处理作为匹配结果的文档。

除了未编制索引的情况外,约束几乎不是cpu。它通常是磁盘I / O.

  

我需要一个迭代分组算法。

“迭代分组”究竟是什么意思?分组是迭代的,因为它迭代前一阶段的结果并检查每个文档属于哪个组!

答案 1 :(得分:-2)

我不确定你是如何理解这个操作应该是计算上昂贵的。对于大多数SQL数据库来说,情况并非如此,而且肯定不适用于MongoDB。您只需要在排序标准上创建一个索引。

以下是如何证明:

打开一个mongo shell并执行此操作。

var bulk = db.speed.initializeOrderedBulkOp()
for ( var i = 1; i <= 100000; i++ ){
  bulk.insert({field1:i,field2:i*i,date:new ISODate()});
  if((i%100) == 0){print(i)}
}

bulk.execute();

批量执行可能需要几秒钟。接下来,我们创建一个辅助函数:

Array.prototype.avg = function() {
  var av = 0;
  var cnt = 0;
  var len = this.length;
  for (var i = 0; i < len; i++) {
    var e = +this[i];
    if(!e && this[i] !== 0 && this[i] !== '0') e--;
    if (this[i] == e) {av += e; cnt++;}
  }
  return av/cnt;
}

剧团准备就绪,舞台布景:

var times = new Array();
for( var i = 0; i < 10000; i++){
  var start = new Date();
  db.speed.find().sort({date:-1}).skip(Math.random()*100000).limit(10); 
  times.push(new Date() - start);
}
print(times.avg() + " msecs");

输出位于 msecs 。这是用于比较的5次运行的输出:

  1. 0.1697 msecs
  2. 0.1441 msecs
  3. 0.1397 msecs
  4. 0.1682 msecs
  5. 0.1843 msecs
  6. 测试服务器在docker镜像内运行,而docker镜像又运行在我的2,13 GHz Intel Core 2 Duo上的VM(boot2docker)中,内存为4GB,运行OSX 10.10.2,很多Safari窗口,iTunes,另外还有Mail,Spotify和Eclipse。不完全是一个生产系统。而且该集合甚至没有日期字段的索引。使用该索引,5次运行的平均值如下所示:

    1. 0.1399 msecs
    2. 0.1431 msecs
    3. 0.1339 msecs
    4. 0.1441 msecs
    5. 0.1767毫秒
    6. qed,hth。