我正在寻找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]
}
数据应按近似顺序排序。
答案 0 :(得分:2)
看起来您确实要求$or
条件而不是$and
。考虑到你是否真的想要“两个”结果,这是有道理的:
作为$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
值的“机会”。但是,如果是这种情况,甚至可能,那么在实际执行_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);
}