【问题标题】:MongoDB Aggregation - Lookup pipeline not returning any documentsMongoDB聚合 - 查找管道不返回任何文档
【发布时间】:2020-01-19 02:41:40
【问题描述】:

我很难让 $lookuppipeline 在 MongoDB Compass 中工作。

我有以下收藏:

玩具

数据

[
  {
    "_id": {
      "$oid": "5d233c3bb173a546386c59bb"
    },
    "type": "multiple",
    "tags": [
      ""
    ],
    "searchFields": [
      "Jungle Stampers - Two",
      ""
    ],
    "items": [
      {
        "$oid": "5d233c3cb173a546386c59bd"
      },
      {
        "$oid": "5d233c3cb173a546386c59be"
      },
      {
        "$oid": "5d233c3cb173a546386c59bf"
      },
      {
        "$oid": "5d233c3cb173a546386c59c0"
      },
      {
        "$oid": "5d233c3cb173a546386c59c1"
      },
      {
        "$oid": "5d233c3cb173a546386c59c2"
      },
      {
        "$oid": "5d233c3cb173a546386c59c3"
      },
      {
        "$oid": "5d233c3cb173a546386c59c4"
      }
    ],
    "name": "Jungle Stampers - Two",
    "description": "",
    "status": "active",
    "category": {
      "$oid": "5cfe727cac920000086b880e"
    },
    "subCategory": "Stamp Sets",
    "make": "",
    "defaultCharge": null,
    "defaultOverdue": null,
    "sizeCategory": {
      "$oid": "5d0cfde57561e107c88fbde3"
    },
    "ageFrom": {
      "$numberInt": "24"
    },
    "ageTo": {
      "$numberInt": "120"
    },
    "images": [
      {
        "_id": {
          "$oid": "5d233c3bb173a546386c59bc"
        },
        "id": {
          "$oid": "5d233c39b173a546386c59ba"
        },
        "url": "/toyimages/5d233c39b173a546386c59ba.jpg",
        "thumbUrl": "/toyimages/thumbs/tn_5d233c39b173a546386c59ba.jpg"
      }
    ],
    "__v": {
      "$numberInt": "2"
    }
  }
]

贷款

数据

[
  {
    "_id": {
      "$oid": "5e1f1661b712215978c746d9"
    },
    "tags": [],
    "member": {
      "$oid": "5e17495e4f81ab3f900dbb63"
    },
    "source": "admin portal - potter1@gmail.com",
    "items": [
      {
        "id": {
          "$oid": "5e1f160eb712215978c746d5"
        },
        "status": "new",
        "_id": {
          "$oid": "5e1f1661b712215978c746db"
        },
        "toy": {
          "$oid": "5d233c3bb173a546386c59bb"
        },
        "cost": {
          "$numberInt": "0"
        }
      },
      {
        "id": {
          "$oid": "5e1f160eb712215978c746d5"
        },
        "status": "new",
        "_id": {
          "$oid": "5e1f1661b712215978c746da"
        },
        "toy": {
          "$oid": "5d233b1ab173a546386c59b5"
        },
        "cost": {
          "$numberInt": "0"
        }
      }
    ],
    "dateEntered": {
      "$date": {
        "$numberLong": "1579095632870"
      }
    },
    "dateDue": {
      "$date": {
        "$numberLong": "1579651200000"
      }
    },
    "__v": {
      "$numberInt": "0"
    }
  }
]

我正在尝试返回状态为“新”或“已淘汰”的玩具及其相关贷款的列表。

我可以使用以下$lookup 聚合来获取所有贷款:

{
  from: 'loans',
  localField: '_id',
  foreignField: 'items.toy',
  as: 'loansSimple'
}

但是我正在尝试使用管道加载具有我感兴趣的两种状态的贷款,但它总是只返回零个文档:

{
  from: 'loans',
  let: {
    'toyid': '$_id'
  },
  pipeline: [
    {
      $match: {
        $expr: {
          $and: [
            {$eq: ['$items.toy', '$$toyid']},
            {$eq: ['$items.status', 'new']} // changed from $in to $eq for simplicity
          ]
        }
      }
    }  
  ],
  as: 'loans'
}

这似乎总是返回 0 个文档,但是我安排它:

我是不是在某个地方弄错了?

我正在使用 MongoDB Atlas,v4.2.2,MongoDB Compass v 1.20.4

【问题讨论】:

  • 您能否将示例数据发布为“文本”?
  • @Valijon 我会添加它,虽然很难将其格式化,但请原谅格式
  • 你可以使用mongoplayground.net来格式化
  • @Valijon 添加了格式化数据

标签: mongodb aggregation-framework


【解决方案1】:

您正试图在内部数组中搜索$$toyid,但运算符表达式$eq 无法解析它。

