MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Vérifier si le champ existe dans un sous-document d'un tableau

Vous voulez essentiellement $elemMatch et le $exists car cela inspectera chaque élément pour voir si la condition "le champ n'existe pas" est vraie pour n'importe quel élément :

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

});

Cela renvoie le deuxième document uniquement car le champ n'est pas présent dans l'un des sous-documents du tableau :

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

Notez que cela "diffère" de ce formulaire :

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

})

Là où cela demande, "tous" les éléments du tableau ne contiennent-ils pas ce champ, ce qui n'est pas vrai lorsqu'un document a au moins un élément qui a le champ présent. Donc le $eleMatch teste la condition par rapport à "chaque" élément du tableau, et vous obtenez ainsi la réponse correcte.

Si vous vouliez mettre à jour ces données afin que tout élément de tableau trouvé qui ne contenait pas ce champ reçoive alors ce champ avec une valeur de false (vraisemblablement), vous pourriez même écrire une déclaration comme celle-ci :

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

Où le .aggregate() le résultat correspond non seulement aux documents, mais filtre le contenu du tableau où le champ n'était pas présent, de manière à simplement renvoyer l'"id" de ce sous-document particulier.

Puis le .update() en boucle correspondent à chaque élément de tableau trouvé dans chaque document et ajoute le champ manquant avec une valeur à la position correspondante.

De cette façon, vous aurez le champ présent dans tous les sous-documents de chaque document où il manquait auparavant.

Si vous souhaitez faire une telle chose, il serait également judicieux de modifier votre schéma pour vous assurer que le champ est toujours là :

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

Ainsi, la prochaine fois que vous ajouterez de nouveaux éléments au tableau dans votre code, l'élément existera toujours avec sa valeur par défaut s'il n'est pas explicitement défini autrement. Et c'est probablement une bonne idée de le faire, ainsi que de définir required sur d'autres champs que vous voulez toujours, comme "id".