【问题标题】:Query complains about missing 2dsphere-index, but it's there查询抱怨缺少 2dsphere-index,但它就在那里
【发布时间】: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您提出的问题

标签: node.js mongodb mongoose mongodb-query geospatial


【解决方案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" 或实际上包含其他前缀的索引才是真正解决的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 2015-10-31
    • 2018-06-25
    • 1970-01-01
    相关资源
    最近更新 更多