创建地理索引和查询附近

时间:2017-06-18 01:06:35

标签: mongodb geospatial

我正在使用azure cosmos db如何在C#中创建索引和查询最接近的前10位用户:

render()

查询:

{
  "_id" : "146138792054898475572",
  "email" : "abc@gmail.com",
  "firstName" : "abc",
  "lastName" : "abc",
  "loc" : {
    "lat" : 31.5200788,
    "lng" : 74.3236112
  },
  "gender" : "Male",
  "deviceId" : "YWg8crAjZLCrV",
  "createdDate" : ISODate("2017-06-11T11:35:41.601Z"),
  "updatedDate" : ISODate("2017-06-17T17:33:10.743Z")
}

2 个答案:

答案 0 :(得分:2)

你的坐标是向后的。每个MongoDB docs

  

如果您使用经度和纬度,请按以下顺序指定坐标:   经度,纬度。

您需要反转坐标:

db.User.find(
   {
      "loc":
        { $near :
           {
             $geometry: { type: "Point",  coordinates: [ 74.3236112, 31.5200788 ] },
             $minDistance: 1000,
             $maxDistance: 5000
           }
        }
   }
)

此外,在您的loc属性中,排序是经度,纬度(在您展示的文档中,您的顺序相反)。

答案 1 :(得分:2)

这里有一些问题主要与您存储文档的方式有关。 MongoDB支持以三种格式之一存储坐标点:

  • 传统坐标对作为数组数据以经度和纬度顺序列出的位置

    [ 74.3236112, 31.5200788 ]
    
  • 传统坐标对作为对象数据可以通过命名键进行组织,但必须才能被排序并明确命名为“lon “”lat“分别”按顺序“:

    { "lon": 74.3236112, "lat": 31.5200788 }
    
  • 作为GeoJSON格式,其中存储的数据可以是任何有效的GeoJSON对象格式。对于“Point”类型,这是:

    {
      "type": "Point",
      "coordinates": [ 74.3236112, 31.5200788 ]
    }
    

为了演示我的样本文档有四种不同格式的集合。我们将使用.createIndex({ "loc": "2dsphere" })在该集合上创建索引,然后使用聚合管道$geoNear来查询并返回查询点的实际距离:

db.geotest.aggregate([
  { "$geoNear": {
    "near": {
      "type": "Point",
      "coordinates": [ 74.3236112, 31.5200788 ]
    },
    "spherical": true,
    "distanceField": "distance"
  }}
])

显示查询的四种不同格式和计算距离。请注意,只有两种“有效”格式会从查询位置返回0的正确距离:

{
        "_id" : ObjectId("5945f800b4051c7e52c90d1c"),
        "email" : "abc@gmail.com",
        "firstName" : "abc",
        "lastName" : "abc",
        "loc" : {
                "type" : "Point",
                "coordinates" : [
                        74.3236112,
                        31.5200788
                ]
        },
        "gender" : "Male",
        "deviceId" : "YWg8crAjZLCrV",
        "createdDate" : ISODate("2017-06-11T11:35:41.601Z"),
        "updatedDate" : ISODate("2017-06-17T17:33:10.743Z"),
        "distance" : 0
}
{
        "_id" : ObjectId("5945f8f6b4051c7e52c90d1e"),
        "email" : "abc@gmail.com",
        "firstName" : "abc",
        "lastName" : "abc",
        "loc" : {
                "lon" : 74.3236112,
                "lat" : 31.5200788
        },
        "gender" : "Male",
        "deviceId" : "YWg8crAjZLCrV",
        "createdDate" : ISODate("2017-06-11T11:35:41.601Z"),
        "updatedDate" : ISODate("2017-06-17T17:33:10.743Z"),
        "distance" : 0
}
{
        "_id" : "146138792054898475572",
        "email" : "abc@gmail.com",
        "firstName" : "abc",
        "lastName" : "abc",
        "loc" : {
                "lat" : 31.5200788,
                "lng" : 74.3236112
        },
        "gender" : "Male",
        "deviceId" : "YWg8crAjZLCrV",
        "createdDate" : ISODate("2017-06-11T11:35:41.601Z"),
        "updatedDate" : ISODate("2017-06-17T17:33:10.743Z"),
        "distance" : 5315650.25629941
}
{
        "_id" : ObjectId("5945f8b7b4051c7e52c90d1d"),
        "email" : "abc@gmail.com",
        "firstName" : "abc",
        "lastName" : "abc",
        "loc" : {
                "lat" : 31.5200788,
                "lon" : 74.3236112
        },
        "gender" : "Male",
        "deviceId" : "YWg8crAjZLCrV",
        "createdDate" : ISODate("2017-06-11T11:35:41.601Z"),
        "updatedDate" : ISODate("2017-06-17T17:33:10.743Z"),
        "distance" : 5315650.25629941
}

因此,为了正确查询,您需要索引的“有效”格式。我个人建议使用GeoJSON格式,因为它被广泛用作标准,并且还提供了存储任何有效GeoJSON对象的选项,而不仅仅是“点坐标”

您可以使用以下操作转换数据:

var ops = [];

db.User.find({ 
  "loc.lng": { "$exists": true }, 
  "loc.lat": { "$exists": true }
}).forEach(function(doc) {
  ops.push({
    "updateOne": {
      "filter": { "_id": doc._id },
      "update": {
        "$set": {
          "loc": {
            "type": "Point",
            "coordinates": [ doc.loc.lng, doc.loc.lat ]
          }
        }
      }
    }
  });
  if ( ops.length >= 1000 ) {
    db.User.bulkWrite(ops);
    ops = [];
  }
});

if ( ops.length > 0 ) {
  db.User.bulkWrite(ops);
  ops = [];
}

这会将"loc"数据重写为更正的格式,以便索引和查询能够正常工作。

实际上,在更新之前,您应该真正对集合执行.dropIndexes()以节省写入成本,然后在完成后重新创建索引。这不是必要的步骤,但建议这样做。

NB 此处$minDistace参数此处修正数据实际上仍然会排除结果,因为展示了与查询点(相同坐标)的实际距离为{{1 }}。因此,在测试时删除该约束,或者更好地使用$geoNear进行测试,如图所示。

同样注意提到了DocumentDB,它不支持聚合管道协议。这里的示例可以毫无问题地对MongoDB起作用,主要用于演示数据的“格式”问题。常规0other geospatial general queries are supported。但数据必须采用正确的支持格式。