如何通过Geowithin从mongodb获得匹配的子文档?

Pri*_*han 7 geospatial mongoose mongodb node.js mongodb-query

我在这里要做的是,我只想要那些在提供的纬度和经度范围内的子文档,但如果我的文档中只有一个子文档匹配而其他子文档不匹配,那么它应该只返回带有该特定文档的文档.但它也归还给我所有子文档也可以有人帮助我.我的文件是这样的

{
         "_id": "5ae04fd45f104a5980cf7e0e",
          "name": "Rehan",
         "email": "rehan@gmail.com",
         "status": true,
         "created_at": "2018-04-25T09:52:20.266Z",
         "parking_space": [
             {
                 "_id": "5ae05dce5f104a5980cf7e0f",
                 "parking_name": "my space 1",
                 "restriction": "no",
                 "hourly_rate": "3",
                 "location": {
                    "type": "Point",
                    "coordinates": [
                        86.84470799999997,
                        42.7052881
                    ]
                },
            },
            {
                "_id": "5ae06d4d5f104a5980cf7e52",
                "parking_name": "my space 2",
                "restriction": "no",
                "hourly_rate": "6",
                "location": {
                    "type": "Point",
                    "coordinates": [
                        76.7786787,
                        30.7352527
                    ]
                },
            }
        ],
    },
    {
        "_id": "5ae2f8148d51db4937b9df02",
        "name": "nellima",
        "email": "neel@gmail.com",
        "status": true,
        "created_at": "2018-04-27T10:14:44.598Z",
        "parking_space": [
            {

                "_id": "5ae2f89d8d51db4937b9df04",
               "parking_name": "my space 3",
                "restriction": "no",
                "hourly_rate": "60",
              "location": {
                    "type": "Point",
                    "coordinates": [
                        76.7786787,
                        30.7352527
                    ]
                },
            }
        ],
    },
   }
Run Code Online (Sandbox Code Playgroud)

我正在应用此查询.

