我的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;
};
并减少:
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}};
我的geo_query:
var res = db.myColelction.mapReduce(map1, reduce1, { out : { inline : 1 }, query : geo_query });
最后是mapReduce查询:
...
{
"_id" : 609096,
"value" : {
"users" : {
"487586" : 1
},
"count" : 1
}
},
{
"_id" : 622448,
"value" : {
"users" : {
"313755" : 1,
"443180" : 1
},
"total" : 4
}
},
...
返回的结果与reduce函数匹配,但没有达到finalize1函数:
$near
此时,我认为我有一个很好的结果集,但{{1}}功能只能扫描附近的100个场地,我想扫描所有场地(所有符合此半径的文件(25米) ),并查看所有场地 - 将它们分组,并计算那个时间段内的独特用户。我一直在搜索,查看文档,我不确定解决方案。任何接受者?
对我来说,最终的结果是排序并通过“total”属性限制结果。理想情况下,我希望按总计desc和限制15进行排序。
答案 0 :(得分:4)
我会做以下事情。首先,你的坐标是错误的。 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
指数好很多{1}}一个(我猜,它只会有值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不是计数的关键,而是子文档中的额外字段。通常,您无法使用聚合框架将值更改为键或键值。
答案 1 :(得分:0)
你说这个功能只能扫描100个场地。我对近处的理解是它将扫描整个集合并且只返回最接近的100个。
从$near的文档粘贴的副本:
注意:您可以使用cursor.limit()进一步限制结果数。 未定义批量大小(即batchSize())以及使用$ near的查询。有关更多信息,请参阅SERVER-5236。