Grupa MongoDB według funkcjonalności
W MySQL
select a,b,count(1) as cnt from list group by a, b having cnt > 2;
Muszę wykonać grupę za pomocą funkcji having condition w mongodb. Ale dostaję następujący błąd. Podziel się swoimi uwagami.
In MongoDB
> res = db.list.group({key:{a:true,b:true},
... reduce: function(obj,prev) {prev.count++;},
... initial: {count:0}}).limit(10);
Sat Jan 7 16:36:30 uncaught exception: group command failed: {
"errmsg" : "exception: group() can't handle more than 20000 unique keys",
"code" : 10043,
"ok" : 0
Gdy zostanie on wykonany, musimy uruchomić następujący plik Na next.
for (i in res) {if (res[i].count>2) printjson(res[i])};
Pozdrawiam, Kumaran
3 answers
MongoDB grupa przez jest bardzo ograniczona w większości przypadków, na przykład
- the result set must be lesser than 10000 keys.
- it will not work in sharded environments
Więc lepiej jest użyć Map reduce. więc zapytanie będzie takie
map = function() { emit({a:true,b:true}, {count:1}); }
reduce = function(k, values) {
var result = {count: 0};
values.forEach(function(value) {
result.count += value.count;
});
return result;
}
A następnie
db.list.mapReduce(map,reduce,{out: { inline : 1}})
To nieprzetestowana wersja. daj mi znać, jeśli to działa
EDIT:
Wcześniejsza funkcja mapy była wadliwa. Dlatego nie otrzymujesz wyników. powinno mieć been
map = function () {
emit({a:this.a, b:this.b}, {count:1});
}
Dane testowe:
> db.multi_group.insert({a:1,b:2})
> db.multi_group.insert({a:2,b:2})
> db.multi_group.insert({a:3,b:2})
> db.multi_group.insert({a:1,b:2})
> db.multi_group.insert({a:3,b:2})
> db.multi_group.insert({a:7,b:2})
> db.multi_group.mapReduce(map,reduce,{out: { inline : 1}})
{
"results" : [
{
"_id" : {
"a" : 1,
"b" : 2
},
"value" : {
"count" : 2
}
},
{
"_id" : {
"a" : 2,
"b" : 2
},
"value" : {
"count" : 1
}
},
{
"_id" : {
"a" : 3,
"b" : 2
},
"value" : {
"count" : 2
}
},
{
"_id" : {
"a" : 7,
"b" : 2
},
"value" : {
"count" : 1
}
}
],
"timeMillis" : 1,
"counts" : {
"input" : 6,
"emit" : 6,
"reduce" : 2,
"output" : 4
},
"ok" : 1,
}
EDIT2:
Kompletne rozwiązanie łącznie z zastosowaniem o liczbie > = 2
map = function () {
emit({a:this.a, b:this.b}, {count:1,_id:this._id});
}
reduce = function(k, values) {
var result = {count: 0,_id:[]};
values.forEach(function(value) {
result.count += value.count;
result._id.push(value._id);
});
return result;
}
>db.multi_group.mapReduce(map,reduce,{out: { replace : "multi_result"}})
> db.multi_result.find({'value.count' : {$gte : 2}})
{ "_id" : { "a" : 1, "b" : 2 }, "value" : { "_id" : [ ObjectId("4f0adf2884025491024f994c"), ObjectId("4f0adf3284025491024f994f") ], "count" : 2 } }
{ "_id" : { "a" : 3, "b" : 2 }, "value" : { "_id" : [ ObjectId("4f0adf3084025491024f994e"), ObjectId("4f0adf3584025491024f9950") ], "count" : 2 } }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-09 13:14:05
Powinieneś zamiast tego użyć MapReduce . Grupa ma swoje ograniczenia.
W przyszłości będziesz mógł korzystać z frameworka agregacji . Ale na razie użyj map/reduce.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-07 11:33:06
Zależy od liczby grup, możesz znaleźć prostsze i szybsze rozwiązanie niż group lub MapReduce używając distinctive :
var res = [];
for( var cur_a = db.list.distinct('a'); cur_a.hasNext(); ) {
var a = cur_a.next();
for( var cur_b = db.list.distinct('b'); cur_b.hasNext(); ) {
var b = cur_b.next();
var cnt = db.list.count({'a':a,'b':b})
if (cnt > 2)
res.push({ 'a': a, 'b' : b 'cnt': cnt}
}
}
Będzie szybciej, jeśli masz indeksy na a i b
db.list.ensureIndex({'a':1,'b':1})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-21 07:10:46