Zasadniczo chcesz $elemMatch
i $exists
operatora, ponieważ spowoduje to sprawdzenie każdego elementu, aby sprawdzić, czy warunek „pole nie istnieje” jest spełniony dla dowolnego elementu:
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
Zwraca to tylko drugi dokument, ponieważ pole nie występuje w jednym z poddokumentów tablicy:
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
Zauważ, że to "różni się" od tego formularza:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
Tam, gdzie to pytanie, „wszystkie” elementy tablicy nie zawierają tego pola, co nie jest prawdą, gdy dokument ma co najmniej jeden element, który ma to pole. Więc $eleMatch
sprawia, że warunek jest testowany z "każdym" elementem tablicy, a tym samym otrzymujesz poprawną odpowiedź.
Jeśli chcesz zaktualizować te dane, aby każdy znaleziony element tablicy, który nie zawierał tego pola, otrzymał to pole z wartością false
(przypuszczalnie ), wtedy możesz nawet napisać takie oświadczenie:
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
}
);
}
);
Gdzie .aggregate()
wynik nie tylko pasuje do dokumentów, ale odfiltrowuje zawartość z tablicy, w której pole nie było obecne, aby po prostu zwrócić "id" tego konkretnego dokumentu podrzędnego.
Następnie zapętlony .update()
instrukcje dopasowują każdy znaleziony element tablicy w każdym dokumencie i dodają brakujące pole z wartością w dopasowanej pozycji.
W ten sposób pole będzie obecne we wszystkich poddokumentach każdego dokumentu, w którym wcześniej go brakowało.
Jeśli chcesz zrobić coś takiego, rozsądnie byłoby również zmienić schemat, aby upewnić się, że to pole również zawsze tam jest:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
Tak więc następnym razem, gdy dodasz nowe elementy do tablicy w kodzie, element zawsze będzie istniał ze swoją wartością domyślną, jeśli nie określono inaczej. I prawdopodobnie jest to dobry pomysł, podobnie jak ustawienie required
w innych polach, które zawsze chcesz, np. „id”.