あなたの質問には2つの可能性がありますが、おそらくあなたを始めるための説明があります。
まず、<の意図を誤解していることを説明する必要があります。強い>$ elemMatch
この場合は誤用されています。
$ elemMatch
のアイデア
配列の要素に実際に適用される「クエリドキュメント」を作成することです。目的は、外部ドキュメントの配列全体ではなく、メンバードキュメント内で個別に一致させるために、配列内のドキュメントに「複数の条件」がある場合です。つまり:
{
"data": [
{ "a": 1, "b": 3 },
{ "a": 2, "b": 2 }
]
}
また、その配列内の実際の単一要素が一致しなくても、次のクエリは機能しますが、ドキュメント全体は機能します。
db.collection.find({ "data.a": 1, "data.b": 2 })
ただし、実際の要素がこれらの条件の両方に一致するかどうかを確認するには、ここで $ elemMatch
を使用します :
db.collection.find({ "data": { "a": 1, "b": 2 } })
したがって、そのサンプルには一致しません。特定の配列要素に両方の要素が含まれている場合にのみ一致します。
これで、 $ elemMatch
ができました。 説明しました、これがあなたの単純化されたクエリです:
db.collection.find({ "tracks.artist": { "$in": arr } })
はるかに単純で、単一のフィールドですべての配列メンバーを調べ、ドキュメント内の任意の要素にこれらの可能な結果の少なくとも1つが含まれている場所を返すことで機能します。
しかし、あなたが何を求めているのかではなく、あなたの質問についても同様です。その最後のステートメントを読むと、 $ in
実際には $ or
調子。これは、ドキュメント内の同じ要素に対して「または」を尋ねるための短縮形です。
そのことを念頭に置いて、あなたが求めているのは"と"です。 すべての「3つの」値が含まれる操作。テストで「3つ」のアイテムのみを送信すると仮定すると、 $ and
これは、 $all<の短縮形です。 / code>
:
db.collection.find({ "tracks.artist": { "$all": arr } })
これは、その配列のメンバー内に、テスト条件で指定された要素の「すべて」に一致する要素を含むドキュメントのみを返します。それはあなたが望むことかもしれませんが、もちろん、「4人以上」のアーティストのリストを指定して、その中から「3人」以下の数だけをテストしたい場合があります。 $ all
演算子が簡潔すぎます。
ただし、これを解決する論理的な方法があります。基本的なクエリでは使用できないが、集約フレームワーク :
var arr = ["A","B","C","D"]; // List for testing
db.collection.aggregate([
// Match conditions for documents to narrow down
{ "$match": {
"tracks.artist": { "$in": arr },
"tracks.2": { "$exists": true } // you would construct in code
}},
// Test the array conditions
{ "$project": {
"user": 1,
"tracks": 1, // any fields you want to keep
"matched": {
"$gte": [
{ "$size": {
"$setIntersection": [
{ "$map": {
"input": "$tracks",
"as": "t",
"in": { "$$t.artist" }
}},
arr
]
}},
3
]
}
}},
// Filter out anything that did not match
{ "$match": { "matched": true } }
])
最初のステージでは、標準クエリ $match<を実装します。 / code>
条件に一致する可能性が「高い」ドキュメントのみにドキュメントをフィルタリングするための条件。ここでの論理的なケースは、 $in<を使用することです。 / code>
以前と同様に、「テスト」配列に存在する要素の少なくとも1つが、ドキュメント自体の配列のメンバーフィールドの少なくとも1つに存在するドキュメントが検索されます。
次の句は、配列の「長さ」に関連しているため、理想的にはコードで構築する必要があります。ここでの考え方は、少なくとも「3」の一致が必要な場合です。その場合、ドキュメントでテストする配列には、それを満たすために少なくとも「3」の要素が必要です。したがって、「2」以下の配列要素を持つドキュメントを取得しても意味がありません。 「3」に一致することは決してないからです。
すべてのMongoDBクエリは基本的にデータ構造の単なる表現であるため、これを非常に簡単に構築できます。つまり、JavaScriptの場合:
var matchCount = 3; // how many matches we want
var match1 = { "$match": { "tracks.artist": { "$in": arr } } };
match1["$match"]["tracks."+ (matchCount-1)] = { "$exits": true };
そこにある論理は、<を含む「ドット表記」形式です。 code> $ examples 指定されたインデックス(n-1)に要素が存在するかどうかをテストします。配列が少なくともその長さであるためには、要素が存在する必要があります。
残りの絞り込みでは、理想的には $を使用します。 setIntersection
実際の配列とテストされた配列の間で一致した要素を返すためのメソッド。ドキュメント内の配列は「テスト配列」の構造と一致しないため、 $ map
各配列要素から「artist」フィールドのみを返すように設定された操作。
これら2つのアレイの「交差」が作成されると、最終的に $ size
テストが適用された共通要素の結果リストのうち、それらの要素の「少なくとも3つ」が共通であることが判明したことを確認します。
最後に、 $ match
状態。
理想的には、これらの演算子を使用できるようにするために、MongoDB2.6以降を使用しています。 2.2.xおよび2.4.xの以前のバージョンでは、それでも可能ですが、作業と処理のオーバーヘッドが少し増えます:
db.collection.aggregate([
// Match conditions for documents to narrow down
{ "$match": {
"tracks.artist": { "$in": arr },
"tracks.2": { "$exists": true } // you would construct in code
}},
// Unwind the document array
{ "$unwind": "$tracks" },
// Filter the content
{ "$match": { "tracks.artist": { "$in": arr } }},
// Group for distinct values
{ "$group": {
"_id": {
"_id": "$_id",
"artist": "$tracks.artist"
}
}},
// Make arrays with length
{ "$group": {
"_id": "$_id._id",
"artist": { "$push": "$_id.artist" },
"length": { "$sum": 1 }
}},
// Filter out the sizes
{ "$match": { "length": { "$gte": 3 } }}
])