[英]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.