如何从mongodb聚合的查找阶段嵌入let变量的值?

问题描述 投票:0回答:1

我创建了一个 mongoplayground 以使其更容易理解。

https://mongoplayground.net/p/wrTinwpfOBI

首先。我有什么?

我的类别架构包含引用过滤器架构实体的 id。

在过滤器架构中,我有一个名为“targetProperty”的属性,其中包含描述产品详细信息的属性名称。产品模式有一个属性详细信息,它是键值映射,其中前面提到的“targetProperty”的值是该详细信息映射的键。产品内的变体实体也可能包含相同的详细信息键值映射属性。

例如,如果过滤器实体的“targetProperty”是“memory”,则存在一个包含“details”属性的产品实体,该属性包含“memory”作为其键和对象{“title”:“Memory”,“value”: “256 GB”}作为其值或某些产品变体可能在其详细信息键值映射中包含“内存”属性。

const categorySchema = new Schema<ICategory>({    
  filters: [{ type: Schema.Types.ObjectId, ref: filterModelName }],
  // some other properties unrelated to the issue...
});

const filterSchema = new Schema<IFilter>({  
  targetProperty: {
    type: String,
    required: true
  } 
  // some other properties...
});

const productSchema = new Schema<IProduct>({          
  details: {
    type: Map,
    of: {
      title: { type: String, required: true },
      value: { type: Schema.Types.Mixed, required: true }
    },
    required: true
  },
  variations: [variationSchema],  
  categories: [Schema.Types.ObjectId],
  // some other properties...
});

const variationSchema = new Schema<IProductVariation>({
  details: {
    type: Map,
    of: {
      title: { type: String, required: true },
      value: { type: Schema.Types.Mixed, required: true }
    }
  }
  // some other properties...
});

第二。我的目标是什么?

在聚合的帮助下,我想收集某个类别的相关过滤器的所有可能选项。例如,如果 iPhone 类别包含内存过滤器,我想迭代包含 iPhone 类别的所有产品并收集内存值,例如“128gb”、“256 GB”等,它们包含在详细信息映射属性的“内存”键下作为 iPhone 类别内存过滤器的可能选项。

我的聚合管道:

db.categories.aggregate([
  {
    "$lookup": {
      "from": "filters",
      "localField": "filters",
      "foreignField": "_id",
      "as": "filters",
      "let": {
        "category_id": "$_id"
      },
      "pipeline": [
        {
          "$lookup": {
            "from": "products",
            "let": {
              "target_property": "$targetProperty"
            },
            "as": "options",
            "pipeline": [
              {
                "$match": {
                  "$and": [
                    {
                      "$expr": {
                        "$in": [
                          "$$category_id",
                          "$categories"
                        ]
                      }
                    },
                    {
                      "$expr": {
                        "$or": [
                          {
                            "$ne": [
                              {
                                // ######## SOURCE OF THE PROBLEM ########
                                "$getField": "details.$$target_property"
                              },
                              null
                            ]
                          },
                          {
                            "$ne": [
                              {
                                // ######## SOURCE OF THE PROBLEM ########
                                "$getField": "variations.details.$$target_property"
                              },
                              null
                            ]
                          }
                        ]
                      }
                    }
                  ]
                }
              },
              {
                "$unwind": {
                  "path": "$variations",
                  "preserveNullAndEmptyArrays": true
                }
              },
              {
                "$group": {
                  "_id": {
                    "$ifNull": [
                      {
                        // ######## SOURCE OF THE PROBLEM ########
                        "$getField": "details.$$target_property.value"
                      },
                      {
                        // ######## SOURCE OF THE PROBLEM ########
                        "$getField": "variations.details.$$target_property.value"
                      }
                    ]
                  }
                }
              },
              {
                "$group": {
                  "_id": null,
                  "options": {
                    "$push": "$_id"
                  }
                }
              },
              {
                "$project": {
                  "_id": 0
                }
              }
            ]
          }
        }
      ]
    }
  }
])

第三。我偶然发现的问题。

