sql >> Databasteknik >  >> NoSQL >> MongoDB

Kontrollera om fältet finns i ett underdokument till en array

Du vill i princip ha $elemMatch och $exists operatorn, eftersom denna kommer att inspektera varje element för att se om villkoret "fältet existerar inte" är sant för något element:

Model.find({
  "line_items": {
      "$elemMatch": { "review_request_sent": { "$exists": false } }
  }
},function(err,docs) {

});

Det returnerar endast det andra dokumentet eftersom fältet inte finns i ett av arrayunderdokumenten:

{
        "id" : 2,
        "line_items" : [
                {
                        "id" : 1,
                        "review_request_sent" : false
                },
                {
                        "id" : 39
                }
        ]
}

Observera att detta "skiljer sig" från detta formulär:

Model.find({
  "line_items.review_request_sent": { "$exists": false } 
},function(err,docs) {

})

Där det frågar innehåller "alla" arrayelementen inte detta fält, vilket inte är sant när ett dokument har minst ett element som har fältet närvarande. Alltså $eleMatch gör att villkoret testas mot "varje" arrayelement, och därmed får du rätt svar.

Om du ville uppdatera dessa data så att alla arrayelement som hittas som inte innehöll det här fältet fick det fältet med värdet false (förmodligen ), då kan du till och med skriva ett påstående så här:

    Model.aggregate(
      [
        { "$match": { 
          "line_items": {
            "$elemMatch": { "review_request_sent": { "$exists": false } }
          } 
        }},
        { "$project": {
          "line_items": {
            "$setDifference": [
              {"$map": {
                "input": "$line_items",
                "as": "item",
                "in": {
                  "$cond": [
                    { "$eq": [ 
                      { "$ifNull": [ "$$item.review_request_sent", null ] },
                      null
                    ]},
                    "$$item.id",
                    false
                  ]
                }
              }},
              [false]
            ]
          }
        }}
    ],
    function(err,docs) {
      if (err) throw err;
      async.each(
        docs,
        function(doc,callback) {
          async.each(
            doc.line_items,
            function(item,callback) {
              Model.update(
                { "_id": doc._id, "line_items.id": item },
                { "$set": { "line_items.$.review_request_sent": false } },
                callback
              );
            },
            callback
          );
        },
        function(err) {
          if (err) throw err;
          // done
        }
      );
    }
  );

Där .aggregate() resultat matchar inte bara dokumenten, utan filtrerar bort innehåll från arrayen där fältet inte fanns, för att bara returnera "id" för det specifika underdokumentet.

Sedan den loopade .update() satser matchar varje hittat matriselement i varje dokument och lägger till det saknade fältet med ett värde vid den matchade positionen.

På så sätt kommer du att ha fältet närvarande i alla underdokument till varje dokument där det saknades tidigare.

Om du vill göra något sådant skulle det också vara klokt att ändra ditt schema för att se till att fältet alltid finns där också:

{id: Number,
  line_items: [{ 
    id: String,
    quantity: Number,
    review_request_sent: { type: Boolean, default: false }
  }],
  total_price: String,
  name: String,
  order_number: Number
}

Så nästa gång du lägger till nya objekt till arrayen i din kod kommer elementet alltid att existera med dess standardvärde om inte annat uttryckligen anges. Och det är förmodligen en bra idé att göra det, liksom att ställa in required på andra fält du alltid vill ha, till exempel "id".



  1. Mongoose-schemareferens och odefinierad typ 'ObjectID'

  2. Morphia-fråga med eller operator

  3. Mongo aggregerad kapslad array

  4. Hur man returnerar en sträng med mongodb-aggregation