Страницы

Поиск по вопросам

вторник, 16 октября 2018 г.

Как оставить в массиве элемент с максимальным значением

У меня следующие документы в коллекции
{ "_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 по несколько раз. Может быть кто-то предложит более правильную реализацию.


Ответ

Лучший способ это запустить два запроса. Первый, чтобы нашел максимальное значение и второй, чтобы остался только документ, где 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] ] } }} ])

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

Отправить комментарий