繁体   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