我正在使用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")
}
答案 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起作用,主要用于演示数据的“格式”问题。常规0
和other geospatial general queries are supported。但数据必须采用正确的支持格式。