MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Verificar se o campo existe em um subdocumento de um Array


Você basicamente quer $elemMatch e o $exists operador, pois isso inspecionará cada elemento para ver se a condição "campo não existe" é verdadeira para qualquer elemento:
Model.find({
  "line_items": {
      "$elemMatch": { "review_request_sent": { "$exists": false } }
  }
},function(err,docs) {

});

Isso retorna o segundo documento apenas porque o campo não está presente em um dos subdocumentos da matriz:
{
        "id" : 2,
        "line_items" : [
                {
                        "id" : 1,
                        "review_request_sent" : false
                },
                {
                        "id" : 39
                }
        ]
}

Observe que isso "difere" deste formulário:
Model.find({
  "line_items.review_request_sent": { "$exists": false } 
},function(err,docs) {

})

Onde isso está perguntando, "todos" os elementos da matriz não contêm esse campo, o que não é verdade quando um documento tem pelo menos um elemento que tem o campo presente. Portanto, o $eleMatch faz com que a condição seja testada em relação a "cada" elemento da matriz e, assim, você obtém a resposta correta.

Se você quiser atualizar esses dados para que qualquer elemento de matriz encontrado que não contenha esse campo receba esse campo com um valor de false (presumivelmente), então você poderia até escrever uma declaração 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
        }
      );
    }
  );

Onde o .aggregate() result não apenas corresponde aos documentos, mas também filtra o conteúdo da matriz onde o campo não estava presente, de modo a retornar apenas o "id" desse subdocumento específico.

Em seguida, o loop .update() As instruções correspondem a cada elemento de matriz encontrado em cada documento e adiciona o campo ausente com um valor na posição correspondente.

Dessa forma você terá o campo presente em todos os subdocumentos de todos os documentos onde antes estava faltando.

Se você quiser fazer isso, também seria aconselhável alterar seu esquema para garantir que o campo também esteja 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
}

Portanto, da próxima vez que você adicionar novos itens à matriz em seu código, o elemento sempre existirá com seu valor padrão, se não for definido explicitamente. E provavelmente é uma boa ideia fazer isso, assim como configurar required em outros campos que você sempre deseja, como "id".