Страницы

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

среда, 10 июля 2019 г.

Mongodb aggregate (агрегация/группировка товаров)

Решил поупражняться с парсерами и работой с mongodb, но столкнулся со следующей проблемой.
Имеется коллекция с товарами. Для примера возьмем следующие документы из нее:
{title: 'acer aspire 6420', source: 'amazon', price: 300} {title: 'acer aspire 6420', source: 'ebay', price: 320}
Из них необходимо получить следующий документ:
{title: 'acer aspire 6420', amazon: 300, ebay: 320}
то есть сгруппировать по названию и вывести цену в каждом из магазинов.
В ходе разбирательства написал следующий код на питоне:
from bson.code import Code
reducer = Code(""" function(origin, res){ res[origin.source] = origin.price } """)
db.products.group(key={"source":1, 'title': 1, 'price': 1}, condition={'title': 'acer aspire 6420'}, initial={}, reduce=reducer)
но по итогу получаю вот такой результат:
{title: 'acer aspire 6420', source: 'amazon', price: 300, amazon: 300, ebay: 320} {title: 'acer aspire 6420', source: 'ebay', price: 320, amazon: 300, ebay: 320}
Подскажите, пожалуйста, в какую сторону дальше копать или как это реализовать, чтобы результат получился таким:
{title: 'acer aspire 6420', amazon: 300, ebay: 320}
Я гуглил и читал документацию, правда. Просто не очень понимаю как это все правильно связать вместе.


Ответ

Вариант с aggregation framework (для mongoshell):
db.products.aggregate([ {$match: {title: "acer aspire 6420"}}, {$project: {_id: 0, title: 1, price: {source: "$source", value: "$price"} }}, {$group: {_id: "$title", prices: {$push: "$price"}}} ])
Что дает на выходе:
{ "_id" : "acer aspire 6420", "prices" : [ { "source" : "amazon", "value" : 300 }, { "source" : "ebay", "value" : 320 } ] }
Первая операция фильтрует по названию (title) товара, следующая формирует поле price в виде объекта с двумя полями - source и value, и последняя операция группирует по title, добавляя все варианты цен в создаваемое поле-массив prices
Т.к. у вас по условию задачи source должен быть именем поля в результате, а пока в mongodb aggregation framework поддержки динамических имен для создаваемых полей нет (https://jira.mongodb.org/browse/SERVER-5947), то можно пройтись по результату с помощью map
db.products.aggregate([ {$match: {title: "acer aspire 6420"}}, {$project: {_id: 0, title: 1, price: {source: "$source", value: "$price"} }}, {$group: {_id: "$title", prices: {$push: "$price"}}} ]).map(function(e) { var r = {} r.title = e._id; e.prices.forEach(function(i){r[i.source] = i.value}); return r; })
Что даст на выходе:
[ { "title" : "acer aspire 6420", "amazon" : 300, "ebay" : 320 } ]
В этом случае есть вероятность (в отличии от первого варианта) потерять информацию, если у вас, например, для amazon есть две разных цены.
Для питона, думаю, сможете сделать по аналогии.

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

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