2015-10-22 16 views
5

Ho una query per ottenere minimum query e maximum query sotto il set di dati di esempio. Nei campi mio caso i nomi sono dinamici, come qui di seguito product_1, product_2 ...MongoDB: ricerca minima, massimo nell'oggetto nidificato con nome di campo dinamico

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

Non ricevo un'idea come posso farlo in Mongo, dove i nomi di campo sono dinamici, significa, in futuro ci possono essere altri i prodotti vengono creati anche come product_4 e product_5 per lo stesso id.

ho bisogno di una domanda che mi dà valore minimo per orderDelivered e il valore massimo per orderCancelled, come ad esempio al precedente risultato documento sarà orderDelivered:16 & orderCancelled:0.
Grazie per qualsiasi idea.

+0

Perché non mettere i vostri prodotti in un array? –

+0

@DmytroShevchenko non ti ho preso. In realtà in un altro caso posso anche dare il conteggio per "orderDelivered" per "nome prodotto". Bene, quale sarà il design nel tuo caso? – Shams

+0

Si prega di dare un'occhiata all'esempio della struttura dei dati nella mia risposta. Potresti descrivere cosa intendi per "orderDelivered" per "nome prodotto"? –

risposta

1

Si dovrebbe ristrutturare il vostro documento in modo che tutti i documenti prodotti sono in un array:

{ 
    "_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 
     } 
    ] 
} 

allora sarete in grado di emettere normali max/min query come questa:

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

cosa dire di come cambiare la struttura dei documenti? – styvane

+2

@ user3100115 i dati esistenti devono essere modificati utilizzando uno strumento separato. Credo che discutere di un tale strumento sarebbe fuori tema qui. –

+0

No puoi farlo con un aggiornamento – styvane

2

È è necessario modificare la struttura dei documenti aggiornandoli. È necessario eseguire il ciclo di ciascun documento utilizzando il metodo .forEach, quindi il nome di tutti i campi che inizia coninizia con product_. Da lì sarà necessario aggiungere nuovi campi products che è una matrice di prodotti utilizzando l'operatore di aggiornamento $set. Detto questo è necessario utilizzare "bulk" operazioni per aggiornare i documenti per la massima efficienza

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(); 
} 

I vostri documenti saranno ora simile a questa:

{ 
     "_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 
       } 
     ] 
} 

Poi viene la query di aggregazione utilizzando il metodo .aggregate():

db.collection.aggregate([ 
    { "$match": { "_id": 542 } }, 
    { "$unwind": "$products" }, 
    { "$group": { 
     "_id": "$_id", 
     "maxOrderCancelled": { "$max": "$products.orderCancelled"}, 
     "minOrderDelivvered": { "$min": "$products.orderDelivered"} 
    }} 
]) 

Che restituisce:

{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivvered" : 6 } 

Da version 3.2 è possibile utilizzare il $max e $min nel vostro $project fase, che è un modo molto più meglio per fare questo perché non c'è alcuna necessità di $unwind l'array prima.

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" 
       } 
      } 
     } 
    }} 
]) 

che produce:

{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivered" : 6 } 
Problemi correlati