我的MapReduce函数有问题-目标是获得某个纬度/经度内的顶级场馆列表,按vid
分组,按不同的user_id
排序。
以下是一个示例数据集:
{ "_id" : ObjectId("51f9234feb97ff0700000046"), "checkin_id" : 39286249, "created_at" : ISODate("2013-07-31T14:47:11Z"), "loc" : { "lat" : 42.3672, "lon" : -86.2681 }, "icv" : 1, "ipv" : 1, "vid" : 348442, "user_id" : 151556, "bid" : 9346, "pid" : 549 }
{ "_id" : ObjectId("51f9234b488fff0700000006"), "checkin_id" : 39286247, "created_at" : ISODate("2013-07-31T14:47:07Z"), "loc" : { "lat" : 55.6721, "lon" : 12.5576 }, "icv" : 1, "ipv" : 1, "vid" : 3124, "user_id" : 472486, "bid" : 7983, "pid" : 2813 }
...
这是我的地图功能:
map1 = function() {
var tempDoc = {};
tempDoc[this.user_id] = 1;
emit(this.vid, {
users: tempDoc,
count: 1
});
}
并减少:
reduce1 = function(key, values) {
var summary = {
users: {},
total: 0
};
values.forEach(function (doc) {
// increment total for every value
summary.total += doc.count;
// Object.extend() will only add keys from the right object that do not exist on the left object
Object.extend(summary.users, doc.user);
});
return summary;
};
我的地理查询(_Q):
var d = Date("2013-07-31T14:47:11Z");
var geo_query = {loc: {$near: [40.758318,-73.952985], $maxDistance: 25}, "icv":1, "created_at": {$gte: d}};
最后是mapReduce查询:
var res = db.myColelction.mapReduce(map1, reduce1, { out : { inline : 1 }, query : geo_query });
返回的结果与reduce函数匹配,但未达到finalize1函数:
...
{
"_id" : 609096,
"value" : {
"users" : {
"487586" : 1
},
"count" : 1
}
},
{
"_id" : 622448,
"value" : {
"users" : {
"313755" : 1,
"443180" : 1
},
"total" : 4
}
},
...
在这一点上,我认为我有一个很好的结果集,但$near
功能只扫描附近的100个场馆,我想扫描所有场馆(所有与该半径(25米)匹配的文档),并查看所有场馆-对它们进行分组,并计算该时间段内的唯一用户。我四处搜索,查看了文档,但我不确定是否有解决方案。有人接电话吗?
对我来说,最终的结果是通过"total"属性对结果进行排序和限制。理想情况下,我想按总desc和限制15进行排序。
我将执行以下操作。首先,你的坐标是错误的。MongoDB想要longitude, latitude
,最好是GeoJSON格式:
loc: { type: 'Point', coordinates: [-73.952985, 40.758318] },
MongoDB不关心lat
和lon
字段名,并将忽略它们。
但您也应该避免Map/Reduce,因为它既缓慢又复杂。相反,我们可以使用聚合框架来做类似的事情:
db.so.aggregate( [
// search for all the (well, million) venues within **250**km
{ $geoNear: {
near: { type: 'Point', coordinates: [-73.952985, 40.758318] },
spherical: true,
distanceField: 'd',
maxDistance: 250 * 1000,
limit: 1000000
} },
// find only the items where icv=1
{ $match: { icv: 1 } },
// group by venue and user
{ $group: {
_id: { vid: '$vid', user_id: '$user_id' },
count: { $sum: 1 } }
},
// then regroup by just venue:
{ $group: {
_id: '$_id.vid',
users: { $addToSet: { user_id: '$_id.user_id', count: '$count' } },
total: { $sum: '$count' }
} },
// now we sort by "total", desc:
{ $sort: { 'total': -1 } },
// and limit by 15:
{ $limit: 15 }
] );
我使用了$geoNear
作为第一阶段,$icv
上的匹配作为第二阶段,因为$geoNear
索引可能会比$icv
索引好得多(正如我所猜测的,它的值无论如何都只有0或1)。
请注意,在这个例子中,我使用了250公里(250*1000米),而不是25公里。
具有以下输入:
db.so.insert( { "_id" : ObjectId("51f9234feb97ff0700000046"), "loc" : { type: 'Point', coordinates: [ -73.2681, 40.3672 ] }, "vid" : 348442, "user_id" : 151556 } );
db.so.insert( { "_id" : ObjectId("51f9234b488fff0700000006"), "loc" : { type: 'Point', coordinates: [ -73.5576, 40.6721 ] }, "vid" : 3124, "user_id" : 472486 } );
db.so.insert( { "_id" : ObjectId("51f92345488fff0700000006"), "loc" : { type: 'Point', coordinates: [ -73.5576, 40.6721 ] }, "vid" : 3124, "user_id" : 47286 } );
db.so.insert( { "_id" : ObjectId("52f92345488fff0700000006"), "loc" : { type: 'Point', coordinates: [ -73.5576, 40.6721 ] }, "vid" : 3124, "user_id" : 47286 } );
你得到的结果:
{
"result" : [
{
"_id" : 3124,
"users" : [
{ "user_id" : 472486, "count" : 1 },
{ "user_id" : 47286, "count" : 2 }
],
"total" : 3
},
{
"_id" : 348442,
"users" : [
{ "user_id" : 151556, "count" : 1 }
],
"total" : 1
}
],
"ok" : 1
}
与您想要的输出只有一个区别,那就是user_id不是计数的键,而是子文档中的一个额外字段。通常,您不能使用聚合框架将值更改为键或将键更改为值。
您说该功能只扫描100个场地。我对near的理解是,它会扫描整个集合,只返回最近的100。
从$near:的文档粘贴的副本
注意:您可以使用cursor.limit()进一步限制结果的数量。没有定义与使用$near的查询一起指定批大小(即batchSize())。有关更多信息,请参阅SERVER-5236。