it-swarm.com.de

Prüfen Sie, ob das Feld in einem Unterdokument eines Arrays vorhanden ist

Ich habe ein ähnliches Schema. 

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

Was ich finden möchte, sind alle Elemente, die das Feld review_request_sent nicht für alle Elemente im Array festgelegt haben.

Beispiel 

{ 
  id: 1, 
  line_items: [
    {
      id: 43,
      review_request_sent: true
    }
  ]
},
{
  id: 2,
  line_items: [
    {
      id: 1,
      review_request_sent: false
    },
    {
      id: 39
    },
 ]
},
{
  id: 3,
  line_items: [
    {
     id: 23,
     review_request_sent: true
    },
    {
     id: 85,
     review_request_sent: true
    },
    {
     id: 12,
     review_request_sent: false
    }
  ]
}

Ich möchte Hilfe bei einer Abfrage/Entdeckung, die nur das zweite Dokument zurückgibt, da das Feld review_request_sent nicht in allen Elementen des Arrays vorhanden ist.

13
user841818

Grundsätzlich möchten Sie $elemMatch und den Operator $exists , da hier jedes Element geprüft wird, um zu sehen, ob die Bedingung "Feld nicht vorhanden" für jedes Element wahr ist

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

});

Das gibt nur das zweite Dokument zurück, da das Feld in einem der Array-Unterdokumente nicht vorhanden ist:

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

Beachten Sie, dass sich dieses Formular von diesem Formular unterscheidet:

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

})

Wenn dies verlangt wird, enthalten "alle" Array-Elemente dieses Feld nicht. Dies ist nicht der Fall, wenn in einem Dokument mindestens ein Element vorhanden ist, in dem das Feld vorhanden ist. Mit $eleMatch wird die Bedingung mit "jedem" Array-Element getestet, und Sie erhalten die richtige Antwort.


Wenn Sie diese Daten aktualisieren möchten, sodass ein Arrayelement, das dieses Feld nicht enthielt, dieses Feld mit dem Wert false (vermutlich) erhalten sollte, können Sie sogar eine Anweisung wie folgt schreiben:

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

Das .aggregate()-Ergebnis stimmt nicht nur mit den Dokumenten überein, sondern filtert Inhalt aus dem Array, in dem das Feld nicht vorhanden war, um nur die "ID" dieses bestimmten Unterdokuments zurückzugeben.

Dann passen die geschleiften .update()-Anweisungen zu jedem gefundenen Array-Element in jedem Dokument und fügen das fehlende Feld mit einem Wert an der übereinstimmenden Position hinzu.

Auf diese Weise haben Sie das Feld in allen Teildokumenten aller Dokumente, in denen es zuvor fehlte.

Wenn Sie so etwas tun möchten, ist es auch ratsam, Ihr Schema zu ändern, um sicherzustellen, dass das Feld auch immer vorhanden ist:

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

Das nächste Mal, wenn Sie dem Array in Ihrem Code neue Elemente hinzufügen, ist das Element immer mit seinem Standardwert vorhanden, sofern nicht ausdrücklich etwas anderes festgelegt ist. Es ist wahrscheinlich eine gute Idee, dies zu tun, und required für andere Felder festzulegen, die Sie immer möchten, wie zum Beispiel "id".

18
Blakes Seven

Sie können dies anders finden, ohne $map und $unwind verwendet $ redact wie folgt:

db.collectionName.aggregate({
  "$match": {
    "line_items.review_request_sent": {
      "$exists": true
    }
  }
}, {
  "$redact": {
    "$cond": {
      "if": {
        "$eq": [{
          "$ifNull": ["$review_request_sent", "null"]
        }, "null"]
      },
      "then": "$$DESCEND",
      "else": "$$Prune"
    }
  }
}, {
  "$match": {
    "line_items": {
      "$size": 1
    }
  }
}).pretty()

In der obigen Abfrage zuerst match prüfen, ob review_request_sent ist vorhanden oder nicht in redact$ ifNull wird verwendet, um zu prüfen, ob review_request_sent präsentiert oder nicht, wenn nicht, dann ersetzt es "null" und $eq überprüfen "null" Dies wird Dokumente wie zurückgeben

{ "_id" : ObjectId("55e6ca322c33bb07ff2c163e"), "id" : 1, "line_items" : [ ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c163f"), "id" : 2, "line_items" : [ { "id" : 39 } ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c1640"), "id" : 3, "line_items" : [ ] }

und nach diesem letzten Spiel nur die Dokumente überprüfen, deren line_items Array enthält den Wert $ size bis 1

2
Yogesh

Wenn Sie nur das gesamte Dokument erhalten möchten, wie in @Blakes Seven erwähnt. Sie können mit $ elemMatch abfragen.
Wenn Sie Array-Elemente filtern möchten und nur die Array-Elemente erhalten möchten, die kein bestimmtes Feld enthalten, können Sie $ filter zusammen mit $ type in der Aggregationspipeline verwenden.

[
    { $match: { "line_items": { "$elemMatch": { "review_request_sent": { "$exists": false } } } } },
    { $project: {
        "line_items": { "$filter": { 
            "input": "$line_items", 
            "as": "item", 
            "cond": { "$eq": [ { "$type": "$$item.review_request_sent" }, "missing" ] }
        } }
    } }
]

Hinweis:$ type gibt "fehlend" zurück, wenn er mit undefinierten Feldern verwendet wird.

1