Mongodb聚合嵌套查询

时间:2020-10-11 16:47:47

标签: mongodb aggregation

对不起,标题不好,但这是我要解决的问题:

我有几个集合,包括票证,字段和fieldOptions,看起来像这样:

门票

{
    _id: 1,
    subject: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
    fields: [
        {
            key: "part-number",
            value: "abc123",
        },
        {
            key: "price",
            value: "10",
        },
        {
            key: "officer",
            value: "2",
        },
    ]
}

字段

[
    {
        _id: 1,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        options: [],
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        displayOrder: 0,
        key: "part-number",
        label: "Part Number",
        required: false,
        type: "text",
    },
    {
        _id: 2,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        options: [],
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        displayOrder: 1,
        key: "price",
        label: "Price",
        required: false,
        type: "text",
    },
    {
        _id: 3,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        options: [1, 2, 3, 4],
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        displayOrder: 2,
        key: "officer",
        label: "Officer",
        required: false,
        type: "select",
    },
    // THIS WAS ADDED AFTER THE 'TICKET' DOC WAS CREATED, SO IT'S NOT LISTED UNDER 'FIELDS' IN THERE
    {
        _id: 4,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        options: [],
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        displayOrder: 1,
        key: "notes",
        label: "Notes",
        required: false,
        type: "text",
    },
]

字段选项

[
    {
        _id: 1,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        key: "none",
        label: "None",
        displayOrder: 0,
        legacy: false,
    },
    {
        _id: 2,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        key: "picard",
        label: "Picard",
        displayOrder: 1,
        legacy: false,
    },
    {
        _id: 3,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        key: "riker",
        label: "Riker",
        displayOrder: 2,
        legacy: false,
    },
    {
        _id: 4,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        key: "data",
        label: "Data",
        displayOrder: 3,
        legacy: false,
    },
    {
        _id: 5,
        created: ISODate("2020-09-10T20:23:46.382Z"),
        updated: ISODate("2020-09-10T20:23:46.382Z"),
        active: true,
        key: "red-shirt",
        label: "Red Shirt",
        displayOrder: 4,
        legacy: true,
    },
]

在我的聚合管道中,我有以下内容:

[
    { $match: {_id: 2} },
    { $unwind: { path: "$fields", "preserveNullAndEmptyArrays": true } },
            {
                "$lookup": {
                    "from": "fields",
                    "let": { "key": "$fields.key" },
                    "as": "fields.def",
                    "pipeline": [
                        { "$match": { "$expr": { "$eq": ["$key", "$$key"] } } },
                        {
                            "$lookup": {
                                "from": "fieldoptions",
                                "let": { "options": "$options" },
                                "pipeline": [
                                    { "$match": { "$expr": { "$in": ["$_id", "$$options"] } } },
                                ],
                                "as": "options"
                            }
                        }
                    ]
                }
            },
            { $unwind: { path: "$fields.def", "preserveNullAndEmptyArrays": true } },
            {
                $group: {
                    _id: "$_id",
                    fields: { $push: "$$ROOT.fields" },
                    root: { $mergeObjects: "$$ROOT" },
                }
            },
            {
                $replaceRoot: {
                    newRoot: {
                        $mergeObjects: ["$root", "$$ROOT"]
                    }
                }
            },
            {
                $unset: "root"
            },
            { $project: { fields: '$fields'}} // this is to remove other junk for testing
]

这带来了以下(几乎正确的)结果:

{
    "_id" : 2,
    "fields" : [ 
        {
            "key" : "part-number",
            "value" : "abc123",
            "def" : {
                "_id" : 1,
                "created" : ISODate("2020-09-10T20:23:46.382Z"),
                "options" : [],
                "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                "active" : true,
                "displayOrder" : 0,
                "key" : "part-number",
                "label" : "Part Number",
                "required" : false,
                "type" : "text",
                "__v" : 0
            }
        }, 
        {
            "key" : "price",
            "value" : "10",
            "def" : {
                "_id" : 2,
                "created" : ISODate("2020-09-10T20:23:46.382Z"),
                "options" : [],
                "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                "active" : true,
                "displayOrder" : 1,
                "key" : "price",
                "label" : "Price",
                "required" : false,
                "type" : "text",
                "__v" : 0
            }
        }, 
        {
            "key" : "officer",
            "value" : "2",
            "def" : {
                "_id" : 3,
                "created" : ISODate("2020-09-10T20:23:46.382Z"),
                "options" : [ 
                    {
                        "_id" : 1,
                        "created" : ISODate("2020-09-10T20:23:46.382Z"),
                        "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                        "active" : true,
                        "key" : "none",
                        "label" : "None",
                        "displayOrder" : 0,
                        "legacy" : false,
                        "__v" : 0
                    }, 
                    {
                        "_id" : 2,
                        "created" : ISODate("2020-09-10T20:23:46.382Z"),
                        "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                        "active" : true,
                        "key" : "picard",
                        "label" : "Picard",
                        "displayOrder" : 1,
                        "legacy" : false,
                        "__v" : 0
                    }, 
                    {
                        "_id" : 3,
                        "created" : ISODate("2020-09-10T20:23:46.382Z"),
                        "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                        "active" : true,
                        "key" : "riker",
                        "label" : "Riker",
                        "displayOrder" : 2,
                        "legacy" : false,
                        "__v" : 0
                    }, 
                    {
                        "_id" : 4,
                        "created" : ISODate("2020-09-10T20:23:46.382Z"),
                        "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                        "active" : true,
                        "key" : "data",
                        "label" : "Data",
                        "displayOrder" : 3,
                        "legacy" : false,
                        "__v" : 0
                    }
                ],
                "updated" : ISODate("2020-09-10T20:23:46.382Z"),
                "active" : true,
                "displayOrder" : 2,
                "key" : "officer",
                "label" : "Officer",
                "required" : false,
                "type" : "select",
                "__v" : 0
            }
        }
    ]
}

