Решил поупражняться с парсерами и работой с 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 есть две разных цены.
Для питона, думаю, сможете сделать по аналогии.
Комментариев нет:
Отправить комментарий