MongoDB select count (different x) в индексированном столбце - подсчет уникальных результатов для больших наборов данных

Я просмотрел несколько статей и примеров и еще не нашел эффективный способ сделать этот SQL-запрос в MongoDB (где есть миллионы документов rows)

Первая попытка

(например, из этого почти дублированного вопроса - Монгольский эквивалент SQL 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 для разных учетных записей? или существует эффективный способ?

Обновление:

Этот комментарий к официальным документам MongoDB не обнадеживает, верно ли это?

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

Update2:

Кажется, новая структура агрегирования отвечает на вышеупомянутый комментарий... (MongoDB 2.1/2.2 и выше, предварительный просмотр доступен, а не для производства)

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

Ответ 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, вы можете. Это также двухфазный процесс: на первом этапе мы создаем новую коллекцию со списком каждого отдельного значения для ключа. Во втором мы делаем счет() в новой коллекции.

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

Обратите внимание, что вы не можете вернуть результат карты/уменьшить встроенный, поскольку это потенциально превысит ограничение размера документа размером 16 МБ. Вы можете сохранить расчет в коллекции, а затем подсчитать() размер коллекции или получить количество результатов от возвращаемого значения mapReduce().

Ответ 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;

Ответ 3

Следующее решение сработало для меня

db.test.distinct( 'пользователь'); [ "alex", "England", "France", "Australia" ]

db.countries.distinct( 'страна'). Длина 4