当我执行以下代码时(一个更大的例子,归结为要点)
var mongoose = require("mongoose");
var LocationSchema = new mongoose.Schema({
userName: String,
loc: {
'type': { type: String, enum: "Point", default: "Point" },
coordinates: { type: [Number] }
}
})
LocationSchema.index({ category: 1, loc: "2dsphere" });
var Location = mongoose.model("location", LocationSchema);
var mongoDB = 'mongodb://user1:test@ds042417.mlab.com:42417/locationdemo';
mongoose.Promise = global.Promise;
mongoose.connect(mongoDB, { useMongoClient: true });
var testUser = Location({
userName: "Tester",
loc: { coordinates: [12.44, 55.69] }
});
testUser.save(function (err) {
if (err) {
return console.log("UPPPPs: " + err);
}
console.log("User Saved, Try to find him:");
let query = Location.find({
loc:
{
$near:
{
$geometry:
{
type: "Point",
coordinates: [12.50, 55.71]
},
$maxDistance: 600000
}
}
})
query.exec(function (err, docs) {
if (err) {
return console.log("Err: " + err);
}
console.log("Found: " + JSON.stringify(docs))
})
});
我收到此错误:
错误:MongoError:错误处理查询:ns = locationdemo.locationsTree:GEONEAR字段= loc maxdist = 600000 isNearSphere = 0 排序:{} Proj:{} planner返回错误:无法找到$ geoNear查询的索引
答案 0 :(得分:2)
您违反了一般规则如何使用索引。虽然no restriction确实“2dsphere”索引是复合索引中的“第一”属性,但是“查询”实际上解决了第一个属性非常重要>为了选择索引。
复合索引手册中的Prefixes对此进行了介绍。摘录:
{ "item": 1, "location": 1, "stock": 1 }
索引具有以下索引前缀:
- {item:1}
- {item:1,location:1}
对于复合索引,MongoDB可以使用索引来支持对索引前缀的查询。因此,MongoDB可以在以下字段中使用索引进行查询:
- 项目字段
- 项目字段和位置字段
- 项目字段,位置字段和库存字段。
但是,MongoDB无法使用索引来支持包含以下字段的查询,因为没有item字段,所列出的字段都不对应于前缀索引:
- 位置字段,
- 库存字段,或
- 位置和库存字段。
由于您的查询引用"loc"
首先且不包含"category"
,因此索引不会被选中,MongoDB会返回错误。
因此,为了使用您定义的索引,您还需要实际查询"category"
。修改您的商家信息:
var mongoose = require("mongoose");
mongoose.set('debug',true);
var LocationSchema = new mongoose.Schema({
userName: String,
category: Number,
loc: {
'type': { type: String, enum: "Point", default: "Point" },
coordinates: { type: [Number] }
}
})
//LocationSchema.index({ loc: "2dsphere", category: 1 },{ "background": false });
LocationSchema.index({ category: 1, loc: "2dsphere" });
var Location = mongoose.model("location", LocationSchema);
var mongoDB = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.connect(mongoDB, { useMongoClient: true });
var testUser = Location({
userName: "Tester",
category: 1,
loc: { coordinates: [12.44, 55.69] }
});
testUser.save(function (err) {
if (err) {
return console.log("UPPPPs: " + err);
}
console.log("User Saved, Try to find him:");
let query = Location.find({
category: 1,
loc:
{
$near:
{
$geometry:
{
type: "Point",
coordinates: [12.50, 55.71]
},
$maxDistance: 600000
}
}
})
query.exec(function (err, docs) {
if (err) {
return console.log("Err: " + err);
}
console.log("Found: " + JSON.stringify(docs))
})
});
只要我们包含"category"
,一切都很好:
用户保存,尝试找到他:
Mongoose:locations.find({loc:{'$ near':{'$ geometry':{type:'Point',coordinates:[12.5,55.71]},'$ maxDistance':600000}},category:1} ,{fields:{}})
找到:[{“_ id”:“59f8f87554900a4e555d4e22”,“userName”:“Tester”,“category”:1,“__ v”:0,“loc”:{“coordinates”:[12.44,55.69],“type” : “点”}},{ “_ ID”: “59f8fabf50fcf54fc3dd01f6”, “username” 的: “测试”, “类别”:1, “__ v”:0, “LOC”:{ “坐标”:[12.44,55.69] “类型”: “点”}}]
备用案例是“前缀”索引与位置。确保首先删除先前的索引或集合:
LocationSchema.index({ loc: "2dsphere", category: 1 },{ "background": false });
除了你可能应该养成设置"background": true
的习惯,否则你会在单元测试中开始遇到竞争条件,在单元测试中,在单元测试代码尝试使用索引之前尚未完成索引。
答案 1 :(得分:0)
我对此问题的第一个解决方案是通过mLab网络界面创建索引,该界面就像魅力一样。
我尝试过Neil建议的解决方案,但仍然失败了。然而,Neil给出的与索引相关的详细说明确实指出了解决问题的方法。 这是一个时间问题(你不总是看到你在本地运行数据库)与我的测试代码执行以下操作相关: 创建索引,创建一个Location文档(第一次也会创建集合),然后在save提供的回调中,我试图找到用户。似乎这里尚未创建索引,这就是错误所在。
如果我将find方法延迟一秒,使用setTimeout就能正常工作。
但是,感谢Neil提供有关使用索引的正确方法的有价值信息(背景): - )