请注意上面聚合查询的 // ######## 问题根源 ######## 注释行下的行。似乎 $lookup 聚合阶段“let”变量的值无法按照我尝试实现它的方式嵌入。我的问题是如何正确嵌入 let 变量的值,以便我能够动态访问详细信息属性的嵌套属性?如果这可能的话...

感谢您的关注!

mongodb mongoose mongodb-query aggregation-framework
1个回答
0
投票

感谢 @cmgchess 在我的问题的评论部分中给出的 $objectToArray 提示,我成功地构建了我需要的聚合管道。最终的聚合管道看起来超级麻烦且丑陋,但最重要的是它正在工作。如果您能够提出更优雅的解决方案,我会将其标记为问题。

这是管道:

db.categories.aggregate([
  $lookup: {
  {
    from: "productfilters",
    localField: "filters",
    foreignField: "_id",
    as: "filters",
    let: {
      category_id: "$_id",
    },
    pipeline: [
      {
        $lookup: {
          from: "products",
          let: {
            target_property: "$targetProperty",
          },
          as: "options",
          pipeline: [
            {
              $match: {
                $expr: {
                  $in: [
                    "$$category_id",
                    "$categories",
                  ],
                },
              },
            },
            {
              $unwind: {
                path: "$variations",
                preserveNullAndEmptyArrays: true,
              },
            },
            {
              $match: {
                $expr: {
                  $or: [
                    {
                      $gt: [
                        {
                          $size: {
                            $filter: {
                              input: {
                                $objectToArray:
                                  "$details",
                              },
                              cond: {
                                $eq: [
                                  "$$this.k",
                                  "$$target_property",
                                ],
                              },
                            },
                          },
                        },
                        0,
                      ],
                    },
                    {
                      $gt: [
                        {
                          $size: {
                            $let: {
                              vars: {
                                arr: {
                                  $filter: {
                                    input: {
                                      $objectToArray:
                                        "$variations.details",
                                    },
                                    cond: {
                                      $eq: [
                                        "$$this.k",
                                        "$$target_property",
                                      ],
                                    },
                                  },
                                },
                              },
                              in: {
                                $cond: {
                                  if: {
                                    $isArray: [
                                      "$$arr",
                                    ],
                                  },
                                  then: "$$arr",
                                  else: [],
                                },
                              },
                            },
                          },
                        },
                        0,
                      ],
                    },
                  ],
                },
              },
            },
            {
              $replaceRoot: {
                newRoot: {
                  $let: {
                    vars: {
                      detailsArr: {
                        $filter: {
                          input: {
                            $objectToArray:
                              "$details",
                          },
                          cond: {
                            $eq: [
                              "$$this.k",
                              "$$target_property",
                            ],
                          },
                        },
                      },
                      variationsArr: {
                        $filter: {
                          input: {
                            $ifNull: [
                              {
                                $objectToArray:
                                  "$variations.details",
                              },
                              [],
                            ],
                          },
                          cond: {
                            $eq: [
                              "$$this.k",
                              "$$target_property",
                            ],
                          },
                        },
                      },
                    },
                    in: {
                      $cond: {
                        if: {
                          $gt: [
                            {
                              $size: "$$detailsArr",
                            },
                            0,
                          ],
                        },
                        then: {
                          $getField: {
                            field: "v",
                            input: {
                              $arrayElemAt: [
                                "$$detailsArr",
                                0,
                              ],
                            },
                          },
                        },
                        else: {
                          $cond: {
                            if: {
                              $gt: [
                                {
                                  $size:
                                    "$$variationsArr",
                                },
                                0,
                              ],
                            },
                            then: {
                              $getField: {
                                field: "v",
                                input: {
                                  $arrayElemAt: [
                                    "$$variationsArr",
                                    0,
                                  ],
                                },
                              },
                            },  
                            else: null,
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
            {
              $group: {
                _id: "$value",
              },
            },
            {
              $sort: {
              _id: 1,
              },
            },
          ],
        },
      },
      {
        $set: {
          options: {
            $map: {
              input: "$options",
              in: {
                $getField: {
                  field: "_id",
                  input: "$$this",
                },
              },
            },
          },
        },
      },
    ],
  }
  }
])

© www.soinside.com 2019 - 2024. All rights reserved.