首页 > 解决方案 > 如何使用 MongoDB 聚合 $lookup 和 $addFields 比较字段并在匹配时返回值?

问题描述

我有 2 个 MongoDB 集合,这里有 2 个模型示例:

用户集合:

{
  _id: ObjectId,
  available: Boolean,
  province: String,
  city: String,
  [...]
}

距离集合:

{
  _id: ObjectId,
  start_province: String,
  start_city: String,
  end_province: String,
  end_city: String,
  distance: Number
}

我需要的是使用类似的查询来查找用户{available: true},然后使用一些聚合向每个用户文档添加一个距离字段,如果用户的城市之间存在匹配,则距离字段start_provincestart_city是在此堆栈之前在 JS 中定义的固定值)。

如果有匹配距离应包含距离值,否则为 0(或未定义)。

我认为我应该使用 $lookup 和/或 $addFields,但我对聚合运算符还没有足够的信心来解决这个问题。

用户结果示例:

[
  {
    _id: ObjectId,
    available: Boolean,
    province: String,
    city: String,
    [...],
    distance: Number
  },
  {...}
]

谢谢你的帮助!


编辑: 按要求添加示例数据:

Users
[
  {name: 'John', available: true, province: 'Rome', city: 'Tivoli'},
  {name: 'Difool', available: true, province: 'Rome', city: 'Ostia'},
  {name: 'Paul', available: true, province: 'Rome', city: 'Rome'},
  {name: 'Andrew', available: false, province: 'Rome', city: 'Grottaferrata'}
]

Distances
[
  {start_province: 'Rome', start_city: 'Rome', end_province: 'Rome', end_city: 'Ostia', distance: 5},
  {start_province: 'Rome', start_city: 'Rome', end_province: 'Rome', end_city: 'Tivoli', distance: 8}
]

用户的预期结果

[
  {name: 'John', available: true, province: 'Rome', city: 'Tivoli', distance: 8},
  {name: 'Difool', available: true, province: 'Rome', city: 'Ostia', distance: 5},
  {name: 'Paul', available: true, province: 'Rome', city: 'Rome', distance: 0}
]

过滤查询:{ available: true }

User.province需要与Distance.end_province匹配

User.city需要与Distance.end_city匹配。

否则,距离应返回 0 或未定义。谢谢。

标签: mongodbaggregate

解决方案


询问

  • 过滤以保持可用
  • 如果加入
    • User.province 需要与 Distance.end_province 匹配
    • User.city 需要与 Distance.end_city 匹配。
  • 如果未加入 => 距离=0 否则获取与第一个加入文档的距离
  • 取消距离(连接结果)

*如果您有 mongodb <5 和索引,如果您可以使用 $eq 查询运算符进行第二次匹配,而不是我使用的聚合 $eq(它相同,只需删除 $expr 并使用查询 $eq)

测试代码在这里

db.users.aggregate([
  {
    "$match": {
      "available": {
        "$eq": true
      }
    }
  },
  {
    "$lookup": {
      "from": "distances",
      "let": {
        "uprovince": "$province",
        "ucity": "$city"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              "$and": [
                {
                  "$eq": [
                    "$$uprovince",
                    "$end_province"
                  ]
                },
                {
                  "$eq": [
                    "$$ucity",
                    "$end_city"
                  ]
                }
              ]
            }
          }
        }
      ],
      "as": "distances"
    }
  },
  {
    "$set": {
      "distance": {
        "$cond": [
          {
            "$eq": [
              "$distances",
              []
            ]
          },
          0,
          {
            "$arrayElemAt": [
              "$distances.distance",
              0
            ]
          }
        ]
      }
    }
  },
  {
    "$unset": [
      "distances"
    ]
  }
])

推荐阅读