如何使用中间表通过$ lookup管道查找值

时间:2019-01-30 05:23:04

标签: mongodb mongoose

我有3个班级:父母-中级-子女。所有关系均为“ 1:m”。 任务是:为每个父母获得有条件的所有孩子的总和。

代码如下:

'use strict';
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let TestParentSchema = new Schema({
  Name: { type: String },
});
module.exports = mongoose.model('TestParent', TestParentSchema);

'use strict';
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let TestIntermediateSchema = new Schema({
  Name: { type: String },
  Parent: { type: Schema.ObjectId, ref: 'TestParent' },
});
module.exports = mongoose.model('TestIntermediate', TestIntermediateSchema);

'use strict';
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let TestChildSchema = new Schema({
  Name: { type: String },
  Value: { type: Number, required: true },
  Intermediate: { type: Schema.ObjectId, ref: 'TestIntermediate' },
});
module.exports = mongoose.model('TestChild', TestChildSchema);

按如下所示填充它:

  let parent1 = new TestParent({ Name: 'parent1' });
  let parent2 = new TestParent({ Name: 'parent2' });
  parent1.save();
  parent2.save();

  let inter11 = new TestIntermediate({ Name: 'inter1-1', Parent: parent1 });
  let inter21 = new TestIntermediate({ Name: 'inter2-1', Parent: parent2 });
  inter11.save();
  inter21.save();

  let child111 = new TestChild({ Name: 'child1-1-1', Intermediate: inter11, Value: 5 });
  let child112 = new TestChild({ Name: 'child1-1-2', Intermediate: inter11, Value: 60 });
  let child211 = new TestChild({ Name: 'child2-1-1', Intermediate: inter21, Value: 10 });
  let child212 = new TestChild({ Name: 'child2-1-2', Intermediate: inter21, Value: 70 });

  child111.save();
  child112.save();
  child211.save();
  child212.save();

目标是为每个父母的Value <50的孩子求和。 (“ parent1”为“ 5”,“ parent2”为“ 10”)

首先,让我们获取所有子代的值:

TestParent.aggregate(
    [
      {
        $lookup: {
          from: 'testintermediates',
          localField: '_id',
          foreignField: 'Parent',
          as: 'myIntermediates',
        },
      },
      {
        $lookup: {
          from: 'testchildren',
          localField: 'myIntermediates._id',
          foreignField: 'Intermediate',
          as: 'myChildren',
        },
      },
      {
        $project: {
          myName: '$Name',
          sumChildren: { $sum: '$myChildren.Value' },
        },
      },
    ],
    function(err, results) {
      let res1 = results[0].sumChildren === 5;
      let res2 = results[1].sumChildren === 10;
    }
  );

好的。

现在让我们尝试向第二次查找添加条件:

TestParent.aggregate(
    [
      {
        $lookup: {
          from: 'testintermediates',
          localField: '_id',
          foreignField: 'Parent',
          as: 'myIntermediates',
        },
      },
      // {
      //   $lookup: {
      //     from: 'testchildren',
      //     localField: 'myIntermediates._id',
      //     foreignField: 'Intermediate',
      //     as: 'myChildren',
      //   },
      // },
      // { //to check if this approach works with the first level (it works)
      //   $lookup: {
      //     from: 'testintermediates',
      //     let: { myid: '$_id' },
      //     pipeline: [
      //       {
      //         $match:
      //         {
      //           $expr: {
      //             $eq: ['$Parent', '$$myid'],
      //             // $eq: ['$LocalId', 55],

      //           },
      //         },
      //       },
      //     ],
      //     as: 'myIntermediates',
      //   },
      // },
      {
        $lookup: {
          from: 'testchildren',
          let: { intermedId: '$myIntermediates._id' },
          pipeline: [
            {
              $match:
              {
                $expr: {
                   $eq: ['$Intermediate', '$$intermedId'], //it doesn't work (
                  // $eq: ['$Value', 60], //it works
                },
              },
            },
          ],
          as: 'myChildren',
        },
      },
      {
        $project: {
          myName: '$Name',
          sumChildren: { $sum: '$myChildren.Value' },
        },
      },
    ],
    function(err, results) {
      let res1 = results[0].sumChildren === 5;
      let res2 = results[1].sumChildren === 10;
    }
  );

就目前而言,我通过将'$ unwind'与'$ group'结合使用来解决此任务,但想知道是否有一种方法可以仅向管道添加条件。谢谢您的帮助。

1 个答案:

答案 0 :(得分:0)

  

想知道是否有一种方法可以向管道中添加条件

首先,让我们从管道的第一阶段开始进行分解。 $lookup的第一阶段:

    "$lookup": {
      from: "testintermediates",
      localField: "_id",
      foreignField: "Parent",
      as: "myIntermediates"}

将匹配的中间体数组返回为myIntermediates。例如:

    {"_id": 1, 
     "x": "some value",
     "myIntermediates": [
         { "_id": 1, "Parent": 1, "y": 5 },
         { "_id": 2, "Parent": 1, "y": 7 },
         { "_id": 3, "Parent": 1, "y": 2 }
     ]}

当您使用另一个single equality match syntax of $lookup链接第二个$lookup阶段时:

    "$lookup": {
      from: "testchildren", 
      localField: "myIntermediates._id",
      foreignField: "Intermediate",
      as: "myChildren"}

之所以可行,是因为当localField是一个数组时,localFieldforeignField之间的相等条件被视为:

"foreignField": { "$in": [ "localField.elem1", "localField.elem2", ... ] }

因此,如果您在$in expression的第二个$lookup阶段也使用uncorrelated subqueries $lookup syntax,它也应该工作。 使用$eq无效的原因是,单个值与值数组之间的相等比较不正确。

总而言之,您的聚合查询可以写为:

TestParent.aggregate(
    [
      {"$lookup": {
          from: "testintermediates",
          localField: "_id",
          foreignField: "Parent",
          as: "myIntermediates",
        },
      },
      {"$lookup": {
          from: "testchildren",
          let: { "intermedId": "$myIntermediates._id" },
          pipeline: [
            {"$match":
              {"$expr": {
                   "$in": ["$Intermediate", "$$intermedId"],
                },
              },
            },
          ],
          as: "myChildren",
        },
      },
      {"$project": {
          myName: "$Name",
          sumChildren: { "$sum": "$myChildren.Value" },
        },
      },
    ]);

另请参阅Specify Multiple Join Conditions with $lookup