查询抱怨缺少 2dsphere-index,但它就在那里

Posted

技术标签:

【中文标题】查询抱怨缺少 2dsphere-index,但它就在那里【英文标题】:Query complains about missing 2dsphere-index, but it's there 【发布时间】:2018-04-12 17:16:18 【问题描述】:

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

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=0 种类: 项目: 规划器返回错误:无法找到 $geoNear 查询的索引

但索引在那里(见第 10 行)和下面 mlab 的屏幕截图。我究竟做错了什么?:

【问题讨论】:

您认为提供的答案中是否有某些内容无法解决您的问题?如果是这样,请对答案发表评论,以澄清究竟需要解决哪些尚未解决的问题。如果它确实回答了您提出的问题,请注意Accept your Answers您提出的问题 【参考方案1】:

您违反了一般如何使用索引的规则。虽然确实有no restriction 认为“2dsphere”索引是复合索引中的“第一个”属性,但是非常重要的是您的“查询”实际上解决了 first 属性选择索引的顺序。

这在复合索引手册中的Prefixes 中有介绍。摘录:

"item": 1, "location": 1, "stock": 1

索引具有以下索引前缀:

项目:1 项目:1,位置:1

对于复合索引,MongoDB 可以使用索引来支持对索引前缀的查​​询。因此,MongoDB 可以使用索引来查询以下字段:

项目字段, 项目字段和位置字段, item 字段和 location 字段和 stock 字段。

但是,MongoDB 不能使用索引来支持包含以下字段的查询,因为没有 item 字段,列出的字段都不对应前缀索引:

位置字段, 库存字段,或 位置和库存字段。

因为您的查询引用了"loc" first 并且不包括"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', 坐标: [ 12.5, 55.71 ] , '$maxDistance': 600000 , 类别: 1 , 字段: ) 找到:["_id":"59f8f87554900a4e555d4e22","userName":"Tester","category":1,"__v":0,"loc":"coordinates":[12.44,55.69],"type" :"Point","_id":"59f8fabf50fcf54fc3dd01f6","userName":"Tester","category":1,"__v":0,"loc":"coordinates":[12.44,55.69] ,"type":"Point"]

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

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

您可能应该养成设置"background": true 的习惯,否则在单元测试代码尝试使用索引之前,您开始在单元测试中遇到竞争条件。

【讨论】:

【参考方案2】:

我对这个问题的第一个解决方案是通过 mLab 网络界面创建索引,这就像一个魅力。

我尝试了 Neil 建议的解决方案,但仍然失败。然而,Neil 给出的与索引相关的详细说明确实为我指明了问题的解决方案。 这是与我的测试代码执行以下操作相关的时间问题(如果您在本地运行数据库,您不会总是看到): 创建了索引,创建了一个 Location 文档(第一次也会创建集合),然后在 save 提供的回调中,我试图找到用户。似乎此处尚未创建索引,这就是导致错误的原因。

如果我将 find 方法延迟一秒,使用 setTimeout 可以正常工作。

不过,感谢 Neil 提供了有关正确使用索引的宝贵信息(背景):-)

【讨论】:

抱歉,您完全错了。正如我所提到的,您确实应该注意创建索引的时间,但正如我通过可重现的清单所证明的那样,index order 这就是您收到错误的原因。如果该索引的 "prefix" 未包含在查询中,则 MongoDB 无法选择正确的索引。这就是给出的答案所说的,因为那是您的代码中的问题。您没有包含"category",它是查询中索引的前缀。使用前缀为 "location" 或实际上包含其他前缀的索引才是真正解决的问题。

以上是关于查询抱怨缺少 2dsphere-index,但它就在那里的主要内容,如果未能解决你的问题,请参考以下文章

“缺少 mysqli 扩展”,但它位于扩展目录中

Django测试抱怨缺少表问题,怎么解决

Perl 不会抱怨缺少分号

Opsworks 食谱抱怨缺少方法

我的测试中缺少伊斯坦布尔抱怨缺少分支覆盖的确切缺点?

facebook oauth 抱怨缺少“登录密码”