User.find({
        "parking_space.location": {
            "$geoWithin": {
                "$centerSphere": [
                    [76.7786787, 30.7352527], 7 / 3963.2
                ]
            }
        },
    }, function(err, park_places) {
        if (err) {
            return res.send({
                data: err,
                status: false
            });
        } else {
            return res.send({
                data: park_places,
                status: true,
                msg: "Parking data according to location"
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

而我正在尝试获取这样的数据.

{
         "_id": "5ae04fd45f104a5980cf7e0e",
          "name": "Rehan",
         "email": "rehan@gmail.com",
         "status": true,
         "created_at": "2018-04-25T09:52:20.266Z",
         "parking_space": [
            {
                "_id": "5ae06d4d5f104a5980cf7e52",
                "parking_name": "my space 2",
                "restriction": "no",
                "hourly_rate": "6",
                "location": {
                    "type": "Point",
                    "coordinates": [
                        76.7786787,
                        30.7352527
                    ]
                },
            }
        ],
    },
    {
        "_id": "5ae2f8148d51db4937b9df02",
        "name": "nellima",
        "email": "neel@gmail.com",
        "status": true,
        "created_at": "2018-04-27T10:14:44.598Z",
        "parking_space": [
            {

                "_id": "5ae2f89d8d51db4937b9df04",
               "parking_name": "my space 3",
                "restriction": "no",
                "hourly_rate": "60",
              "location": {
                    "type": "Point",
                    "coordinates": [
                        76.7786787,
                        30.7352527
                    ]
                },
            }
        ],
    },
   }
Run Code Online (Sandbox Code Playgroud)

是否有可能获得这样的数据.

Nei*_*unn 5

对于您要在此处进行的操作,更好的选择是实际使用$geoNear聚合管道阶段来确定约束内的“最近”匹配。值得注意的是你的实际标准询问$geoWithin酒店7英里范围应用由数学的应用。因此,使用确实可以更好地表达这一点$geoNear,并且它的选项实际上允许您执行所需的操作。

User.aggregate([
  { "$geoNear": {
    "near": {
      "type": "Point",
      "coordinates": [76.7786787, 30.7352527]
    },
    "spherical": true,
    "distanceField": "distance",
    "distanceMultiplier": 0.000621371,
    "maxDistance": 7 * 1609.34,
    "includeLocs": "location"
  }},
  { "$addFields": {
    "parking_space": {
      "$filter": {
        "input": "$parking_space",
        "cond": {
          "$eq": ["$location", "$$this.location"]
        }
      }
    }
  }}
],function(err,park_places) {
  // rest of your code.
})
Run Code Online (Sandbox Code Playgroud)

这将产生如下结果:

{
        "_id" : "5ae04fd45f104a5980cf7e0e",
        "name" : "Rehan",
        "email" : "rehan@gmail.com",
        "status" : true,
        "created_at" : "2018-04-25T09:52:20.266Z",
        "parking_space" : [
                {
                        "_id" : "5ae06d4d5f104a5980cf7e52",
                        "parking_name" : "my space 2",
                        "restriction" : "no",
                        "hourly_rate" : "6",
                        "location" : {
                                "type" : "Point",
                                "coordinates" : [
                                        76.7786787,
                                        30.7352527
                                ]
                        }
                }
        ],
        "distance" : 0,
        "location" : {
                "type" : "Point",
                "coordinates" : [
                        76.7786787,
                        30.7352527
                ]
        }
}
{
        "_id" : "5ae2f8148d51db4937b9df02",
        "name" : "nellima",
        "email" : "neel@gmail.com",
        "status" : true,
        "created_at" : "2018-04-27T10:14:44.598Z",
        "parking_space" : [
                {
                        "_id" : "5ae2f89d8d51db4937b9df04",
                        "parking_name" : "my space 3",
                        "restriction" : "no",
                        "hourly_rate" : "60",
                        "location" : {
                                "type" : "Point",
                                "coordinates" : [
                                        76.7786787,
                                        30.7352527
                                ]
                        }
                }
        ],
        "distance" : 0,
        "location" : {
                "type" : "Point",
                "coordinates" : [
                        76.7786787,
                        30.7352527
                ]
        }
}
Run Code Online (Sandbox Code Playgroud)

我们在聚合管道中使用两个阶段,以便解释每个阶段的实际操作:

首先,$geoNear在给定的GeoJSON格式位置下执行查询,以便在"near"选项中进行比较,这当然是主要的约束条件。索引"spherical"通常需要该选项"2dsphere",这是您实际需要的数据索引类型。该"distanceField"是其他强制参数,并指定将实际记录从“文件”被匹配上的位置的查询点的距离属性的名称。

其他选项是使此功能适用于您要在此处执行的部分。首先是"distanceMultiplier",实际上是“可选”的,因为它仅控制将在所指定的属性中输出的值"distanceField"。我们在此处使用的值将把作为“距离”返回的米数调整为miles,这就是您通常要查看的距离。实际上,这对其余选项没有任何其他影响,但是由于"distanceField"是必需的,因此我们希望显示“期望的”数值。

下一个选项是另一个主要的“过滤器”,用于模仿您的$geoWithin陈述。该"maxDistance"选项设置了匹配位置可以“多远”的上限。在这种情况下,我们给它7的里程,我们乘上1609.34这是多少都是在英里。请注意"distanceMultiplier",对此数字没有影响,因此任何“转换”也必须在此完成。

这里的最后一个选项是"includeLocs",这实际上是距离约束之外最重要的选项。这是告诉我们文档中包含的位置数组实际用于“最近匹配”的“位置数据”的实际部分。当然,这里定义的是将用于在此管道阶段返回的文档中存储此数据的属性。您可以看到"location"添加到每个文档的其他属性反映了这一点。

因此,该管道阶段实际上已识别出匹配的"location"数据,但这并没有明确识别出实际匹配了哪个数组成员。因此,为了实际返回特定数组成员的信息,我们可以将其$filter用于比较。

当然,该操作是将“匹配位置”与每个数组成员的实际“位置”数据进行简单比较。由于将永远只有一个匹配项,因此您可以交替使用$indexOfArray$arrayElemAt进行比较,并仅提取“单个”结果,但这$filter通常是最容易理解的操作。


半径限制的整个要点都可以通过对条件的一些简短更改来证明。因此,如果我们将位置稍微移开:

  { "$geoNear": {
    "near": {
      "type": "Point",
      "coordinates": [76.7786787, 30.6352527]    // <-- different location
    },
    "spherical": true,
    "distanceField": "distance",
    "distanceMultiplier": 0.000621371,
    "maxDistance": 7 * 1609.34,
    "includeLocs": "location"
  }},
Run Code Online (Sandbox Code Playgroud)

它仍然在输出中报告的半径范围内,如以下指定"distanceField"

 "distance" : 6.917030204982402,
Run Code Online (Sandbox Code Playgroud)

但是,如果您将该半径更改为小于报告的数字:

  { "$geoNear": {
    "near": {
      "type": "Point",
      "coordinates": [76.7786787, 30.6352527]    // <-- different location
    },
    "spherical": true,
    "distanceField": "distance",
    "distanceMultiplier": 0.000621371,
    "maxDistance": 6.91 * 1609.34,      // <--- smaller radius
    "includeLocs": "location"
  }},
Run Code Online (Sandbox Code Playgroud)

然后查询将不返回问题中显示的任何文档。因此,您可以看到此设置如何控制与$geoWithin查询实现的边界相同的边界,当然,我们现在可以识别匹配的子文档。


多次比赛

作为对该主题的最后注解,我们可以看到如何使用该"includeLocs"选项来标识父文档数组中某个位置的匹配条目。尽管这应该适合此处的用例,但明显的限制是在一定范围内匹配多个位置。

因此,“多个”匹配完全超出了$geoNearMongoDB或其他地理空间操作的范围。另一种情况是,$unwind将数组内容放在初始位置之后$geoNear,然后是$geoWithin阶段,以便“过滤”这些多个匹配项:

User.aggregate([
  { "$geoNear": {
    "near": {
      "type": "Point",
      "coordinates": [76.7786787, 30.7352527]
    },
    "spherical": true,
    "distanceField": "distance",
    "distanceMultiplier": 0.000621371,
    "maxDistance": 7 * 1609.34,
  }},
  { "$unwind": "$parking_space" },
  { "$match": {
    "parking_space.location": {
      "$geoWithin": {
        "$centerSphere": [
          [76.7786787, 30.7352527], 7 / 3963.2
        ]
      }
    }
  }}
],function(err,park_places) {
  // rest of your code.
})
Run Code Online (Sandbox Code Playgroud)

$geoNear这里实际使用舞台可能会更好,并且我们实际上只是在做相同的事情而无需"includeLocs"选择。但是,如果您确实要这样做,那么只需$geoWithin$unwind舞台的任一侧使用,就没有错:

User.aggregate([
  { "$match": {
    "parking_space.location": {
      "$geoWithin": {
        "$centerSphere": [
          [76.7786787, 30.7352527], 7 / 3963.2
        ]
      }
    }
  }}
  { "$unwind": "$parking_space" },
  { "$match": {
    "parking_space.location": {
      "$geoWithin": {
        "$centerSphere": [
          [76.7786787, 30.7352527], 7 / 3963.2
        ]
      }
    }
  }}
],function(err,park_places) {
  // rest of your code.
})
Run Code Online (Sandbox Code Playgroud)

可以这样做的原因是,尽管当它实际上可以使用在集合上定义的地理空间索引时,它$geoWithin“最佳”地工作,但实际上它不需要索引才能返回结果。

因此,无论哪种情况,在“初始查询”返回包含至少一个条件匹配项的“文档”之后,我们简单地$unwind处理数组内容,然后再次应用相同的约束条件以过滤出那些数组条目,现在称为文档。如果您希望返回“数组”,则可以始终将元素$group$push数组重新组合成数组。

相反,$geoNear流水线阶段必须仅用作第一个流水线阶段。那是它唯一可以使用索引的地方,因此不可能在以后的阶段使用它。但是当然,“最近距离”信息可能对您有用,因此值得在查询结果和条件中包括在内。