#mongodb #aggregation_framework #mongodb_query
У меня следующие документы в коллекции
{
"_id": 1,
"values":[
{
"value1":10,
"value2":12,
"value3":30
},
{
"value1":20,
"value2":12,
"value3":100
},
{
"value1":30,
"value2":14,
"value3":50
}
]
},
{
"_id": 2,
"values":[
{
"value1":10,
"value2":12,
"value3":60
},
{
"value1":20,
"value2":12,
"value3":80
},
{
"value1":30,
"value2":14,
"value3":70
}
]
}
Мне нужно оставить один документ с одним элементом в массиве по следующим критериям:
values.value1 = 20
values.value2 = 12
values.value3 // максимальное значение из всех значений values.value3 во всех документах
Например, по вышеуказанным критериям должен получиться такой документ:
{
"_id": 1,
"values":[
{
"value1":20,
"value2":12,
"value3":100
}
]
}
Также, подойдет решение, просто сортирующие документы и элементы по указанным критериям.
Чтобы первым документом был тот, в котором значение values.value3 максимально из возможных
и первым элементом в массиве был объект с максимальным значением. Например:
{
"_id": 1,
"values":[
{
"value1":20,
"value2":12,
"value3":100
},
{
elements...
}
]
},
{
"_id": 2,
"values":[
{
elements...
}
]
}
Сделать это у меня получается, но я использую $match, $unwind, $group, $redact, $sort
по несколько раз. Может быть кто-то предложит более правильную реализацию.
Ответы
Ответ 1
Лучший способ это запустить два запроса. Первый, чтобы нашел максимальное значение и второй, чтобы остался только документ, где value3 равно этому значению. Решение для MongoDB версия 3.2 или новее: Начиная с версии 3.2 можно использовать оператор $max, который возвращает максимальное значение для каждого документа в $project этапе. Оператор $map здесь возвращает массив value3. Следующий этап в цепочке — это $group, где нужно группировать документы и возвращать максимальное значение. var result = db.collection.aggregate([ { "$project": { "maxVal": { "$max": { "$map": { "input": "$values", "as": "value", "in": "$$value.value3" } } } } }, { "$group": { "_id": null, "mval": { "$max": "$maxVal" } } } ]); Поскольку метод .aggregate() возвращает курсор, нужно использовать метод toArray() и оператор [] для получения значения. var maximumValue = result.toArray()[0]['mval']; Теперь можно использовать это значение, чтобы оставить в массиве только элемент с максимальным значением. Для этого нужно использовать оператор $filter db.collection.aggregate( [ { "$match": { "values.value3": maximumValue } }, { "$project": { "values": { "$filter": { "input": "$values", "as": "value", "cond": { "$eq": [ "$$value.value3", maximumValue ] } } } } } ]) Решение для MongoDB версии 2.6 или новее: Решение для версий с 2.6 по 3.2 менее эффективно. Сначала в первом запросе нужно денормализовать массив value3 сразу после $project, используя оператор $unwind. Последний этап в цепочке это $group, где группируем документы и возвращаем максимальное значение value3 var result = db.collection.aggregate([ { "$project": { "value3": { "$map": { "input": "$values", "as": "value", "in": "$$value.value3" } } } }, { "$unwind": "$value3" }, { "$group": { "_id": null, "mval": { "$max": "$value3" } } } ]); var maximumValue = result.toArray()[0]['mval']; Чтобы оставить в массиве только элемент с максимальным значением, у нас есть два варианта: Используя оператор $redact db.collection.aggregate( [ { "$match": { "values.value3": maximumValue } }, { "$redact": { "$cond": [ { "$or": [ { "$eq": [ "$value3", maximumValue ] }, { "$not": "$value3" } ]}, "$$DESCEND", "$$PRUNE" ] }} ]) Используя оператор $project и $setDifference db.collection.aggregate( [ { "$match": { "values.value3": maximumValue } }, { "$project": { "values": { "$setDifference": [ { "$map": { "input": "$values", "as": "value", "in": { "$cond": [ { "$eq": [ "$$value.value3", maximumValue ] }, "$$value", false ] } }}, [false] ] } }} ])
Комментариев нет:
Отправить комментарий