看起来您实际上是在请求$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);
}