基本的には、 $elemMatch
が必要です。
および$exists
演算子。これにより、各要素が検査され、「フィールドが存在しない」という条件がどの要素にも当てはまるかどうかが確認されます。
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
フィールドが配列サブドキュメントの1つに存在しないため、2番目のドキュメントのみが返されます。
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
これはこのフォームとは「異なる」ことに注意してください:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
それが求めているのは、配列要素の「すべて」にこのフィールドが含まれていないことです。これは、フィールドが存在する要素がドキュメントに少なくとも1つある場合には当てはまりません。したがって、$eleMatch
条件を「各」配列要素に対してテストするため、正しい応答が得られます。
このデータを更新して、このフィールドを含まない配列要素が見つかった場合は、false
の値でそのフィールドを受け取ることになります。 (おそらく)、次のようなステートメントを書くこともできます:
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
}
);
}
);
.aggregate()
の場所 結果はドキュメントと一致するだけでなく、フィールドが存在しなかった配列からコンテンツを除外して、その特定のサブドキュメントの「id」を返すだけです。
次に、ループした.update()
ステートメントは、各ドキュメントで見つかった各配列要素と一致し、欠落しているフィールドに一致した位置の値を追加します。
このようにして、以前は欠落していたすべてのドキュメントのすべてのサブドキュメントにフィールドが存在するようになります。
そのようなことをしたい場合は、スキーマを変更して、フィールドも常にそこにあることを確認することも賢明です:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
したがって、次にコードの配列に新しい項目を追加するとき、明示的に設定されていない限り、要素は常にデフォルト値で存在します。そして、required
を設定するだけでなく、そうすることはおそらく良い考えです。 「id」など、常に必要な他のフィールド。