You необходимо изменить структуру своих документов, обновив их. Вам понадобится цикл через каждый документ, используя метод .forEach
, а затем $unset
имя всего поля, начинающееся с product_
. Оттуда вам нужно будет добавить новые поля products
, который является массивом продуктов, используя оператор обновления $set
. Это, как говорится, вы должны использовать "bulk" операции, чтобы обновить документы для достижения максимальной эффективности
var bulkOp = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.find().forEach(function(doc) {
var allproducts = [];
for(var key in doc) {
if(Object.prototype.hasOwnProperty.call(doc, key) && /^product_\d+/.test(key)) {
var product = {};
product["name"] = key;
product["orderCancelled"] = doc[key]["orderCancelled"];
product["orderDelivered"] = doc[key]["orderDelivered"];
allproducts.push(product);
var unsetField = {};
unsetField[key] = "";
bulkOp.find({"_id": doc._id}).update({ "$unset": unsetField });
};
count++;
};
bulkOp.find({"_id": doc._id}).update({
"$set": { "products": allproducts }
});
count++;
if(count % 500 === 0) {
// Execute per 500 operations and re-init
bulkOp.execute();
bulkOp = db.collection.initializeOrderedBulkOp();
}
})
// clean up queues
if(count > 0) {
bulkOp.execute();
}
Ваши документы теперь будут выглядеть следующим образом:
{
"_id" : NumberLong(542),
"products" : [
{
"name" : "product_1",
"orderCancelled" : 0,
"orderDelivered" : 6
},
{
"name" : "product_2",
"orderCancelled" : 3,
"orderDelivered" : 16
},
{
"name" : "product_3",
"orderCancelled" : 5,
"orderDelivered" : 11
}
]
}
Затем приходит ваш агрегирования запрос с использованием метода .aggregate()
:
db.collection.aggregate([
{ "$match": { "_id": 542 } },
{ "$unwind": "$products" },
{ "$group": {
"_id": "$_id",
"maxOrderCancelled": { "$max": "$products.orderCancelled"},
"minOrderDelivvered": { "$min": "$products.orderDelivered"}
}}
])
Какие возвращает:
{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivvered" : 6 }
От version 3.2 вы можете использовать $max
и $min
в вашем $project
этапе, который является гораздо более лучшим способом сделать это, потому что нет необходимости в $unwind
массив сначала.
db.collection.aggregate([
{ "$match": { "_id": 542 } },
{ "$project": {
"maxOrderCancelled": {
"$max": {
"$map": {
"input": "$products",
"as": "order",
"in": "$$orc.orderCancelled"
}
}
},
"minOrderDelivered": {
"$min": {
"$map": {
"input": "$products",
"as": "orc",
"in": "$$orc.orderDelivered"
}
}
}
}}
])
Что дает:
{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivered" : 6 }
Почему вы не положить ваши продукты в массив? –
@ ДмитроШевченко, я тебя не понял. Фактически в другом случае я также могу указать счетчик 'orderDelivered' на' product name'. Ну, какой будет дизайн в вашем случае? – Shams
Пожалуйста, взгляните на пример структуры данных в моем ответе. Не могли бы вы рассказать, что вы подразумеваете под подсчетом 'orderDelivered'' by product name? –