使用嵌套查询的Mongo $ lookup过滤器

时间:2019-05-21 10:52:47

标签: mongodb aggregation-framework lookup

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

我的数据的简化版本如下。...

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

.. and ..

    // '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();

在该示例中,第一阶段将匹配j1j2,因为它们都包含“ 1234”,那么我想根据任务状态进一步过滤出作业,例如,只有j2的任务包含status==FAILED,所以我的最终结果将是j2文档。

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

1 个答案:

答案 0 :(得分:1)

$match管道内的$lookupjobCollection文档没有任何关系。它仅过滤tasks集合的文档。因此,您必须在$match之后再使用一个$lookup阶段来筛选出ROOT(jobCollection)文档。

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": [] }}},
])