Найдите повторяющиеся значения в массиве и суммируйте их агрегацией MongoDB без использования $unwind и $group

#node.js #mongodb #mongodb-query #aggregation-framework #spring-data-mongodb

Вопрос:

Это мой документ MongoDB:

 {
"name": ClassA1
"data": [
        {
  "first": John,
  "second": David"
  "age": 21,
  "score": 1
   },
    {
  "first": John,
  "second": David"
  "age": 21,
  "score": 1
   },
   {
  "first": John,
  "second": David"
  "age": 22,
  "score": 1
   }
 ]

}

 

Чего я пытаюсь здесь добиться, так это того, что я хочу выяснить, есть ли повторяющийся «возраст» (значение 21) в массиве данных, я хочу суммировать только поле оценки и скопировать другой объект, но без использования $unwind и $group в агрегации MongoDB.
другие поля, такие как первое, второе, могут быть скопированы как есть, и в моем случае они всегда будут одинаковыми, единственными разными полями будут возраст и оценка. я хочу сравнить повторяющийся возраст и суммировать баллы, в этом случае мой результат должен быть:

 {
"name": ClassA1
"data": [
        {
  "first": John,
  "second": David"
  "age": 21,
  "score": 2
   },
   {
  "first": John,
  "second": David"
  "age": 22,
  "score": 1
   }
 ]

}

 

Как вы можете видеть, оценка первого элемента теперь равна 2, которая добавляется из предыдущего.
Я надеюсь, вы понимаете.
Детская площадка: https://mongoplayground.net/p/nyXUMEivMIt
Итак, вот что я попробовал:

 db.collection.aggregate([
  {
    $addFields: {
      values: {
        $reduce: {
          input: "$array",
          initialValue: [],
          in: {
            $concatArrays: [
              "$value",
              {
                $cond: [
                  {
                    $in: [
                      "$this.age",
                      "$value.age"
                    ]
                  },
                  [
                    {
                      "$sum": {
                        "$add": [
                          "$this.score",
                          "$value.score"
                        ]
                      }
                    }
                  ],
                  [
                    "$this"
                  ]
                ]
              }
            ]
          }
        }
      }
    }
  }
])

 

это ошибка, которую я получаю:

сбой запроса: (Location16554) Ошибка PlanExecutor во время агрегирования :: вызвано :: $add поддерживает только числовые типы или типы дат, а не массив

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

1. Пожалуйста, покажите нам, что вы пробовали до сих пор и где вы застряли? Вы не можете просто ожидать, что мы напишем код для вас на StackOverflow !

2. Извините, пожалуйста, проверьте ответ ниже, я опубликовал то, что я пробовал, и самое близкое, что я сейчас нахожусь. @h-сифат

3. Детская площадка: mongoplayground.net/p/nyXUMEivMIt

Ответ №1:

Вот мое решение для этого. Я просто использовал JavaScript, чтобы решить эту проблему. Здесь мы помещаем первый элемент с новым возрастом на карту, а затем, если мы найдем другого человека того же возраста, мы добавим оценку к предыдущему объекту person и сделаем текущий неопределенным. После итерации по этому массиву мы фильтруем весь неопределенный элемент и возвращаем его.

Редактировать:

Если вы хотите запустить это на игровой площадке mongo, вам нужно преобразовать тело функции в однострочную строку. Попробуйте это здесь: пример игровой площадки Mongo

 PipeLine = [
  {
    $match: {
      class: "ClassA1",
    },
  },
  {
    $project: {
      _id: 0,
      class: 1,
      data: {
        $function: {
          body: function (data) {
            const map = {};
            data = data.map((item) => {
              if (map[item.age]) map[item.age].score  = item.score;
              else {
                map[item.age] = item;
                return item;
              }
            });
            return data.filter((item) => item !== undefined);
          },
          args: ["$data"],
          lang: "js",
        },
      },
    },
  },
];
 

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

1. есть ли лучшее решение, чем функция/

2. Разве мы не можем использовать в нем карту или сокращение? функции требуют оперативной памяти для больших данных.

3. Я не знаю, может быть. Алгоритм, который я нашел, что сначала нам нужно найти objects в data с равным возрастом. Если вы хотите сделать это с помощью традиционного цикла for, вам придется пройти весь data массив для каждого отдельного элемента, и это будет O(n^2) сложно по времени, но это правда, что это займет O(1) место в памяти. И я не вижу здесь большого потребления памяти, потому что мы храним только object reference в этом map объекте. Это не значит, что мы глубоко клонируем все предметы и делаем их копии. Если потребление памяти превышает 100mb лимит, используйте эту allowDiskuse опцию.

4. Я думаю, что мог бы лучше объяснить здесь: mongodb.com/community/forums/t/…

5. нет решения без ветра лол mongodb.com/community/forums/t/…