Mongo地图减少-半径内用户最多的场地



我的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不关心latlon字段名,并将忽略它们

但您也应该避免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。

相关内容

  • 没有找到相关文章

最新更新