集計式演算子
Overview
このガイドでは、 Kotlin Sync ドライバーを使用して 集計パイプライン で使用する式を構築する方法を学習できます。 BSON ドキュメントではなく、検出可能でタイプセーフな Kotlin メソッドを使用して式操作を実行できます。 これらのメソッドはスムーズなインターフェース パターンに従うため、集計操作を連鎖させて、圧縮され、自然に読み取れるコードを作成できます。
このガイドの操作では、 com.mongodb.client.model.mql のメソッドを使用します パッケージ。これらのメソッドは、ドライバーが MongoDB の配置を操作するメカニズムである Query API を慣用的に使用する方法を提供します。 Query API の詳細については、サーバー マニュアル ドキュメントを参照してください。
操作の使用方法
このガイドの例では、コードに次のインポートが含まれていることを前提としています。
import com.mongodb.client.model.Aggregates import com.mongodb.client.model.Accumulators import com.mongodb.client.model.Projections import com.mongodb.client.model.Filters import com.mongodb.client.model.mql.MqlValues
式内のドキュメント フィールドにアクセスするには、 current()
メソッドを使用して、集計パイプラインによって処理されている現在のドキュメントを参照する必要があります。 フィールドの値にアクセスするには、 getString()
やgetDate()
などの適切な型のメソッドを使用する必要があります。 フィールドに型を指定すると、ドライバーはその型と互換性のあるメソッドのみを提供するようになります。 次のコードは、 name
という string フィールドを参照する方法を示しています。
current().getString("name")
操作で値を指定するには、その値をof()
コンストラクター メソッドに渡して、有効なタイプに変換します。 次のコードは、 1.0
の値を参照する方法を示しています。
of(1.0)
操作を作成するには、フィールドまたは値の参照にメソッドを連鎖させます。 複数のメソッドを連鎖させることで、より複雑な操作を構築できます。
次の例では、医療機関を少なくとも 1 回訪問した新しいメキシコ語の検査を作成する操作を作成します。 この操作では、次のアクションが実行されます。
gt()
メソッドを使用して、visitDates
配列の値が0
より大きいかどうかを確認eq()
メソッドを使用して、state
フィールドの値が「ニューメキシコ」であるかどうかを確認します
and()
メソッドはこれらの操作をリンクして、パイプライン ステージは両方の条件を満たすドキュメントのみと一致するようにします。
current() .getArray("visitDates") .size() .gt(of(0)) .and(current() .getString("state") .eq(of("New Mexico")))
group()
などの一部の集計ステージは操作を直接受け入れますが、他のステージでは最初にcomputed()
やexpr()
などのメソッドに操作を含めることが必要です。 TExpression
タイプの値を受け取るこれらのメソッドでは、特定の集計で式を使用できます。
集計パイプライン ステージを完了するには、集計ビルダのメソッドに式を含めます。 次のリストは、一般的な集計ビルダのメソッドに式を含める方法の例を示しています。
match(expr(<expression>))
project(fields(computed("<field name>", <expression>)))
group(<expression>)
これらの方法の詳細については、 「 集計によるデータの変換 」ガイドを参照してください。
コンストラクター メソッド
これらのコンストラクター メソッドを使用して、 Kotlin 集計式で使用する値を定義できます。
方式 | 説明 |
---|---|
集計パイプラインによって処理されている現在のドキュメントを参照します。 | |
集計パイプラインによって処理されている現在のドキュメントをマップ値として参照します。 | |
指定されたプリミティブに対応する MqlValue 型を返します。 | |
指定されたプリミティブの配列に対応する MqlValue 型の配列を返します。 | |
エントリ値を返します。 | |
空のマップ値を返します。 | |
クエリ API に存在する null 値を返します。 |
重要
これらのメソッドのいずれかに値を指定すると、ドライバーはその値を文字通りそれを扱います。 たとえば、 of("$x")
はx
という名前のフィールドではなく、string の値"$x"
を表します。
これらのメソッドを使用する例については、「操作」の下のセクションのいずれかを参照してください。
操作
次のセクションでは、ドライバーで使用可能な集計式操作に関する情報と例を提供します。 操作は、目的と機能によって分類されます。
各セクションには、ドライバーで使用できる集計メソッドと、クエリ API の対応する式演算子を説明する表があります。 メソッド名は API ドキュメントにリンクされ、集計パイプライン演算子名はサーバー マニュアル ドキュメントの説明と例にリンクされます。 各メソッドは対応する集計演算子と実質的に同等ですが、期待されるパラメーターと実装が異なる場合があります。
各セクションの例では、 listOf()
メソッドを使用して集計ステージからパイプラインを作成しています。 次に、各例によりパイプラインがMongoCollection
のaggregate()
メソッドに渡されます。
注意
ドライバーは、各例に提供されている Query API 式とは異なる Query API 式を生成します。 ただし、どちらの式でも生成される集計結果は同じになります。
重要
ドライバーは、クエリ API のすべての集計パイプライン演算子のメソッドを提供しません。 サポートされていない操作を集計で使用するには、BSON Document
型を使用して式全体を定義する必要があります。
算術演算
このセクションで説明されているメソッドを使用して、 MqlInteger
またはMqlNumber
タイプの値に対して算術演算を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
毎日の降順の降順の測定値(メートル単位)を含む特定の年の気象データがあるとします。 各月の平均降格量をミリ秒単位で求めます。
multiply()
演算子は、 precipitation
フィールドに25.4
を乗算してフィールド値をミリ秒に変換します。 avg()
アキュムレータ メソッドはavgPrecipMM
フィールドとして平均を返します。 group()
メソッドは、各ドキュメントのdate
フィールドに指定された値を月別にグループ化します。
次のコードは、この集計のパイプラインを示しています。
val month = current().getDate("date").month(of("UTC")) val precip = current().getInteger("precipitation") val results = collection.aggregate<Document>( listOf( Aggregates.group( month, Accumulators.avg("avgPrecipMM", precip.multiply(25.4)) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $group: { _id: { $month: "$date" }, avgPrecipMM: { $avg: { $multiply: ["$precipitation", 25.4] } } } } ]
配列演算
このセクションで説明されているメソッドを使用して、 MqlArray
の値に対して配列操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
映画のコレクションがあり、それぞれに今後の公開イベントのためにネストされたドキュメントの配列が含まれているとします。 ネストされた各ドキュメントには、劇場の総シート数を表す 配列が含まれています。配列の最初のエントリはプレミアム シートの数、2 番目のエントリは通常のシートの数です。 ネストされた各ドキュメントには、ライブタイムのためにすでに購入されたチケットの数も含まれています。 このコレクションのドキュメントは、次のようになります。
{ "_id": ..., "movie": "Hamlet", "showtimes": [ { "date": "May 14, 2023, 12:00 PM", "seats": [ 20, 80 ], "ticketsBought": 100 }, { "date": "May 20, 2023, 08:00 PM", "seats": [ 10, 40 ], "ticketsBought": 34 }] }
filter()
メソッドは、指定された述語に一致する結果のみを表示します。 この場合、述語はsum()
を使用してシートの合計数を計算し、 lt()
メソッドを使用してその値をticketsBought
の数と比較します。 project()
メソッドは、これらのフィルタリングされた結果を新しいavailableShowtimes
配列フィールドとして保存します。
Tip
getArray()
メソッドを使用して値を特定の型として操作する場合は、配列に含まれる値のタイプを指定する必要があります。 たとえば、アプリケーション内の他の場所で整数を使用して計算を実行するには、配列に整数が含まれている必要があります。
このセクションの例では、 seats
配列にMqlDocument
型の値が含まれていることを指定し、各配列エントリからネストされたフィールドを抽出できるようにします。
次のコードは、この集計のパイプラインを示しています。
val showtimes = current().getArray<MqlDocument>("showtimes") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed("availableShowtimes", showtimes .filter { showtime -> val seats = showtime.getArray<MqlInteger>("seats") val totalSeats = seats.sum { n -> n } val ticketsBought = showtime.getInteger("ticketsBought") val isAvailable = ticketsBought.lt(totalSeats) isAvailable }) ) ) ) )
注意
読みやすくするために、前の例ではtotalSeats
変数とisAvailable
変数に中間値を割り当てています。これらの中間値を変数に割り当てない場合、コードは引き続き同等の結果を生成します。
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { availableShowtimes: { $filter: { input: "$showtimes", as: "showtime", cond: { $lt: [ "$$showtime.ticketsBought", { $sum: "$$showtime.seats" } ] } } } } } ]
ブール演算
このセクションで説明されているメソッドを使用して、 MqlBoolean
タイプの値に対してブール値演算を実行できます。
非常に低い気象値または高い気象温度の読み取り値(華氏単位)を極端に分類するとします。
or()
temperature
演算子は、 メソッドとlt()
メソッドを使用して、gt()
フィールドを事前定義された値と比較して、温度が極端に高いかどうかをチェックします。project()
メソッドは、この結果をextremeTemp
フィールドに記録します。
次のコードは、この集計のパイプラインを示しています。
val temperature = current().getInteger("temperature") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed( "extremeTemp", temperature .lt(of(10)) .or(temperature.gt(of(95))) ) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { extremeTemp: { $or: [ { $lt: ["$temperature", 10] }, { $gt: ["$temperature", 95] } ] } } } ]
比較操作
このセクションで説明されているメソッドを使用して、 MqlValue
型の値に対して比較操作を実行できます。
Tip
cond()
メソッドは Kotlin の三項演算子に似ており、ブール値に基づく単純なブランチに使用できます。 値型でパターン マッチングを実行したり、値に対してその他のチェックを実行したりするなど、より複雑な比較を行うには、 switchOn()
メソッドを使用します。
方式 | 集計パイプライン演算子 |
---|---|
次の例では、 location
フィールドの値が"California"
であるすべてのドキュメントに一致するパイプラインを示しています。
val location = current().getString("location") val results = collection.aggregate<Document>( listOf( Aggregates.match( Filters.expr(location.eq(of("California"))) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $match: { location: { $eq: "California" } } } ]
条件付き操作
このセクションで説明されているメソッドを使用して、条件付き操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
メンバーシップ情報を持つカスタマーのコレクションがあるとします。 元々、カスタマーはメンバーかどうかのどちらかでした。 時間と共に、メンバーシップレベルが導入され、同じフィールドが使用されます。 このフィールドに保存される情報は数あるタイプのいずれかになる可能性があり、そのメンバーシップ レベルを示す標準化された値を作成する必要があります。
switchOn()
メソッドは、各句を順番にチェックします。 値が 句で示される型と一致する場合、 句はメンバーシップ レベルに対応する string の値を決定します。 元の値が string の場合、それはメンバーシップ レベルを表し、その値が使用されます。 データ型がブール値の場合、メンバーシップ レベルのGold
またはGuest
のいずれかが返されます。 データ型が配列の場合、 配列内の最新のメンバーシップ レベルに一致する最新の string が返されます。 member
フィールドが不明なタイプの場合、 switchOn()
メソッドはデフォルト値Guest
を提供します。
次のコードは、この集計のパイプラインを示しています。
val member = current().getField("member") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed("membershipLevel", member.switchOn { field -> field .isString { s -> s } .isBoolean { b -> b.cond(of("Gold"), of("Guest")) } .isArray { a -> a.last() } .defaults { d -> of("Guest") } }) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { membershipLevel: { $switch: { branches: [ { case: { $eq: [ { $type: "$member" }, "string" ] }, then: "$member" }, { case: { $eq: [ { $type: "$member" }, "bool" ] }, then: { $cond: { if: "$member", then: "Gold", else: "Guest" } } }, { case: { $eq: [ { $type: "$member" }, "array" ] }, then: { $last: "$member" } } ], default: "Guest" } } } } ]
便利な操作
このセクションで説明されている方法を使用して、 MqlValue
タイプの値にカスタム関数を適用できます。
読みやすさを向上させ、コードの再利用を可能にするには、冗長コードを静的メソッドに移動します。 ただし、 Kotlin で静的メソッドを直接連鎖させることはできません。 passTo()
メソッドを使用すると、値をカスタム静的メソッドに連鎖させることができます。
方式 | 集計パイプライン演算子 |
---|---|
対応する演算子がありません |
いくつかのベンチマークに対してクラスがどのように実行されているかを判断したいとします。 各クラスの平均最終成績を見つけ、ベンチマーク値と比較します。
次のカスタム メソッドgradeAverage()
は、ドキュメントの配列と、それらのドキュメント間で共有される整数フィールドの名前を受け取ります。 指定された配列内のすべてのドキュメントにわたるそのフィールドの平均を計算し、指定された配列内のすべての要素にわたるそのフィールドの平均を決定します。 evaluate()
メソッドは、指定された値を指定された 2 つの範囲制限と比較し、値の比較方法に基づいて応答stringを生成します。
fun gradeAverage(students: MqlArray<MqlDocument>, fieldName: String): MqlNumber { val sum = students.sum { student -> student.getInteger(fieldName) } val avg = sum.divide(students.size()) return avg } fun evaluate(grade: MqlNumber, cutoff1: MqlNumber, cutoff2: MqlNumber): MqlString { val message = grade.switchOn { on -> on .lte(cutoff1) { g -> of("Needs improvement") } .lte(cutoff2) { g -> of("Meets expectations") } .defaults { g -> of("Exceeds expectations") } } return message }
Tip
passTo()
メソッドを使用すると、他の集計でカスタム メソッドを再利用できます。 たとえば、 gradeAverage()
メソッドを使用すると、クラスだけでなく、エントリー年または地区によってフィルタリングされた学生グループの平均成績を見つけることができます。 同様に、 evaluate()
メソッドを使用して、個々の生徒のパフォーマンスまたは学校全体のパフォーマンスを評価することもできます。
passArrayTo()
メソッドは、すべての生徒の配列を受け取り、 gradeAverage()
メソッドを使用して平均スコアを計算します。 次に、 passNumberTo()
メソッドはevaluate()
メソッドを使用してクラスのパフォーマンスを判断します。 この例では、 project()
メソッドを使用して結果をevaluation
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
val students = current().getArray<MqlDocument>("students") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed("evaluation", students .passArrayTo { s -> gradeAverage(s, "finalGrade") } .passNumberTo { grade -> evaluate(grade, of(70), of(85)) }) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { evaluation: { $switch: { branches: [ { case: { $lte: [ { $avg: "$students.finalGrade" }, 70 ] }, then: "Needs improvement" }, { case: { $lte: [ { $avg: "$students.finalGrade" }, 85 ] }, then: "Meets expectations" } ], default: "Exceeds expectations" } } } } ]
変換操作
このセクションで説明されているメソッドを使用して、変換操作を実行して特定のMqlValue
タイプ間で変換できます。
方式 | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
廃止年を含む学生データのコレクションがあり、string として保存されるとします。 この値は 5 年間で、その値を新しい フィールドに保存します。
parseInteger()
メソッドはgraduationYear
を整数に変換し、 add()
が同期年を計算できるようにします。 addFields()
メソッドは、この結果を新しいreunionYear
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
val students = current().getArray<MqlDocument>("students") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed("evaluation", students .passArrayTo { s -> gradeAverage(s, "finalGrade") } .passNumberTo { grade -> evaluate(grade, of(70), of(85)) }) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $addFields: { reunionYear: { $add: [ { $toInt: "$graduationYear" }, 5 ] } } } ]
日付操作
このセクションで説明されている方法を使用して、 MqlDate
型の値に対して日付操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
パッケージの配達に関するデータがあり、 "America/New_York"
タイムゾーン内のいずれかの月曜日に発生した配達を一致させたいとします。
deliveryDate
フィールドに、 "2018-01-15T16:00:00Z"
や"Jan 15, 2018, 12:00
PM EST"
などの有効な日付を表す string 値が含まれている場合は、 parseDate()
メソッドを使用して string を日付型に変換できます。
dayOfWeek()
メソッドは、日付が曜日を決定し、それを数値に変換します。 数値割り当てでは、 "America/New_York"
タイムゾーンを使用する場合、 0
が日曜日を意味するようになりました。 eq()
メソッドは、この値を2
(月曜日)と比較します。
次のコードは、この集計のパイプラインを示しています。
val deliveryDate = current().getString("deliveryDate") val results = collection.aggregate<Document>( listOf( Aggregates.match( Filters.expr( deliveryDate .parseDate() .dayOfWeek(of("America/New_York")) .eq(of(2)) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $match: { $expr: { $eq: [ { $dayOfWeek: { date: { $dateFromString: { dateString: "$deliveryDate" } }, timezone: "America/New_York" }}, 2 ] } } } ]
ドキュメント操作
このセクションで説明されている方法を使用して、 MqlDocument
型の値に対してドキュメント操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
mailing.address
フィールドの下に子ドキュメントとしてアドレスを含むレガシーカスタマーデータのコレクションがあるとします。 ワシントン州に住んでいるカスタマーをすべて検索したいとします。 このコレクションのドキュメントは、次のようになります。
{ "_id": ..., "customer.name": "Mary Kenneth Keller", "mailing.address": { "street": "601 Mongo Drive", "city": "Vasqueztown", "state": "CO", "zip": 27017 } }
getDocument()
メソッドはmailing.address
フィールドをドキュメントとして取得するため、ネストされたstate
フィールドはgetString()
メソッドで取得できます。 eq()
メソッドは、 state
フィールドの値が"WA"
であるかどうかを確認します。
次のコードは、この集計のパイプラインを示しています。
val address = current().getDocument("mailing.address") val results = collection.aggregate<Document>( listOf( Aggregates.match( Filters.expr( address .getString("state") .eq(of("WA")) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $match: { $expr: { $eq: [{ $getField: { input: { $getField: { input: "$$CURRENT", field: "mailing.address"}}, field: "state" }}, "WA" ] }}}]
マップ操作
このセクションで説明されているメソッドを使用して、 MqlMap
またはMqlEntry
のいずれかのタイプの値に対してマップ操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません |
たとえば、各ドキュメントが提供を担当する個々の商品を表す在庫データのコレクションがあるとします。 各ドキュメントには、すべての倉庫のマップと、アイテムの在庫内に持つコピーの数である フィールドが含まれています。 すべての倉庫に存在する商品の合計コピー数を判別したい場合があります。 このコレクションのドキュメントは、次のようになります。
{ "_id": ..., "item": "notebook" "warehouses": [ { "Atlanta", 50 }, { "Chicago", 0 }, { "Portland", 120 }, { "Dallas", 6 } ] }
entries()
メソッドは、 warehouses
フィールドのマップエントリを配列として返します。 sum()
メソッドは、 getValue()
メソッドで検索された 配列の値に基づいて、アイテムの合計値を計算します。 この例では、 project()
メソッドを使用して、結果を新しいtotalInventory
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
val warehouses = current().getMap<MqlNumber>("warehouses") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed("totalInventory", warehouses .entries() .sum { v -> v.getValue() }) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { totalInventory: { $sum: { $getField: { $objectToArray: "$warehouses" }, } } } } ]
string操作
このセクションで説明されているメソッドを使用して、 MqlString
タイプの値に対して string 操作を実行できます。
方式 | 集計パイプライン演算子 |
---|---|
従業員の姓と従業員 ID から、会社の従業員の小文字のユーザー名を生成するとします。
append()
メソッドはlastName
employeeID
フィールドと フィールドを 1 つのユーザー名に結合し、toLower()
メソッドはユーザー名全体を小文字にします。この例では、 project()
メソッドを使用して、結果を新しいusername
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
val lastName = current().getString("lastName") val employeeID = current().getString("employeeID") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed( "username", lastName .append(employeeID) .toLower() ) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { username: { $toLower: { $concat: ["$lastName", "$employeeID"] } } } } ]
型チェック操作
このセクションで説明されている方法を使用して、 MqlValue
型の値に対して型チェック操作を実行できます。
これらのメソッドはブール値を返しません。 代わりに、メソッドによって指定される型と一致するデフォルト値を指定します。 チェックされた値がメソッドの種類と一致する場合は、チェックされた値が返されます。 それ以外の場合は、指定されたデフォルト値が返されます。 データ型に基づいてブランチ ロジックをプログラムするには、 switchOn()
を参照してください。
方式 | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません |
評価データのコレクションがあるとします。 レビュー スキーマの初期バージョンでは、ユーザーはスター評価なしで負のレビューを送信できました。 スター評価のないこれらの否定的なレビューを、最小値が 1 スターになるように変換します。
isNumberOr()
メソッドは、 rating
の値を返すか、 rating
が数値でないか null の場合は1
の値を返します。 project()
メソッドは、この値を新しいnumericalRating
フィールドとして返します。
次のコードは、この集計のパイプラインを示しています。
val rating = current().getField("rating") val results = collection.aggregate<Document>( listOf( Aggregates.project( Projections.fields( Projections.computed( "numericalRating", rating .isNumberOr(of(1)) ) ) ) ) )
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { numericalRating: { $cond: { if: { $isNumber: "$rating" }, then: "$rating", else: 1 } } } } ]