MongoDB/Mongoose - 通过geoNear和子文档进行聚合

7
我正在使用node-geoip模块,并执行聚合查询。我要查询的架构如下:
var mongoose = require('mongoose');
require('./location.js');

module.exports = mongoose.model('Region',{
    attr1: Number,
    attr2: String,
    attr3: String,
    locations:[mongoose.model('Location').schema]
});

并且

var mongoose = require('mongoose');

module.exports = mongoose.model('Location',{
    attr1: Number,
    latlong: { type: [Number], index: '2d' },
});

我需要在聚合查询中执行$geoNear操作,但我遇到了一些问题。首先,这是我的聚合方法:

var region = require('../models/region');

var geo = geoip.lookup(req.ip);

region.aggregate([
    {$unwind: "$locations"},
    {$project: {
        attr1 : 1,
        attr2 : 1,
        locations : 1,
        lower : {"$cond" : [{$lt: [ '$locations.attr1', '$attr1']}, 1, 0]}
    }},
     {
      $geoNear: {
         near: { type:"Point", '$locations.latlong': geo.ll },
         maxDistance: 40000,
         distanceField: "dist.calculated"
      }
     },
    { $sort: { 'locations.attr1': -1 } },
    {$match : {lower : 1}},
    { $limit: 1 }
], function(err,f){...});

我遇到的第一个问题是似乎必须将geoNear放在管道的第一阶段:exception: $geoNear is only allowed as the first pipeline stage. 那么我的问题是,我是否可以在不展开子文档的情况下执行geoNear搜索?如果可以,如何实现?
另一个错误信息是errmsg: \"exception: 'near' field must be point\"。这是什么意思,对我的代码有什么影响?我已经尝试使用near作为:
near: { type:"Point", '$locations.latlong': geo.ll },

你尝试过将“near”定义为一个点吗?例如:near: [经度, 纬度] - cdagli
数组的索引是多键的。$geoNear 在数组上应该可以正常工作 - 排序应该是每个文档中最接近的点。第二个错误表示您没有使用正确的语法 $geoNear。请查看$geoNear,特别是底部的示例。 - wdberkeley
@wdberkeley 感谢您的回复。当您说“排序应该是每个文档中最接近的点”,这是什么意思?我不太明白这对我的代码意味着什么?这是否意味着我可以将$geoNear移动到展开$locations之前的第一阶段? - jordan
@wdberkeley,另外,我的语法错在哪里?我看过那些例子并遵循它们的惯例。具体是哪里出了问题? - jordan
1个回答

4
首先声明一下:我不是Node/Mongoose专家,所以希望你能将通用格式翻译成Node/Mongoose格式。
针对这个错误:
 errmsg: "exception: 'near' field must be point"

对于“2d”索引,这不能是一个GeoJson点,而必须是一个“传统坐标对”。例如,
{
  "$geoNear": {
    "near": geo.ll,
    "maxDistance": 40000,
    "distanceField": "dist.calculated"
  }
}

如果要使用GeoJSON,您需要使用“2dsphere”索引。
通过这个更改,$geoNear查询将与查询中的点数组一起工作。一个示例在shell中:
> db.test.createIndex({ "locations": "2d" })
> db.test.insert({ "locations": [ [1, 2], [10, 20] ] });
> db.test.insert({ "locations": [ [100, 100], [180, 180] ] });
> db.test.aggregate([{
  "$geoNear": {
    "near": [10, 10],
    "maxDistance": 40000,
    "distanceField": "dist.calculated",
    num: 1
  }
}]);
{
  "result": [{
    "_id": ObjectId("552aaf7478dd9c25a3472a2a"),
    "locations": [
      [
        1,
        2
      ],
      [
        10,
        20
      ]
    ],
    "dist": {
      "calculated": 10
    }
  }],
  "ok": 1
}

请注意,每份文档只会得到一个距离(最近的点),这在语义上与展开然后确定到每个点的距离不同。我无法确定这对您的用例是否重要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接