集計フレームワークを使用してこれを行うには、いくつかの方法があります
たとえば、単純なデータセット:
{
"_id" : ObjectId("538181738d6bd23253654690"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 2, "rating": 6 },
{ "_id": 3, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654691"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 4, "rating": 6 },
{ "_id": 2, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654692"),
"movies": [
{ "_id": 2, "rating": 5 },
{ "_id": 5, "rating": 6 },
{ "_id": 6, "rating": 7 }
]
}
最初の「ユーザー」を例として使用して、他の2人のユーザーのいずれかが同じ映画を少なくとも2つ持っているかどうかを調べます。
MongoDB 2.6以降では、$setIntersection
演算子と$size
演算子:
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document if you want to keep more than `_id`
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
}},
// Unwind the array
{ "$unwind": "$movies" },
// Build the array back with just `_id` values
{ "$group": {
"_id": "$_id",
"movies": { "$push": "$movies._id" }
}},
// Find the "set intersection" of the two arrays
{ "$project": {
"movies": {
"$size": {
"$setIntersection": [
[ 1, 2, 3 ],
"$movies"
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
これは、これらの演算子を持たない以前のバージョンのMongoDBでも、さらにいくつかの手順を使用するだけで可能です。
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document along with the "set" to match
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
"set": { "$cond": [ 1, [ 1, 2, 3 ], 0 ] }
}},
// Unwind both those arrays
{ "$unwind": "$movies" },
{ "$unwind": "$set" },
// Group back the count where both `_id` values are equal
{ "$group": {
"_id": "$_id",
"movies": {
"$sum": {
"$cond":[
{ "$eq": [ "$movies._id", "$set" ] },
1,
0
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
詳細h3>
それは少し理解するのが難しいかもしれないので、各段階を見て、それらを分解して、彼らが何をしているのかを見ることができます。
$ match :コレクション内のすべてのドキュメントを操作する必要はないため、正確なを見つけるためにさらに作業が必要な場合でも、一致しない可能性のあるアイテムを削除する機会です。 もの。したがって、明らかなことは、同じ「ユーザー」を除外し、その「ユーザー」で見つかったものと同じ映画を少なくとも1つ含むドキュメントのみを照合することです。
次に意味があるのは、n
と一致させたい場合にそれを考慮することです。 その場合、エントリは、n-1
より大きい「movies」配列を持つドキュメントのみになります。 実際に一致が含まれている可能性があります。 $and
の使用 ここは面白く見え、特に必要ではありませんが、必要な一致が4
の場合 その場合、ステートメントの実際の部分は次のようになります。
"$and": [
{ "movies": { "$not": { "$size": 1 } } },
{ "movies": { "$not": { "$size": 2 } } },
{ "movies": { "$not": { "$size": 3 } } }
]
したがって、基本的に、n
を持つのに十分な長さではない可能性のある配列を「除外」します。 一致します。ここで、この $size
クエリフォームの演算子は、 $size
集約フレームワーク用。たとえば、 $gt
などの不等式演算子でこれを使用する方法はありません。 要求された「サイズ」を具体的に一致させることが目的です。したがって、このクエリフォームは、より小さい可能性のあるすべてのサイズを指定します。
$ project :このステートメントにはいくつかの目的がありますが、そのいくつかは、使用しているMongoDBのバージョンによって異なります。まず、オプションで、ドキュメントのコピーが_id
の下に保持されます これらのフィールドが残りのステップで変更されないように値を設定します。ここでの他の部分は、次のステージのコピーとして、ドキュメントの上部にある「movies」配列を保持することです。
2.6より前のバージョンで提示されたバージョンでも発生しているのは、_id
を表す追加の配列があることです。 一致する「映画」の値。 $cond
の使用法 ここでの演算子は、配列の「リテラル」表現を作成する方法にすぎません。面白いことに、MongoDB 2.6では、 $literal
と呼ばれる演算子が導入されています。 $cond
を使用している面白い方法なしでこれを正確に行うには ここです。
$ unwind :さらに何かを行うには、映画の配列をほどく必要があります。どちらの場合も、既存の_id
を分離する唯一の方法です。 「セット」と照合する必要があるエントリの値。したがって、2.6より前のバージョンでは、存在する両方のアレイを「巻き戻す」必要があります。
$ group :MongoDB 2.6以降では、_id
のみを含む配列にグループ化するだけです。 「評価」が削除された映画の値。
2.6より前では、すべての値が「並べて」表示されるため(そして多くの重複があります)、2つの値を比較して、それらが同じであるかどうかを確認します。それがtrue
であるところ 、これは $cond
に通知します 1
の値を返す演算子ステートメント または0
ここで、条件はfalse
。これは、 $sum
を介して直接返されます。 配列内の一致する要素の数を必要な「セット」に合計します。
$ project :これがMongoDB 2.6以降の別の部分であるのは、「映画」の配列をプッシュバックしたためです_id
次に使用している値$setIntersection
それらのアレイを直接比較します。この結果、同じ要素を含む配列が作成されるため、これは $size
でラップされます。 その一致するセットで返された要素の数を判別するための演算子。
$ match :ここで実装された最終段階であり、交差する要素の数が必要な数以上であるドキュメントのみを照合する明確なステップを実行します。
最終
それは基本的にあなたがそれをする方法です。 2.6より前のバージョンは少し扱いにくく、セットのすべての可能な値によって検出された各配列メンバーを複製することによって拡張が行われるため、もう少し多くのメモリが必要になりますが、それでもこれを行うための有効な方法です。
あなたがする必要があるのは、より大きなn
でこれを適用することです 条件に一致する値を一致させます。もちろん、元のユーザーの一致に必要なn
が含まれていることを確認します。 可能性。それ以外の場合は、n-1
でこれを生成します 「映画」の「ユーザー」配列の長さから。