【问题标题】:Finding all documents within date range and within distance of point查找日期范围内和点距离内的所有文档
【发布时间】:2018-07-12 23:44:25
【问题描述】:

我正在开发一个系统,该系统允许用户注册以接收有关在特定日期范围内某个位置一定距离内发布的新事件的警报。

当用户注册警报时,我会创建一个如下所示的文档:

{
    "_id":"5b43ab8eb0a638000e188615",
    "confirmed":true,
    "confirmed_at":"2018-07-10T14:20:51.078Z",
    "created_at":"2018-07-09T18:38:06.874Z",
    "date_start":"2018-07-30T00:00:00.000Z",
    "date_end":"2018-08-31T00:00:00.000Z",
    "email":"me@example.com",
    "location":{
        "type":"Point",
        "coordinates": [-90.44065060000003,31.5790588]
    },
    "location_name":"Brookhaven, MS 39601, USA",
    "distance":150,
    "unsubscribed":false,
    "unsubscribed_at":null
}

位置字段上还有一个2dspehere 索引。

创建新事件时,我需要能够查询集合中的所有文档,这些文档的 date_startdate_end 范围包含事件的日期,并且球体中的位置包含事件。

给定一个看起来像这样的事件:

{
    start_date: "2018-08-01",
    latitude: 30.6954595,
    longitude: -88.174414
}

我希望上面的文档会被返回,因为活动的开始日期在给定的范围内,并且活动在距离所需位置 150 英里(准确地说是 147 英里)内。

另外需要注意的是,事件本身在 MongoDB 中并不存在。它们在单独的系统中创建和管理,我们将定期查询该系统的 API 以获取新创建的事件。

这是我第一次涉足 mongodb 的地理空间方面,所以我不确定如何处理这个问题。我一直在尝试使用 $geoWithin$centerSphere 运算符,但似乎无法让它们工作。

我是否正在尝试从 MongoDB 中进行单个查询,或者我是否需要提取所有满足数据范围条件的文档并在应用程序中遍历文档以查看它们的范围包含活动地点?

我想我已经包含了所有相关信息,但如果需要更多信息,请提出,我会更新问题。

谢谢!

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    经过大量的谷歌搜索和阅读 MongoDB 的文档后,我想我已经找到了这个问题的正确答案,并认为我会与将来可能偶然发现这个问题的任何人分享。

    所以,解决这个问题的最好方法似乎是利用 MongoDB 的聚合框架。

    为了得到我需要的答案,我必须设置一个 3 阶段聚合管道。

    第一步是使用$geoNear 阶段(documentation)。这允许我从外部数据源传递相关事件的坐标以及事件的日期。使用这些值,我创建了一个如下所示的阶段:

    {
        $geoNear: {
            near: { type: "Point", coordinates: [event-longitude-goes-here,event-latitude-goes-here]},
            distanceField: "calculated_distance" /** This field is appended to the returned documents */,
            distanceMultiplier: 0.000621371 /** This converts meters to miles */,
            maxDistance: 804672 /** this is 500 miles expressed in meters */,
            query: {
                /** This lets us do a preliminary filter of the documents so we don't do so many distance calculations */
                confirmed: true,
                unsubscribed: false,
                date_start: {$lte: Date('event-start-date-here')},
                date_end: {$gte: Date('event-start-date-here-again')}
            },
            spherical: true
        }
    }
    

    这为我提供了一组文档,其 date_startdate_end 范围包含活动开始日期,并且距离活动坐标的距离为 500 英里或更短。请注意,使用了 500 英里的最大距离,因为这是用户可以请求警报的最大距离,因此没有理由将文档退回到更远的地方。

    接下来,我需要添加一个$project 阶段(documentation),这样我就可以只提取我想要的字段。这个阶段还允许我对文档本身中的两个不同字段进行比较。就我而言,在即将到来的$match 阶段不允许比较两个文档字段。

    {
        $project: {
            _id: 1,
            email: 1,
            location_name: 1,
            calculated_distance: 1,
            distance: 1,
            ofInterest: {$gte: ["$distance", "$calculated_distance"]}
        }
    }
    

    投影中的ofInterest 字段为$geoNear 阶段返回的文档添加了一个新字段。如果$distance 大于或等于$calculated_distance,此字段将为true,否则为false

    最后,我创建了一个$match 阶段(documentation),它会拉出所有$ofInterest 字段为true 的文档。

    {
        $match: {
            ofInterest: true
        }
    }
    

    生成的文档集合将仅包含那些满足日期标准并且距离值大于用户到事件的距离的文档。

    完整的aggregate 电话最终看起来像这样:

    db.collection.aggregate([
    {
        $geoNear: {
            near: { type: "Point", coordinates: [-88.174414,30.6954595]},
            distanceField: "calculated_distance",
            distanceMultiplier: 0.000621371,
            maxDistance: 804672,
            query: {
                confirmed: true,
                unsubscribed: false,
                date_start: {$lte: Date('2018-07-30')},
                date_end: {$gte: Date('2018-07-30')}
            },
            spherical: true
        }
    },
    {
        $project: {
            _id: 1,
            email: 1,
            location_name: 1,
            calculated_distance: 1,
            distance: 1,
            ofInterest: {$gte: ["$distance", "$calculated_distance"]}
        }
    },
    {
        $match: {
            ofInterest: true
        }
    }]);
    

    得到这个答案比我最初想象的要复杂一些,但我认为一旦你了解了发生了什么,它就非常简单了。

    【讨论】:

      猜你喜欢
      • 2021-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-22
      • 2016-10-08
      • 1970-01-01
      相关资源
      最近更新 更多