#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/…