簡體   English   中英

如何使用mongoose從mongodb集合中查找最近位置的數據?

[英]How to find data of nearest location from mongodb collection using mongoose?

我正在尋找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]
           }

數據應按近至遠順序排序。

看來您實際上是在要求$or條件而不是$and 考慮到您是否真正想要“兩者”的結果,這將是有意義的:

  • $near查詢結果
  • “以及”與賦予_id值的$in條件匹配的文檔。

作為$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 ,實際上將“距離”“ $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""maxDistance" ,而另一個"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值進行.slice() 這是一個簡單的過程,使用具有唯一鍵的對象然后返回數組。

就像是:

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);

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM