簡體   English   中英

MongoDB,如何查詢靠近特定位置的子文檔?

[英]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")
        }
]}

每個文檔都有一堆屬性,例如firstNamephone等。它還具有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全部。

對於其他任何事情,您都需要通過將子文檔放置在它們自己的集合中來“拉平”文檔,這些子文檔集還包含您需要的“外部”文檔屬性,或者對這些信息進行附加查詢的“合並”邏輯。

還要注意,只有$geoNeargeoNear命令會將投影的“距離”值返回到文檔中。 前者使您可以控制字段名稱,而后者則是任意的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM