查询抱怨缺less2dsphere-index,但它在那里

当我执行下面的代码(一个更大的例子,归结为要领)

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

我得到这个错误:

Err:MongoError:处理查询错误:ns = locationdemo.locationsTree:GEONEAR field = loc maxdist = 600000 isNearSphere = 0sorting:{} Proj:{} planner返回错误:无法find$ geoNear查询的索引

但索引在那里(见第10行)以及下面mlab的截图。 我究竟做错了什么?: 在这里输入图像说明

一般来说,你打破了如何使用索引的规则。 虽然“2dsphere”索引没有任何限制是复合索引中“第一”属性的限制 ,但是非常重要的一点是,您的“查询”实际上要处理第一个属性 ,以便select索引。

这在复合索引手册的前缀中有介绍。 摘录如下:

{ "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"一切都很好:

用户保存,尝试find他:
Mongoose:locations.find({loc:{'$ geometry':{'$ geometry':{type:'Point',coordinates:[12.5,55.71]},'$ maxDistance':600000}},category:1} ,{fields:{}})
find:[{“_id”:“59f8f87554900a4e555d4e22”,“userName”:“Tester”,“category”:1,“__ v”:0,“loc”:{“coordinates”:[12.44,55.69],“type” : “点”}},{ “_ ID”: “59f8fabf50fcf54fc3dd01f6”, “username” 的: “testing”, “类别”:1, “__ v”:0, “LOC”:{ “坐标”:[12.44,55.69] “types”: “点”}}]

另一种情况是简单地将索引与该位置“前缀” 。 确保首先删除以前的索引或集合:

 LocationSchema.index({ loc: "2dsphere", category: 1 },{ "background": false }); 

以及你可能应该习惯于设置"background": true ,否则你开始跑unit testing的竞赛条件,在unit testing代码试图使用它之前索引还没有完成创build。

我第一个解决这个问题的方法是通过mLab网页界面来创build索引,这个界面的function就像一个魅力一样。

我已经尝试了Neil提出的解决scheme,但仍然失败。 尼尔给出的有关索引的详细指令的确给我指出了解决这个问题的方法。 这是一个计时问题(你不会总是看到,如果你在本地运行数据库)有关,我的testing代码做了以下事情:创build索引,创build一个位置文档(第一次也将创build集合),然后在保存提供的callback,我试图find用户。 看来这个指数还没有在这里创build,这是错误的原因。

如果我延迟查找方法一秒,使用setTimeout它工作正常。

但是,还是要感谢Neil有关正确使用索引(背景)的宝贵信息:-)