首页 > 解决方案 > Mongo $lookup 过滤器使用嵌套查询

问题描述

我正在使用 mongoDB 的 $lookup 函数,特别是管道语法,以允许我执行一些比我使用的 ORM(Sails/Waterline)允许的更复杂的查询。

我的数据的精简版看起来像....

    // 'job' collection
    {
      "id"      : j1,
      "mediaID" : "ABC1234"
    },
    {
      "id"      : j2,
      "mediaID" : "DEF1234"
    },
    {
      "id"      : j3,
      "mediaID" : "FGH3456"
    }

..和..

    // 'task' collection

    // j1 tasks
    {
      "id"      : "t1",
      "job"     : "j1",
      "taskName": "MOVE",
      "status"  : "COMPLETE"
    },
    {
      "id"      : "t2",
      "job"     : "j1",
      "taskName": "PUBLISH",
      "status"  : "COMPLETE"
    },
    // j2 tasks
    {
      "id"      : "t3",
      "job"     : "j2",
      "taskName": "MOVE",
      "status"  : "FAILED"
    },
    // j3 tasks
    {
      "id"      : "t4",
      "job"     : "j3",
      "taskName": "MOVE",
      "status"  : "COMPLETE"
    }

..其中任务集合链接到作业集合通过job.id -> task.job

我想要实现的是,能够通过job.mediaID和/或也过滤工作task.status。我目前拥有的查询几乎得到了我想要的,但它没有过滤掉jobs,它只是没有填充tasks部分。

我目前的查询如下...

    let results = await jobCollection.aggregate([

      // First filter jobs with matching criteria
      {
        $match: {
          $text: {$search: "1234"}
        }
      },

      // Next, link the task collection, and filter by status
      {
        "$lookup": {
          from    : 'task',
          'let'   : {job_id: '$_id'},
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    // This does the linking of job.id -> task.job_id
                    {$eq: ['$job', '$$job_id']},
                    // This filters out unwanted tasks
                    {$eq: ['$status', 'FAILED']}
                  ]
                }
              }
            }
          ],
          as      : 'tasks'
        }
      }
    ])
    .toArray();

在该示例中,第一阶段将匹配j1并且j2由于它们都包含“1234”,因此我想根据任务状态进一步过滤掉作业,例如,只有j2一个具有 的任务status==FAILED,所以我的最终结果是只是j2文件。

我希望这能让。我想我可能只需要在最后添加一些巧妙的投影。谢谢

标签: mongodbaggregation-frameworklookup

解决方案


内部管道与文档无关$match。它只过滤要收集的文档。因此,您必须在 之后再使用一个阶段来过滤掉 ROOT( ) 文档。$lookupjobCollectiontasks$match$lookupjobCollection

jobCollection.aggregate([
  { "$match": { "$text": { "$search": "1234" }}},
  { "$lookup": {
    "from": "task",
    "let": { "job_id": "$_id" },
    "pipeline": [
      { "$match": {
        "$expr": {
          "$and": [
            { "$eq": ["$job", "$$job_id"] },
            { "$eq": ["$status", "FAILED"] }
          ]
        }
      }}
    ],
    "as": "tasks"
  }},
  { "$match": { "tasks": { "$ne": [] }}},
])

推荐阅读