Конвейер агрегации для поиска и объединения вложенных документов - PullRequest
1 голос
/ 04 августа 2020

Я борюсь с написанием конвейера агрегации для поиска вложенных документов по их _id и возврата указанного c имени без перезаписи существующих ключей / значений в данных. Мне удалось сделать это для вложенного массива, но я не могу сделать это для массива, вложенного во вложенный массив.

Итак, я хочу найти _id каждого ингредиента и каждого субингредиента и объединить их с данными для этих ингредиентов, которые уже существуют (т. е. количество, измерение).

Вот что у меня есть на данный момент: https://mongoplayground.net/p/ft4oIMm_8wg

Коллекция продуктов:

[
    {
      "_id": {
        "$oid": "5ecf269bceb735416db0b329"
      },
      "id": 36,
      "title": "Product 1",
      "description": {
        "generalInformation": "Some information",
        "activeIngredients": [
          {
            "_id": 1636,
            "qty": 133.5,
            "measure": "µg",
            "subIngredient": [
              {
                "_id": 1626,
                "qty": 16.6,
                "measure": "µg"
              }
            ],
            
          },
          {
            "_id": 1234,
            "qty": 133.5,
            "measure": "µg",
            "subIngredient": [
              {
                "_id": 1122,
                "qty": 16.6,
                "measure": "µg"
              },
              {
                "_id": 1212,
                "qty": 16.6,
                "measure": "µg"
              }
            ],
            
          },
          
        ]
      },
      
    },
    {
      "_id": {
        "$oid": "5ecf269bceb735416db0b346"
      },
      "id": 36,
      "title": "Product 2",
      "description": {
        "generalInformation": "Some information",
        "activeIngredients": [
          {
            "_id": 1234,
            "qty": 133.5,
            "measure": "µg",
            "subIngredient": [
              {
                "_id": 1122,
                "qty": 16.6,
                "measure": "µg"
              }
            ],
            
          },
          {
            "_id": 1234,
            "qty": 133.5,
            "measure": "µg",
            "subIngredient": [
              {
                "_id": 1122,
                "qty": 16.6,
                "measure": "µg"
              },
              {
                "_id": 1212,
                "qty": 16.6,
                "measure": "µg"
              }
            ],
            
          },
          
        ]
      },
      
    }
  ]

Коллекция ингредиентов:

[
    {
      "_id": 1234,
      "name": "Ingredient 1",
      
    },
    {
      "_id": 1122,
      "name": "Ingredient 2",
      
    },
    {
      "_id": 1212,
      "name": "Ingredient 3",
      
    },
    {
      "_id": 1636,
      "name": "Ingredient 4",
      
    },
    {
      "_id": 1626,
      "name": "Ingredient 5",
      
    }
  ]

Что должно быть возвращено:

[
  {
    "_id": ObjectId("5ecf269bceb735416db0b329"),
    "title": "Product 1"
    "description": {
      "activeIngredients": {
        "_id": 1636,
        "measure": "µg",
        "name": "Ingredient 4",
        "qty": 133.5,
        "subIngredient": [
          {
            "_id": 1626,
            "measure": "µg",
            "qty": 16.6
          }
        ]
      },
      "generalInformation": "Some information"
    },
    "ingredients": [
      {
        "_id": 1636,
        "measure": "µg",
        "name": "Ingredient 4",
        "qty": 133.5,
        "subIngredient": [
          {
            "_id": 1626,
            "measure": "µg",
            "qty": 16.6,
            "name": "Ingredient 2"
          }
        ]
      },
      {
        "_id": 1234,
        "measure": "µg",
        "name": "Ingredient 1",
        "qty": 133.5,
        "subIngredient": [
          {
            "_id": 1122,
            "measure": "µg",
            "qty": 16.6,
            "name": "Ingredient 2"
          },
          {
            "_id": 1212,
            "measure": "µg",
            "qty": 16.6,
            "name": "Ingredient 2"
          }
        ]
      }
    ]
    
  },
  
]

Мой текущий конвейер:

[
  {
    "$unwind": {
      "path": "$description.activeIngredients",
      "preserveNullAndEmptyArrays": false
    }
  },
  {
    "$lookup": {
      "from": "ingredients",
      "localField": "description.activeIngredients._id",
      "foreignField": "_id",
      "as": "description.activeIngredients.name"
    }
  },
  {
    "$addFields": {
      "description.activeIngredients.name": {
        "$arrayElemAt": [
          "$description.activeIngredients.name.name",
          0
        ]
      }
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "ingredients": {
        "$push": "$description.activeIngredients"
      },
      "description": {
        "$first": "$description"
      },
      "title": {
        "$first": "$title"
      }
    }
  },
  {
    "$lookup": {
      "from": "ingredients",
      "localField": "ingredients.subIngredient._id",
      "foreignField": "_id",
      "as": "subIngredients"
    }
  }
]

Благодарю за любую помощь. Спасибо.

1 Ответ

1 голос
/ 04 августа 2020

Вы не за горами, и вы можете достичь этого результата несколькими способами, один из которых - просто $unwind массив subingredients, $lookup на нем и, наконец, добавить еще один $group этап для реструктуризации документ.

Как вы ясно показали, что знаете, как делать все это, я покажу другой способ, использующий такие операторы, как $ map , $ indexOfArray и Mon go s v3.6 $ lookup синтаксис.

Стратегия заключается в том, что вместо того, чтобы раскручивать подмассив, мы просто $lookup все соответствующие подкомпоненты, а затем «объединяем "два массива с использованием указанных мной операторов.

т.е. взяв:

[ {id: 5, name: "name"} ];
[ {id: 5, qty: 25} ]

И превратив их в:

[ {id: 5, name: "name", qty: 25} ]

Вот как мы это делаем:

db.products.aggregate([
  {
    "$unwind": {
      "path": "$description.activeIngredients",
      "preserveNullAndEmptyArrays": false
    }
  },
  {
    "$lookup": {
      "from": "ingredients",
      "localField": "description.activeIngredients._id",
      "foreignField": "_id",
      "as": "description.activeIngredients.name"
    }
  },
  {
    "$addFields": {
      "description.activeIngredients.name": {
        "$arrayElemAt": [
          "$description.activeIngredients.name.name",
          0
        ]
      }
    }
  },
  {
    "$lookup": {
      "from": "ingredients",
      "let": {
        sub: "$description.activeIngredients.subIngredient"
      },
      "pipeline": [
        {
          $match: {
            $expr: {
              "$setIsSubset": [
                [
                  "$_id"
                ],
                {
                  $map: {
                    input: "$$sub",
                    as: "datum",
                    in: "$$datum._id"
                  }
                }
              ]
            }
          }
        }
      ],
      "as": "subIngredients"
    }
  },
  {
    "$addFields": {
      "description.activeIngredients.subIngredient": {
        $map: {
          input: "$description.activeIngredients.subIngredient",
          as: "sub",
          in: {
            "$mergeObjects": [
              "$$sub",
              {
                name: {
                  $arrayElemAt: [
                    "$subIngredients.name",
                    {
                      "$indexOfArray": [
                        "$subIngredients._id",
                        "$$sub._id"
                      ]
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "ingredients": {
        "$push": "$description.activeIngredients"
      },
      "description": {
        "$first": "$description"
      },
      "title": {
        "$first": "$title"
      }
    }
  }
])

MongoPlayground

...