最佳解决方案: $let(返回按条件过滤的loans)+ $filter(对内部数组应用过滤器)运算符帮助我们获得所需的结果。

db.toys.aggregate([
  {
    $lookup: {
      from: "loans",
      let: {
        "toyid": "$_id",
        "toystatus": "new"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $gt: [
                {
                  $size: {
                    $let: {
                      vars: {
                        item: {
                          $filter: {
                            input: "$items",
                            as: "tmp",
                            cond: {
                              $and: [
                                {
                                  $eq: [
                                    "$$tmp.toy",
                                    "$$toyid"
                                  ]
                                },
                                {
                                  $eq: [
                                    "$$tmp.status",
                                    "$$toystatus"
                                  ]
                                }
                              ]
                            }
                          }
                        }
                      },
                      in: "$$item"
                    }
                  }
                },
                0
              ]
            }
          }
        }
      ],
      as: "loans"
    }
  }
])

MongoPlayground

替代解决方案 1. 使用 $unwind 扁平化 items 属性。 (我们创建名为tmp 的额外字段,其中存储items 值,使用$unwind 运算符将其展平,按照您的操作进行匹配,然后从结果中排除)

db.toys.aggregate([
  {
    $lookup: {
      from: "loans",
      let: {
        "toyid": "$_id"
      },
      pipeline: [
        {
          $addFields: {
            tmp: "$items"
          }
        },
        {
          $unwind: "$tmp"
        },
        {
          $match: {
            $expr: {
              $and: [
                {
                  $eq: [
                    "$tmp.toy",
                    "$$toyid"
                  ]
                },
                {
                  $eq: [
                    "$tmp.status",
                    "new"
                  ]
                }
              ]
            }
          }
        },
        {
          $project: {
            tmp: 0
          }
        }
      ],
      as: "loans"
    }
  }
])

MongoPlayground

替代解决方案 2。我们使用$reduce 创建玩具的数组,并使用$in 运算符检查toyid 是否存在于该数组中。

db.toys.aggregate([
  {
    $lookup: {
      from: "loans",
      let: {
        "toyid": "$_id"
      },
      pipeline: [
        {
          $addFields: {
            toys: {
              $reduce: {
                input: "$items",
                initialValue: [],
                in: {
                  $concatArrays: [
                    "$$value",
                    [
                      "$$this.toy"
                    ]
                  ]
                }
              }
            }
          }
        },
        {
          $match: {
            $expr: {
              $in: [
                "$$toyid",
                "$toys"
              ]
            }
          }
        },
        {
          $project: {
            toys: 0
          }
        }
      ],
      as: "loans"
    }
  }
])

【讨论】:

  • “最佳解决方案”是最干净的,似乎表现最好,使用这个,谢谢!
  • 不客气。我不知道$let 运算符可以返回数组/对象/对象字段,我即将在 MongoDB JIRA 中打开一个新任务来实现这样的事情......很好的问题!
【解决方案2】:

$expr 收到 aggregation expressions,此时 $$items.toy 会按照您的预期为数组中的每个元素解析(但是,如果它会仍然会给您“坏”的结果,因为您将获得贷款在其 items 数组中具有所需的玩具 ID 和状态为 new 的任何其他项目。

因此,您有两种解决方法:

  1. 如果您不关心查找文档中的其他项目,您可以在查找管道的开头添加 $unwind 阶段,如下所示:
{
    from: 'loans',
    let: {
        'toyid': '$_id'
    },
    pipeline: [
        {
            $unwind: "$items"
        },
        {
            $match: {
                $expr: {
                    $and: [
                        {$eq: ['$items.toy', '$$toyid']},
                        {$eq: ['$items.status', 'new']} // changed from $in to $eq for simplicity
                    ]
                }
            }
        }
    ],
    as: 'loans'
}
  1. 如果您确实关心它们,只需以一种可能的方式迭代数组以获得“正确”匹配,这里是一个使用 $filter 的示例
    {
        from: 'loads',
        let: {
            'toyid': '$_id'
        },
        pipeline: [
            {
                $addFields: {
                    temp: {
                        $filter: {
                            input: "$items",
                            as: "item",
                            cond: {
                                $and: [
                                    {$eq: ["$$item.toy", "$$toyid"]},
                                    {$eq: ["$$item.status", "new"]}
                                ]
                            }
                        }


                    }
                }

            }, {$match: {"temp.0": {exists: true}}}
        ],
        as: 'loans'
    }

【讨论】:

    猜你喜欢
    • 2023-02-10
    • 2021-11-16
    • 2023-03-30
    • 1970-01-01
    • 2021-06-01
    • 2023-02-25
    • 1970-01-01
    • 2017-10-27
    相关资源
    最近更新 更多