【问题标题】:How to find data of nearest location from mongodb collection using mongoose?如何使用 mongoose 从 mongodb 集合中查找最近位置的数据?
【发布时间】:2016-07-31 19:11:31
【问题描述】:

我正在寻找 branchId [Array] 的最近分支列表, 例如:[109,110,115]

// My Schema
var BranchSchema = new Schema({
branchId : { type : Schema.Types.ObjectId },
branchName : { type : String },
branchAddress : {
    Address:{ type : String, required : false},
    City :{ type : String, required : false },
    Country : { type : String , required : false}
},
loc: {
    type: [Number],  // [<longitude>, <latitude>]
    index: '2d'      // create the geospatial index
}

});

    clubList.find({
            $and:[{ 
                    loc: { $near: coords, $maxDistance: maxDistance }
                },
                {_id :{ $in: branchId}} //branchId :  [108,109,110]
            ]},function(err, branchData){
            if (err) 
            {
               console.log(err);
            }
           else
           {
               console.log(branchData); 
            // Expecting sort near by branch of array [108,109,110]
           }

数据应按从近到远的顺序排序。

【问题讨论】:

  • 我没有看到问题。
  • @AaronGillion 是的。另一个未能实际提出问题。但是如果你“仔细看”你会看到{ "_id": { "$in": branchId } } 行。这里的问题是 OP 实际上想要一个 $or 查询,因为将值的“列表”作为 $and 条件提供给主键是没有意义的。所以他们错过了“以及”实际上意味着$or。 “问题”是您不能将$or 与这样的地理空间索引一起使用,考虑到其他“_id”因此不一定“靠近”查询点

标签: node.js mongodb mongoose mean-stack node-mongodb-native


【解决方案1】:

看起来您实际上是在请求$or 条件而不是$and。考虑到您是否真的想要“两者”的结果,这是有道理的:

  • $near 查询结果
  • “以及”与$in 条件相匹配的文档,该条件赋予_id 值。

作为$and 条件,查询可以写成:

clublist.find({
    "loc": { "$near": coords, "$maxDistance": maxDistance },
    "_id": { "$in": branchId }    // branchId is the array [108,109,110]
},function(err,branchData) {
  /* Data needs to be "both" near as well has having the supplied 
     _id values. So at maximum only "three" results, provided all
     "three" are within the maxDistance
   */
})

因此,考虑到这一点,您的意思似乎很可能是$or,但还有另一个问题。

您不能将$or 与需要地理空间索引的$near 之类的查询操作一起使用。所以这样的操作会报错:

clublist.find({
    "$or": [
        { "loc": { "$near": coords, "$maxDistance": maxDistance } },
        { "_id": { "$in": branchId } }    // branchId is the array [108,109,110]
    ]
},function(err,branchData) {
   // err:  "geoNear must be top-level expr"
})

为了做到这一点,您实际上希望运行单独的查询,然后合并结果。由于这些实际上是单独的结果,因此您可能还希望包含“距离”以便对组合进行“排序”。

async.parallel 是一个很好的工具,因为它允许您从不同的操作中获取结果并在一个回调中工作。还有聚合$geoNear 实际上将“距离”“投影”到结果中,这是对整体结果进行排序的重要部分:

async.parallel(
  [
    function(callback) {
      clublist.aggregate(
        [
          { "$geoNear": {
            "near": coords,
            "distanceField": "distance",
            "maxDistance": maxDistance
          }},
          { "$limit": 50 }
        ],
        callback
      )
    },
    function(callback) {
      clublist.aggregate(
        [
          { "$geoNear": {
            "near": coords,
            "distanceField": "distance",
            "query": { "_id": { "$in": branchId } }
          }}
        ],
        callback
      )
    }
  ],
  function(err,results) {
    if (err) throw err;
    var branchData = [];
    branchData = results.forEach(function(el) { 
       branchData = branchData.concat(el);
    });

    // Sort and return the 50 nearest
    branchData = branchData.sort(function(a,b) {
      return a.distance > b.distance
    }).slice(0,50)
  }
)

在这种情况下,您将分别运行每个查询,其中一个由"maxDistance" 约束结果,另一个由$in 的参数约束。由于“组合”结果将大于设置的“限制”,您需要按距离对该结果进行排序,然后返回组合的总限制。

这就是您想要考虑 _id 选择的地方的方式,但结果实际上可能会返回一个“距离”,否则该距离将不会包含在 $near 中。

但是,如果您的意图始终是将 _id 选择“放在”结果的顶部,那么您可以使用常规查询并为这些结果“注入”0 的距离:

async.parallel(
  [
    function(callback) {
      clublist.aggregate(
        [
          { "$geoNear": {
            "near": coords,
            "distanceField": "distance",
            "maxDistance": maxDistance
          }},
          { "$limit": 50 }
        ],
        callback
      )
    },
    function(callback) {
      clublist.find({ "_id": { "$in": branchId }}).toArray(function(err,branchData) {
        branchData = branchData.map(function(doc) {
          doc.distance = 0;
        });
        callback(err,branchData);
      })
    }
  ],
  function(err,results) {
    if (err) throw err;
    var branchData = [];
    branchData = results.forEach(function(el) { 
       branchData = branchData.concat(el);
    });

    // Sort and return the 50 nearest
    branchData = branchData.sort(function(a,b) {
      return a.distance > b.distance
    }).slice(0,50)
  }
)

然后相同的排序将这些值保持在顶部,并返回来自其他查询操作的任何结果。

当然有“机会”在“两个”查询中返回指定的_id 值。但如果是这种情况或什至有可能,那么您可以在实际执行 .slice() 操作之前或通常在返回最终结果之前简单地匹配任何“重复”_id 值的数组内容。这是一个简单的过程,使用具有唯一键的对象,然后返回数组。

类似:

function(err,results) {
    if (err) throw err;
    var branchData = [];
    branchData = results.forEach(function(el) { 
       branchData = branchData.concat(el);
    });

    var uniqueBranch = {};

    // Just keep unique _id for smallest distance value
    for ( var idx in branchData ) {
      if ( ( uniqueBranch.hasOwnProperty(branchData[idx]._id) ) 
           && ( branchData[idxx].distance > uniqueBranch[branchData[idx]._id].distance ) )
         continue;
      uniqueBranch[branchData[idx]._id] = branchData[idx];
    }

    // Go back to array, sort and slice
    branchData = Object.keys(uniqueBranch).map(function(k) {
        return uniqueBranch[k];
    }).sort(function(a,b) {
        return a.distance > b.distance;
    }).slice(0,50);

}

【讨论】:

    猜你喜欢
    • 2019-04-11
    • 1970-01-01
    • 2019-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-04
    相关资源
    最近更新 更多