[英]MongoDB, How to query subdocuments close to specific location?
我的MongoDb数据库的集合users
包含以下结构的文档:
{
firstName: "firstname",
"phone": "123456",
"places":[
{
"name" : "somename",
"address" : "Woollahra, New South Wales, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z"),
"id" : ObjectId("5517632982ae879883216fe2b2")
},
{
"name" : "somename",
"address" : "something else, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z"),
"id" : ObjectId("5517632982ae879883216fe2b2")
}
]}
每个文档都有一堆属性,例如firstName
, phone
等。它还具有places
属性,该属性是子文档的数组。
每个子文档都有loc
属性,该属性存储“ place”子文档描述的坐标。 我基本上需要按从传递给查询的特定位置的距离的顺序拉出place对象。
我不知道如何运行collection.find
$near
查询以根据其位置获取位置列表。 我想首先我需要建立2dsphere
对指数places.loc
和尝试:
db.users.createIndex({"places.loc":"2dsphere"})
但是我得到了"errmsg" : "exception: Can't extract geo keys
。
使用数据库中已有的结构,这甚至可能吗? 如果可以,我该怎么办? 我的文档样本如下,在此先感谢您的帮助。 顺便说一句,我正在使用带有本机mongoDB驱动程序的NodeJ。
编辑:
我试过了:
db.users.createIndex({"loc":"2dsphere"})
结果是:
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 3,
"note" : "all indexes already exist",
"ok" : 1
}
那给了我希望,但是当我尝试运行查询时:
db.users.find({
'places.loc': {
$near: {
$geometry: {
type: "Point",
coordinates: [-73.965355, 40.782865]
},
$maxDistance: 20000
}
}
})
我得到这个:
Error: error: {
"$err" : "Unable to execute query: error processing query: ns=marankings.users limit=0 skip=0\nTree: GEONEAR field=places.loc maxdist=20000 isNearSphere=0\nSort: {}\nProj: {}\n planner returned error: unable to find index for $geoNear query",
"code" : 17007
}
如前所述,最接近当前结构的是使用$geoNear
,它是一个聚合框架运算符。 这具有解决子文档中“匹配”所需的必要投影需求。
但是首先要对样本进行重做,而不会出现错误:
{
"firstName": "firstname",
"phone": "123456",
"places":[
{
"name" : "somename",
"address" : "Woollahra, New South Wales, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z"),
},
{
"name" : "somename",
"address" : "something else, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-36.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z"),
}
]
}
我将在名为“ places”的集合中创建该集合,然后将索引放在该集合上,如下所示:
db.places.ensureIndex({ "places.loc": "2dsphere" })
现在让我们尝试一个基本的.find()
操作:
db.places.find({
"places.loc": {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": [
151.23721839999996,
-33.8884085
]
}
}
}
})
这将匹配并返回您的“整个文档”,但不会告诉您有关匹配的数组元素或到查询点的距离的任何信息。
让我们现在使用$geoNear
来查看操作:
db.places.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [
151.23721839999996,
-33.8884085
]
},
"distanceField": "dist",
"includeLocs": "locs",
"spherical": true
}}
])
在这个阶段可以给我们带来以下结果:
{
"_id" : ObjectId("558299b781483914adf5e423"),
"firstName" : "firstname",
"phone" : "123456",
"places" : [
{
"name" : "somename",
"address" : "Woollahra, New South Wales, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z")
},
{
"name" : "somename",
"address" : "something else, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-36.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z")
}
],
"dist" : 0,
"locs" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
}
}
请注意其中的“ dist”和“ locs”额外字段。 这些分别是与匹配查询点的“距离”和与子文档匹配到该特定距离的“位置”数据。
该文档仍然相同,但是由于这是聚合框架,因此您可以进一步:
db.places.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [
151.23721839999996,
-33.8884085
]
},
"distanceField": "dist",
"includeLocs": "locs",
"spherical": true
}},
{ "$redact": {
"$cond": {
"if": { "$eq": [
{ "$ifNull": [ "$loc", "$$ROOT.locs" ] },
"$$ROOT.locs"
]},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
因此, $redact
用作一种将数组内容“过滤”为仅与找到位置匹配的“条目”的方法:
{
"_id" : ObjectId("558299b781483914adf5e423"),
"firstName" : "firstname",
"phone" : "123456",
"places" : [
{
"name" : "somename",
"address" : "Woollahra, New South Wales, Australia",
"loc" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
},
"url" : "ttttt2",
"registeredOn" : ISODate("2015-06-17T20:14:10.986Z")
}
],
"dist" : 0,
"locs" : {
"type" : "Point",
"coordinates" : [
151.23721839999996,
-33.8884085
]
}
}
当然,正如我已经说过的那样,每个文档的数组中只能有“一个”匹配项,因为这将返回$geoNear
全部。
对于其他任何事情,您都需要通过将子文档放置在它们自己的集合中来“拉平”文档,这些子文档集还包含您需要的“外部”文档属性,或者对这些信息进行附加查询的“合并”逻辑。
还要注意,只有$geoNear
和geoNear
命令会将投影的“距离”值返回到文档中。 前者使您可以控制字段名称,而后者则是任意的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.