我的问题是,如果我向'fields'集合中添加某些内容(我们将其称为属性'foo'),则现有的票证对象没有'foo'引用,因此它不会显示在票证对象中,我想以某种方式(在聚合框架中)获取当前字段的列表,并为每个字段查找对应的值(如果存在),否则返回一个空值,如下所示:

新票务结果

{
    _id: 2,
    fields: [
        ... //existing fields
        {
            key: "foo", // <-- new fields / fields not listed on the ticket
            value: null,
            def: {
                _id: 1,
                created: ISODate("2020-09-10T20:23:46.382Z"),
                options: [],
                updated: ISODate("2020-09-10T20:23:46.382Z"),
                active: true,
                displayOrder: 0,
                key: "foo",
                label: "Foo",
                required: false,
                type: "text",
                __v: 0,
            },
        },
    ],
}

如何实现?另外,有没有比我现有的方法更好的方法了?我是聚合技术的新手,还在尝试。

1 个答案:

答案 0 :(得分:1)

合并这两个字段是一个非常漫长的过程,您可以按照以下步骤操作,

  • $match您的情况
  • $addFieldskey数组中添加字段keys的数组
  • $facet生成2个单独的数组,一个用于匹配的字段,第二个用于不匹配的数组
    • fields中创建匹配字段的数组,在let和match keys查询和查找中使用$in集合传递fieldoptions数组,
      • $project使用$map$reduce获取具有当前根键和值的组合对象
    • other_fields中创建不匹配字段的数组,在let和match keys$not中传递$in数组意味着不包含fieldoptions集合并进行查找
      • $project使用$map$mergeObjects获取具有当前根键和值的组合对象
  • $project使用fields合并$concatArrays数组中的两个数组
  • $unwind解构fields数组
  • $replaceWith fields对象替换为根
  • $unwind解构fields数组
  • $group按票证ID并构造fields数组
db.ticket.aggregate([
  { $match: { _id: 1 } },
  { $addFields: { keys: { $map: { input: "$fields", in: "$$this.key" } } } },
  {
    $facet: {
      fields: [
        {
          $lookup: {
            from: "fields",
            let: { key: "$keys" },
            as: "_fields",
            pipeline: [
              { $match: { $expr: { $in: ["$key", "$$key"] } } },
              {
                $lookup: {
                  from: "fieldoptions",
                  localField: "options",
                  foreignField: "_id",
                  as: "options"
                }
              }
            ]
          }
        },
        {
          $project: {
            fields: {
              $map: {
                input: "$fields",
                as: "f",
                in: {
                  $mergeObjects: [
                    "$$f",
                    {
                      def: {
                        $reduce: {
                          input: "$_fields",
                          initialValue: {},
                          in: {
                            $cond: [{ $eq: ["$$f.key", "$$this.key"] }, "$$this", "$$value"]
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      ],
      other_fields: [
        {
          $lookup: {
            from: "fields",
            let: { key: "$keys" },
            as: "_fields",
            pipeline: [
              { $match: { $expr: { $not: { $in: ["$key", "$$key"] } } } },
              {
                $lookup: {
                  from: "fieldoptions",
                  localField: "options",
                  foreignField: "_id",
                  as: "options"
                }
              }
            ]
          }
        },
        {
          $project: {
            fields: {
              $map: {
                input: "$_fields",
                as: "f",
                in: {
                  $mergeObjects: [
                    { def: "$$f" },
                    { key: "$$f.key", value: null }
                  ]
                }
              }
            }
          }
        }
      ]
    }
  },
  { $project: { fields: { $concatArrays: ["$fields", "$other_fields"] } } },
  { $unwind: "$fields" },
  { $replaceWith: "$fields" },
  {
    $unwind: {
      path: "$fields",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $group: {
      _id: "$_id",
      fields: { $push: "$$ROOT.fields" }
    }
  }
])

Playground


您正在查询的查询,这是该查询Playground的优化版本

相关问题