#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] ] } }} ])
Комментариев нет:
Отправить комментарий