在$ lookup上使用Foriegn Collection的$ geowithin

时间:2017-09-21 10:28:33

标签: mongodb mongodb-query aggregation-framework

我有两个集合成员和MobileUserLocations - 其中每个用户位置被保存(可以是多个)作为userId作为外来字段。

成员:

{
  _id: ObjectId("591553ffa4233a181506880c"),
  userName: "Test user"
}

MobileUserLocations:

{ _id: ObjectId("59156070a4233a1815068b6b"),
  userId: ObjectId("591553ffa4233a181506880c"),
  location: {type: "Point", coordinates: [76.9121, 10.2232]]},
  updatedOn: 2017-05-12T07:12:48.626Z,
  status: 1 
},
{ _id: ObjectId("59156070a4233a1815068b6b"),
  userId: ObjectId("591553ffa4233a181506880c"),
  location: {type: "Point", coordinates: [76.8121, 10.1232]]},
  updatedOn: 2017-05-12T07:12:48.626Z,
  status: 1 
}

我希望得到半径范围内的会员 - 比如说特定地理位置的5公里 - 比如:[10.0132295,76.3630502](lat,lng格式)。

我试过了:

collection.aggregate([
                                {$match: {_id: { $ne: options.accessToken.userId }},
                                { "$lookup": {
                                    "localField": "_id",
                                    "from": "MobileUserLocations",
                                    "foreignField": "userId",
                                    "as": "userLocInfo"
                                  }
                                },
                                {
                                    $project: {
                                        _id: 1,
                                        userLocInfo: {
                                           "$filter": {
                                                "input": "$userLocInfo",
                                                "as": "userLoc",
                                                "cond": { 
                                                            "$eq": [ "$$userLoc.status", -1],  
                                                       "$$userLoc.location": {"$geoWithin": {"$centerSphere": [[76.3630502, 10.0132295], 5 / 3963.2]}}
                                                        }
                                            } 
                                        }
                                    }
                                },
                                {$unwind: "$userLocInfo"}

                      ]

但没有得到。如果我从过滤器cond中删除$ geowithin,它就会得到,否则就没有了。但是,如果我个性化地查询集合,我就会得到结果。

任何人都可以知道这个问题吗?

1 个答案:

答案 0 :(得分:1)

这不起作用,因为$geoWithin不是"逻辑运算符",但它是"查询运算符"并且只能在使用$match的聚合管道中使用。幸运的是,这真的是你想要的。虽然你还没有明白为什么:

collection.aggregate([
  { "$match": { 
    "_id": { "$ne": options.accessToken.userId }
  }},
  { "$lookup": {
    "localField": "_id",
    "from": "MobileUserLocations",
    "foreignField": "userId",
    "as": "userLocInfo"
  }},
  { "$unwind": "$userLocInfo" },
  { "$match": {
    "userLocInfo.status": -1, 
    "userLocInfo.updatedOn": "2017-05-12T12:11:04.183Z", 
    "userLocInfo.location": {
      "$geoWithin": {
        "$centerSphere": [[76.3630502, 10.0132295], 5 / 3963.2]
      }
    }
  }}
])

除此之外,还有一个非常好的理由是它的唯一运作方式。要理解,请查看"explain"输出:

    {
        "$lookup" : {
            "from" : "MobileUserLocations",
            "as" : "userLocInfo",
            "localField" : "_id",
            "foreignField" : "userId",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "$and" : [ 
                    {
                        "status" : {
                            "$eq" : -1.0
                        }
                    }, 
                    {
                        "updatedOn" : {
                            "$eq" : "2017-05-12T12:11:04.183Z"
                        }
                    }, 
                    {
                        "location" : {
                            "$geoWithin" : {
                                "$centerSphere" : [ 
                                    [ 
                                        76.3630502, 
                                        10.0132295
                                    ], 
                                    0.00126160678239806
                                ]
                            }
                        }
                    }
                ]
            }
        }
    }

这告诉你的是,$unwind和跟随$match都被$lookup阶段吸收。这意味着$geoWithin和其他条件实际上是在外国集合上执行的"在" 之前返回结果。

这就是$lookup处理可能违反16MB限制的结果联接的方式。它也是目前最有效的方式"过滤"加入的结果。

这就是你真正想要做的事情。

根据您问题中的数据,此声明:

db.members.aggregate([
  { "$lookup": {
    "localField": "_id",
    "from": "MobileUserLocations",
    "foreignField": "userId",
    "as": "userLocInfo"
  }},
  { "$unwind": "$userLocInfo" },
  { "$match": {
    "userLocInfo.location": {
      "$geoWithin": {
        "$centerSphere": [[76.9121, 10.2232], 5 / 3963.2]
      }
    }
  }}
])

过滤掉$lookup中符合约束的一个位置:

/* 1 */
{
    "_id" : ObjectId("591553ffa4233a181506880c"),
    "userName" : "Test user",
    "userLocInfo" : {
        "_id" : ObjectId("59c3c37359f55d64d6e30297"),
        "userId" : ObjectId("591553ffa4233a181506880c"),
        "location" : {
            "type" : "Point",
            "coordinates" : [ 
                76.9121, 
                10.2232
            ]
        },
        "updatedOn" : ISODate("2017-05-12T07:12:48.626Z"),
        "status" : 1.0
    }
}