mongoose 对lookup $match中的两个集合(原始集合和外部集合)使用条件

1bqhqjot  于 2022-11-13  发布在  Go
关注(0)|答案(2)|浏览(154)

我不确定是真实的的有问题还是缺少文档,您可以将国外收集文档的条件放在查找$match中,也可以将原始收集文档的条件放在查找$match中,$expr
但是当我想同时使用这两个特性时,它就不起作用了。

{ $lookup:
    {
      from: 'books',
      localField: 'itemId',
      foreignField: '_id',
      let: { "itemType": "$itemType" },
      pipeline: [
         { $match: { $expr: { $eq: ["$$itemType", "book"] } }}
      ],
      as: 'bookData'
    }
  }

$expr为原始文档设置了条件。但如果我只想获取带有status: 'OK'的外来文档,该怎么办?类似于:

{ $match: { status: "OK", $expr: { $eq: ["$$itemType", "book"] } }}

不起作用。

wqlqzqxt

wqlqzqxt1#

我试着用你提供的情况来玩。试着把$expr作为$match对象的第一个键。它应该能做到这一点。

{ $lookup:
    {
      from: 'books',
      localField: 'itemId',
      foreignField: '_id',
      let: { "itemType": "$itemType" },
      pipeline: [
         { $match: { $expr: { $eq: ["$$itemType", "book"] }, status: 'OK' }}
      ],
      as: 'bookData'
    }
  }
v6ylcynt

v6ylcynt2#

目前公认的答案是“错误的”,因为它实际上并没有改变任何东西。$match predicate 的字段表示顺序并没有什么区别。我将用您的具体情况来演示这一点,但这里有一个额外的复杂性,我们将在稍后讨论。同时,请考虑以下文档:

{
    _id: 1,
    status: "OK",
    key: 123
  }

此查询:

db.collection.find({
  status: "OK",
  $expr: {
    $eq: [
      "$key",
      123
    ]
  }
})

下面这个查询只是颠倒了 predicate 的顺序:

db.collection.find({
  $expr: {
    $eq: [
      "$key",
      123
    ]
  },
  status: "OK"
})

将查找并返回该文档。可以在herehere中找到第一个的演示。
同样,您的原始$match

{ $match: { status: "OK", $expr: { $eq: ["$$itemType", "book"] } }}

将表现得与已接受答案中的行为相同:

{ $match: { $expr: { $eq: ["$$itemType", "book"] }, status: 'OK' }}

换句话说,是否首先使用$expr在行为上没有区别。但是,我怀疑总体聚合没有表达您想要的逻辑。让我们进一步探讨这个问题。首先,我们需要解决这个问题:
$expr正在放置原始文档条件
事实并非如此。根据the documentation for $expr,该运算符“* 允许在查询语言中使用聚合表达式。*”
此功能的主要用途(实际上也是文档中列出的第一个功能)是比较单个文档中的两个字段。在$lookup的上下文中,引用原始文档中的字段的功能允许您将它们的值与要联接的集合进行比较。文档中有一些这样的示例,例如此处和该页上引用$expr其他地方。
记住这一点,让我们回到聚合上,如果我理解正确的话,您使用{ $expr: { $eq: ["$$itemType", "book"] } predicate 的目的是从 original 集合中过滤文档,对吗?
如果是这样的话,那么这不是您当前正在做的聚合。您可以在this playground example中看到,嵌套在$lookuppipeline中的$match * 不 * 影响原始集合中的文档。相反,您应该通过在基pipeline上的初始$match来进行过滤。因此,类似于this的内容:

db.orders.aggregate([
  {
    $match: {
      $expr: {
        $eq: [
          "$itemType",
          "book"
        ]
      }
    }
  }
])

或者,更简单地说,this

db.orders.aggregate([
  {
    $match: {
      "itemType": "book"
    }
  }
])

基于所有这些,您的最终管道看起来可能与以下内容类似:

db.orders.aggregate([
  {
    $match: {
      "itemType": "book"
    }
  },
  {
    $lookup: {
      from: "books",
      localField: "itemId",
      foreignField: "_id",
      let: {
        "itemType": "$itemType"
      },
      pipeline: [
        {
          $match: {
            status: "OK"
          }
        }
      ],
      as: "bookData"
    }
  }
])

Playground example here。此管道:
1.按数据的itemType筛选原始集合(orders)中的数据。它从示例数据中删除包含_id: 3的文档,因为该文档的itemType与我们要查找的文档("book")的itemType不同。
1.它使用localField/foreignField语法在books中查找数据,其中books文档的_idorders集合中源文档的itemId相匹配。
1.它进一步使用let/pipeline语法来表示books文档的status"OK"的附加条件。这就是为什么具有"BAD"statusbooks文档不会被拉入具有_id: 2orders文档的bookData中。
第二部分和第三部分(合并)的文档位于此处。

相关问题