2015-10-22 11 views
5

Ich habe eine Abfrage, um die minimum query und maximum query auf unter Beispieldatensatz zu erhalten. In meinem Fall Felder sind Namen dynamisch, wie unten product_1, product_2 ...MongoDB: Suche Minimum, Maximum in verschachtelten Objekt mit dynamischen Feldname

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

ich eine Idee bekommen bin nicht, wie kann ich dies tun in Mongo, wo die Feldnamen dynamisch sind, in Zukunft bedeutet, es können auch andere sein Produkte werden auch als product_4 und product_5 für die gleichen id erstellt.

Ich brauche eine Abfrage, die mir Minimalwert für orderDelivered und Maximalwert für orderCancelled, wie beispielsweise in oben Dokument Ergebnis wird orderCancelled:0orderDelivered:16 & gibt.
Danke für jede Idee.

+0

Warum Sie nicht über Ihre Produkte in ein Array setzen? –

+0

@DmytroShevchenko Ich habe dich nicht bekommen. Tatsächlich kann ich in einem anderen Fall auch die Zählung für 'orderDelivered' durch' Produktname' angeben. Nun, was wird das Design in Ihrem Fall sein? – Shams

+0

Bitte schauen Sie sich das Beispiel der Datenstruktur in meiner Antwort an. Könnten Sie bitte beschreiben, was Sie meinen, indem Sie für 'orderDelivered' nach' product name' zählen? –

Antwort

1

Sie sollten Ihr Dokument neu strukturieren, so dass alle Produktdokumente in einem Array sind:

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

Dann werden Sie in der Lage sein, normale max/min Abfragen wie folgt auszustellen:

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

Wie sieht es mit der Änderung der Dokumentstruktur aus? – styvane

+2

@ user3100115 vorhandene Daten müssen mit einem separaten Tool geändert werden. Ich glaube, dass die Diskussion über ein solches Tool hier nicht möglich wäre. –

+0

Nein, Sie können dies mit einem Update – styvane

2

Sie Sie müssen Ihre Dokumentenstruktur ändern, indem Sie sie aktualisieren. Sie müssen jedes Dokument durchlaufen, indem Sie die .forEach Methode verwenden und dann $unset den Namen aller Felder, die mit product_ beginnen. Von dort müssen Sie neue Felder products hinzufügen, die eine Reihe von Produkten ist, die den Aktualisierungsoperator $set verwenden. Davon abgesehen Sie "bulk" Operationen verwenden sollten Ihre Dokumente für maximale Effizienz

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

Ihre Dokumente nun wie folgt aussehen zu aktualisieren:

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

Dann kommt Ihre Aggregation Abfrage mit der .aggregate() Methode:

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

Zurückgeben:

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

Von version 3.2 können Sie die $max und $min in Ihrer $project Bühne benutzen, die eine viel bessere Möglichkeit, dies zu tun, weil es keine Notwendigkeit, $unwind Array zuerst.

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

Welche ergibt:

{ "_id" : NumberLong(542), "maxOrderCancelled" : 5, "minOrderDelivered" : 6 } 
Verwandte Themen