MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Verifica se il campo esiste in un documento secondario di un array

In pratica vuoi $elemMatch e $exists operatore, poiché ispezionerà ogni elemento per vedere se la condizione "campo non esiste" è vera per qualsiasi elemento:

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

});

Ciò restituisce il secondo documento solo perché il campo non è presente in uno dei documenti secondari dell'array:

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

Nota che questo "differisce" da questo modulo:

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

})

Dove ciò sta chiedendo "tutti" gli elementi dell'array non contengono questo campo, il che non è vero quando un documento ha almeno un elemento che ha il campo presente. Quindi il $eleMatch verifica la condizione rispetto a "ogni" elemento dell'array e quindi ottieni la risposta corretta.

Se volevi aggiornare questi dati in modo che qualsiasi elemento dell'array trovato che non contenesse questo campo ricevesse quel campo con un valore di false (presumibilmente), allora potresti anche scrivere una dichiarazione come questa:

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

Dove il .aggregate() result non solo corrisponde ai documenti, ma filtra il contenuto dall'array in cui il campo non era presente, in modo da restituire semplicemente l'"id" di quel particolare documento secondario.

Quindi il loop .update() le istruzioni corrispondono a ciascun elemento dell'array trovato in ogni documento e aggiungono il campo mancante con un valore nella posizione corrispondente.

In questo modo avrai il campo presente in tutti i documenti secondari di ogni documento dove prima mancava.

Se vuoi fare una cosa del genere, sarebbe anche saggio cambiare il tuo schema per assicurarti che anche il campo sia sempre lì:

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

Quindi la prossima volta che aggiungi nuovi elementi all'array nel tuo codice, l'elemento esisterà sempre con il suo valore predefinito se non diversamente impostato in modo esplicito. Ed è probabilmente una buona idea farlo, oltre a impostare required su altri campi che vuoi sempre, come "id".