2015-10-22 2 views
5

У меня есть запрос на получение minimum query и maximum query на приведенном ниже образце данных. В полях моего случая имена динамичны, как показано ниже product_1, product_2 ...MongoDB: поиск минимального, максимального значения в вложенном объекте с именем динамического поля

{ 
    "_id" : NumberLong(540), 
    "product_1" : { 
      "orderCancelled" : 0, 
      "orderDelivered" : 6 
    }, 
    "product_2" : { 
      "orderCancelled" : 3, 
      "orderDelivered" : 16 
    }, 
    "product_3" : { 
      "orderCancelled" : 5, 
      "orderDelivered" : 11 
    } 
} 

Я не получаю представление о том, как я могу сделать это в Монго, где имена полей являются динамическими, значит, в будущем может быть и другими продукты также создаются как product_4 и product_5 для тех же id.

мне нужен запрос, который дает мне минимальное значение для orderDelivered и максимальное значение для orderCancelled, как, например, в результате выше документа будет orderDelivered:16 & orderCancelled:0.
Спасибо за любую идею.

+0

Почему вы не положить ваши продукты в массив? –

+0

@ ДмитроШевченко, я тебя не понял. Фактически в другом случае я также могу указать счетчик 'orderDelivered' на' product name'. Ну, какой будет дизайн в вашем случае? – Shams

+0

Пожалуйста, взгляните на пример структуры данных в моем ответе. Не могли бы вы рассказать, что вы подразумеваете под подсчетом 'orderDelivered'' by product name? –

ответ

1

Вы должны перестроить свой документ таким образом, чтобы все документы продукта в массиве:

{ 
    "_id": NumberLong(540), 
    products: [ 
     { 
      "name": "product_1", 
      "orderCancelled": 0, 
      "orderDelivered": 6 
     }, 
     { 
      "name": "product_2", 
      "orderCancelled": 3, 
      "orderDelivered": 16 
     }, 
     { 
      "name": "product_3", 
      "orderCancelled": 5, 
      "orderDelivered": 11 
     } 
    ] 
} 

Тогда вы будете иметь возможность выпускать нормальные макс/мин запросов, как это:

db.test.aggregate([ 
    { 
     $match: { "_id" : NumberLong(540) } 
    }, 
    { 
     $unwind: "$products" 
    }, 
    { 
     $group: { 
      _id: "$_id", 
      minDelivered: { $min: "$products.orderDelivered" }, 
      maxCancelled: { $max: "$products.orderCancelled" } 
     } 
    } 
]) 
+0

как насчет того, как изменить структуру документов? – styvane

+2

@ user3100115 существующие данные необходимо изменить с помощью отдельного инструмента. Я считаю, что обсуждение такого инструмента не будет обсуждаться здесь. –

+0

Нет, вы можете сделать это с обновлением – styvane

2

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 } 
Смежные вопросы