MongoDB — Структурируйте массив без использования ключевого поля в объединении

#javascript #arrays #mongodb #aggregation-framework

Вопрос:

У меня возникла проблема с созданием count элементов, возвращаемых из массива, без учета или использования этих полей в моем объединении.

Структура данных выглядит следующим образом:

 [
  {
    "_id": "1",
    "title": "Vanella Icream",
    "contain": "sugar",
    "details": [
      {
        "flavour": "Vanella"
      },
      {
        "weight": "10KG"
      },
      {
        "sugar": "15KG"
      }
    ]
  },
  {
    "_id": "2",
    "title": "Pretzels",
    "contain": "salt",
    "details": [
      {
        "flavour": "Wheat"
      },
      {
        "weight": "10KG"
      },
      {
        "sugar": "15KG"
      }
    ]
  },
  {
    "_id": "3",
    "title": "Rasmalai Icream",
    "contain": "sugar",
    "details": [
      {
        "flavour": "Vanella"
      },
      {
        "weight": "15KG"
      },
      {
        "sugar": "12KG"
      }
    ]
  },
  {
    "_id": "4",
    "title": "Vanella Icream",
    "contain": "sugar",
    "details": [
      {
        "flavour": "Vanella"
      },
      {
        "weight": "15KG"
      },
      {
        "sugar": "12KG"
      }
    ]
  }
]
 

Вывод, который я хочу:

 [
  {
    "details": {
      "flavour": {
        "Vanella": 3, //Number of times Vanella present in each document.
        "Wheat": 1,
      },
      "weight": {
        "10KG": 2,
        "15KG": 2
      },
      "sugar": {
        "12KG": 2,
        "15KG": 2
      }
    }
  }
]
 

Запрос:

 db.collection.aggregate([
  {
    "$unwind": {
      "path": "$details"
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {
        "$mergeObjects": [
          "$details",
          "$ROOT"
        ]
      }
    }
  },
  {
    "$facet": {
      "flavour": [
        {
          "$group": {
            "_id": "$flavour",
            "sum": {
              "$sum": 1
            }
          }
        },
        {
          "$addFields": {
            "flavour": "$_id"
          }
        },
        {
          "$project": {
            "_id": 0
          }
        }
      ],
      "weight": [
        {
          "$group": {
            "_id": "$weight",
            "sum": {
              "$sum": 1
            }
          }
        },
        {
          "$addFields": {
            "weight": "$_id"
          }
        },
        {
          "$project": {
            "_id": 0
          }
        }
      ]
    }
  },
  {
    "$addFields": {
      "flavour": {
        "$reduce": {
          "input": {
            "$filter": {
              "input": {
                "$map": {
                  "input": "$flavour",
                  "as": "w",
                  "in": {
                    "$cond": [
                      {
                        "$ne": [
                          "$w.flavour",
                          null
                        ]
                      },
                      {
                        "$let": {
                          "vars": {
                            "o": [
                              [
                                "$w.flavour",
                                "$w.sum"
                              ]
                            ]
                          },
                          "in": {
                            "$arrayToObject": "$o"
                          }
                        }
                      },
                      null
                    ]
                  }
                }
              },
              "as": "f",
              "cond": {
                "$ne": [
                  "$f",
                  null
                ]
              }
            }
          },
          "initialValue": {},
          "in": {
            "$let": {
              "vars": {
                "d": "$value",
                "p": "$this"
              },
              "in": {
                "$mergeObjects": [
                  "$d",
                  "$p"
                ]
              }
            }
          }
        }
      },
      "weight": {
        "$reduce": {
          "input": {
            "$filter": {
              "input": {
                "$map": {
                  "input": "$weight",
                  "as": "w",
                  "in": {
                    "$cond": [
                      {
                        "$ne": [
                          "$w.weight",
                          null
                        ]
                      },
                      {
                        "$let": {
                          "vars": {
                            "o": [
                              [
                                "$w.weight",
                                "$w.sum"
                              ]
                            ]
                          },
                          "in": {
                            "$arrayToObject": "$o"
                          }
                        }
                      },
                      null
                    ]
                  }
                }
              },
              "as": "f",
              "cond": {
                "$ne": [
                  "$f",
                  null
                ]
              }
            }
          },
          "initialValue": {},
          "in": {
            "$let": {
              "vars": {
                "d": "$value",
                "p": "$this"
              },
              "in": {
                "$mergeObjects": [
                  "$d",
                  "$p"
                ]
              }
            }
          }
        }
      }
    }
  },
  {
    "$project": {
      "details": "$ROOT"
    }
  }
])
 

Здесь я пытаюсь получить flavour и weight с их количеством, вручную добавляя эти поля на $filter этапе. Я хочу сделать это, не принимая на себя эти ключи. Таким образом, даже если в массиве присутствует 20 элементов details , он сопоставит эти элементы и покажет мне вывод с их количеством соответственно.

Я надеюсь, вы, ребята, понимаете.

Игровая площадка:https://mongoplayground.net/p/j1mzgWvcmvd


Ответ №1:

Я парень с форума Mongodb:

Попробуйте это https://mongoplayground.net/p/tfyfpIkHilQ

Комментарии:

1. Если это вас устраивает, попробуйте прочитать и понять конвейер. Если у вас есть вопросы, просто задавайте 🙂

2. Да, я это понимаю. Я скучаю count по группе «В». Отличная работа!! 🙂

Ответ №2:

Вам нужно изменить схему, то, что вы хотите сделать, легко, и оба эти запроса настолько сложны и медленны, что даже второй, который намного меньше, имеет 2 $unwind и 3 $group с 3 $arrayToObject и 8 этапами в общей сложности из-за схемы и схемы ответа.

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

Ваша схема должна быть примерно такой

 {
    "_id": "2",
    "title": "Pretzels",
    "contain": "salt",
    "details": [
      {
        "type" : "flavour",
        "value" : "Wheat"
      },
      {
        "type" : "weight",
        "value" : "10KG"
      },
      {
        "type" : "sugar",
        "value" : "15KG"
      }
    ]
  }
 

Смотрите этот пример

Преобразует вашу схему в новую схему и выдает нужные вам результаты, но без данных в ключах (первая часть вам бы не понадобилась, вам понадобился бы только приведенный ниже запрос, если бы у вас была эта схема с самого начала)

Запрос с новой схемой (без данных в ключах)

 [{"$unwind": { "path": "$details"}},
  {"$replaceRoot": {"newRoot": "$details"}},
  {
    "$group": {
      "_id": {
        "type": "$type",
        "value": "$value"
      },
      "sum": {"$sum": 1}
    }
  },
  {
    "$replaceRoot": {
      "newRoot": {"$mergeObjects": ["$_id","$ROOT"]}
    }
  },
  {"$project": {"_id": 0}},
  {
    "$group": {
      "_id": "$type",
      "values": {
        "$push": {
          "value": "$value",
          "sum": "$sum"
        }
      }
    }
  },
  {"$addFields": {"type": "$_id"}},
  {"$project": {"_id": 0}}
]
 

Операторы MongoDB не предназначены для поддержки данных в ключах или динамических ключах(ключи uknown) (для этого вы делаете сложные вещи, подобные описанным выше).

Если вы хотите изменить свою схему, либо сделайте это с помощью обновления в базе данных, либо перенесите документы в приложение и сделайте это с помощью javascript, а затем вставьте заново.

Даже если вы решите этот вопрос в следующем, у вас снова возникнут проблемы.