sql >> Base de Datos >  >> NoSQL >> MongoDB

Compruebe si el campo existe en un subdocumento de una matriz

Básicamente quieres $elemMatch y $exists operador, ya que inspeccionará cada elemento para ver si la condición "el campo no existe" es verdadera para cualquier elemento:

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

});

Eso devuelve el segundo documento solo porque el campo no está presente en uno de los subdocumentos de la matriz:

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

Tenga en cuenta que esto "difiere" de este formulario:

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

})

Donde se pregunta si "todos" los elementos de la matriz no contienen este campo, lo cual no es cierto cuando un documento tiene al menos un elemento que tiene el campo presente. Entonces el $eleMatch hace que la condición se pruebe contra "cada" elemento de la matriz y, por lo tanto, obtiene la respuesta correcta.

Si desea actualizar estos datos para que cualquier elemento de matriz encontrado que no contenga este campo reciba ese campo con un valor de false (presumiblemente), entonces incluso podría escribir una declaración como esta:

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

Donde el .aggregate() El resultado no solo coincide con los documentos, sino que filtra el contenido de la matriz donde el campo no estaba presente, para devolver simplemente la "id" de ese subdocumento en particular.

Luego el bucle .update() las declaraciones coinciden con cada elemento de matriz encontrado en cada documento y agregan el campo faltante con un valor en la posición coincidente.

De esa manera, tendrá el campo presente en todos los subdocumentos de todos los documentos donde faltaba antes.

Si desea hacer tal cosa, también sería prudente cambiar su esquema para asegurarse de que el campo también esté siempre allí:

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

Entonces, la próxima vez que agregue nuevos elementos a la matriz en su código, el elemento siempre existirá con su valor predeterminado si no se establece explícitamente de otra manera. Y probablemente sea una buena idea hacerlo, además de configurar required en otros campos que siempre desee, como "id".