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