ここでの基本的な概念は、条件を適用して配列要素を条件に「フィルタリング」するために、集約フレームワークが必要であるということです。利用可能なバージョンに応じて、適用できるさまざまな手法があります。
すべての場合において、これは結果です:
{
"_id" : ObjectId("593921425ccc8150f35e7664"),
"user1" : 1,
"user2" : 4,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-09T10:04:50Z"),
"body" : "hiii 1"
}
}
{
"_id" : ObjectId("593921425ccc8150f35e7663"),
"user1" : 1,
"user2" : 3,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-10T10:04:50Z"),
"body" : "hiii 2"
}
}
{
"_id" : ObjectId("593921425ccc8150f35e7662"),
"user1" : 1,
"user2" : 2,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-08T10:04:50Z"),
"body" : "hiii 0"
}
}
MongoDB3.4以降
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$replaceRoot": {
"newRoot": {
"$let": {
"vars": {
"messages": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"maxDate": {
"$max": {
"$map": {
"input": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"as": "m",
"in": "$$m.datetime"
}
}
}
},
"in": {
"_id": "$_id",
"user1": "$user1",
"user2": "$user2",
"messages": {
"$arrayElemAt": [
{ "$filter": {
"input": "$$messages",
"as": "m",
"cond": { "$eq": [ "$$m.datetime", "$$maxDate" ] }
}},
0
]
}
}
}
}
}}
])
これは、 $replaceRoot<を利用する最も効率的な方法です。 / code>
これにより、 <を使用して、「置換」構造で使用する変数を宣言できます。 code> $ let
。ここでの主な利点は、「2つの」パイプラインステージのみが必要なことです。
配列のコンテンツを一致させるには、 $ filter コード>
$ eq
を適用する場所
"sender"
の値をテストするための論理演算 。条件が一致する場合、一致する配列エントリのみが返されます。
同じ $ filter
を使用する
一致する「送信者」エントリのみが考慮されるように、 $ max
「フィルタリングされた」リストを「datetime」
の値に変更します 。 $ max
] 5
値は、条件による「最新の」日付です。
この値が必要なのは、後で「フィルター処理された」配列から返された結果をこの「maxDate」と比較できるようにするためです。これは、 "in"
内で発生することです $ let
のブロック
ここで、フィルタリングされたコンテンツに対して以前に宣言された2つの「変数」と「maxDate」が再び $ filter
「最新の日付」を持つ両方の条件を満たす唯一の値を返すため。
「1つの」結果のみが必要なため、 $arrayElemAtを使用します。
配列ではなく値を使用します。
MongoDB 3.2
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$project": {
"user1": 1,
"user2": 1,
"messages": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"maxDate": {
"$max": {
"$map": {
"input": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"as": "m",
"in": "$$m.datetime"
}
}
}
}},
{ "$project": {
"user1": 1,
"user2": 1,
"messages": {
"$arrayElemAt":[
{ "$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.datetime", "$maxDate" ] }
}},
0
]
}
}}
])
これは基本的に説明と同じプロセスですが、$がありません。 replaceRoot
パイプラインステージでは、2つの $ project コード>
ステージ。この理由は、最終的な $ filter
、および複合ステートメントで実行することはできないため、代わりにパイプラインを分割します。これは、運用の全体的なコストにわずかな影響を及ぼします。
MongoDB 2.6から3.0では、 <を除いて、ここでほとんどの手法を使用できます。 code> $ arrayElemAt
そして、単一のエントリで「配列」の結果を受け入れるか、 $ unwind
単一のエントリになるはずの段階に対処します。
MongoDB以前のバージョン
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$unwind": "$messages" },
{ "$match": { "messages.sender": 1 } },
{ "$sort": { "_id": 1, "messages.datetime": -1 } },
{ "$group": {
"_id": "$_id",
"user1": { "$first": "$user1" },
"user2": { "$first": "$user2" },
"messages": { "$first": "$messages" }
}}
])
簡単に見えますが、これははるかにコストのかかる操作です。ここでは、 $ unwind
を使用する必要があります
配列要素に条件を適用するため。これは、配列エントリごとに各ドキュメントのコピーを生成するため、非常にコストのかかるプロセスであり、「フィルタリング」の場合にこれを回避する最新の演算子に本質的に置き換えられます。
2番目の $ match
ここでのステージは、「送信者」条件に一致しなかった要素(現在は「ドキュメント」)を破棄します。次に、 $ sort
を適用します。
_id
によって、各ドキュメントの「最新」の日付を一番上に表示するため したがって、2つの「ソート」キー。
最後に、 $ group
を適用します
元のドキュメントを参照するには、 $firstを使用します。
「上」にある要素を取得するためのアキュムレータとして。