2016-04-10 23 views
0

I am looking for list of nearest branch of branchId [Array], eg :[109,110,115]mongooseを使ってmongodbコレクションから最も近い場所のデータを見つける方法は?

// 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] 
      } 

Data should be sorted in near to far order.

+1

私は質問が表示されません。 –

+1

@AaronGillionはい。実際に問題を提示していない別の失敗。しかし、 "慎重に"見れば、 '{" _id ":{" $ in ":branchId}}'行が見えます。ここでの問題は、OPが実際に '$ or'クエリを望んでいるということです。なぜなら、' $と '条件としてプライマリキーに値のリストを与えるのは意味がないからです。だから彼らは "同様に"実際には$またはを意味していないことがあります。 "問題"は、このようなジオスペースインデックスで '$ or'を使うことはできませんし、他の" _id "が照会されたポイントに必ずしも近くないと考えることも意味がありません –

答えて

1

あなたが実際に代わり$and$or条件を求めているように見えます。

  • $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です。さまざまな操作の結果を取得して1つのコールバックで処理できるため、これは良いツールです。 1はcontrainsあなたが別々に各クエリを実行している。この場合、

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

、:全体的な結果をソートするのに重要な部分である、実際に「プロジェクト」の結果への「距離」、集約$geoNearもあります結果は"maxDistance"であり、もう1つは$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); 

} 
関連する問題