集計式演算子
Overview
このガイドでは、MongoDB Java ドライバーを使用して、集計パイプラインで使用する式を構築する方法を学習できます。 BSON ドキュメントではなく、検出可能なタイプセーフの Java メソッドを使用して式操作を実行できます。 これらのメソッドはスムーズなインターフェース パターンに従うため、集計操作を連鎖させて、より圧縮とより自然な読み取りの両方のコードを作成できます。
このガイドの操作では、 com.mongodb.client.model.mql のメソッドを使用します パッケージ。これらのメソッドは、ドライバーが MongoDB の配置を操作するメカニズムである Query API を慣用的に使用する方法を提供します。 Query API の詳細については、サーバー マニュアル ドキュメントを参照してください。
操作の使用方法
このガイドの例では、コードに次の静的インポートが含まれていることを前提としています。
import static com.mongodb.client.model.Aggregates.*; import static com.mongodb.client.model.Accumulators.* import static com.mongodb.client.model.Projections.*; import static com.mongodb.client.model.Filters.*; import static com.mongodb.client.model.mql.MqlValues.*; import static java.util.Arrays.asList;
式内のドキュメント フィールドにアクセスするには、集計パイプラインによって処理されている現在のドキュメントを参照する必要があります。 このドキュメントを参照するには、 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>)
これらのメソッドの詳細については、 集計ビルダを参照してください。
これらの例では、 asList()
メソッドを使用して集計ステージのリストを作成しています。 このリストは、 MongoCollection
のaggregate()
メソッドに渡されます。
コンストラクター メソッド
これらのコンストラクター メソッドを使用して、Java 集計式で使用する値を定義できます。
方式 | 説明 |
---|---|
集計パイプラインによって処理されている現在のドキュメントを参照します。 | |
集計パイプラインによって処理されている現在のドキュメントをマップ値として参照します。 | |
指定されたプリミティブに対応する MqlValue 型を返します。 | |
指定されたプリミティブの配列に対応する MqlValue 型の配列を返します。 | |
エントリ値を返します。 | |
空のマップ値を返します。 | |
クエリ API に存在する null 値を返します。 |
重要
これらのメソッドのいずれかに値を指定すると、ドライバーはその値を文字通りそれを扱います。 たとえば、 of("$x")
はx
という名前のフィールドではなく、string の値"$x"
を表します。
これらのメソッドを使用する例については、「操作」のいずれかのセクションを参照してください。
操作
次のセクションでは、ドライバーで使用可能な集計式操作に関する情報と例を提供します。 操作は、目的と機能によって分類されます。
各セクションには、ドライバーで使用できる集計メソッドと、クエリ API の対応する式演算子を説明する表があります。 メソッド名は API ドキュメントにリンクされ、集計パイプライン演算子名はサーバー マニュアル ドキュメントの説明と例にリンクされます。 各 Java メソッドは対応する Query API 式と実質的に同等ですが、期待されるパラメータと実装が異なる場合があります。
注意
ドライバーは、各例に提供されている Query API 式とは異なる Query API 式を生成します。 ただし、どちらの式でも生成される集計結果は同じになります。
重要
ドライバーは、クエリ API のすべての集計パイプライン演算子のメソッドを提供しません。 集計でサポートされていない操作を使用する必要がある場合は、BSON Document
型を使用して式全体を定義する必要があります。 Document
型の詳細については、ドキュメント を参照してください。
算術演算
このセクションで説明されているメソッドを使用して、 MqlInteger
またはMqlNumber
タイプの値に対して算術演算を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
毎日の降順の降順の測定値(メートル単位)を含む特定の年の気象データがあるとします。 各月の平均降格量をミリ秒単位で求めます。
multiply()
演算子は、 precipitation
フィールドに25.4
を乗算して値をミリ秒に変換します。 avg()
アキュムレータ メソッドはavgPrecipMM
フィールドとして平均を返します。 group()
メソッドは、各ドキュメントのdate
フィールドに指定された値を月別にグループ化します。
次のコードは、この集計のパイプラインを示しています。
var month = current().getDate("date").month(of("UTC")); var precip = current().getInteger("precipitation"); asList(group( month, avg("avgPrecipMM", precip.multiply(25.4)) ));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $group: { _id: { $month: "$date" }, avgPrecipMM: { $avg: { $multiply: ["$precipitation", 25.4] } } } } ]
配列演算
このセクションで説明されているメソッドを使用して、 MqlArray
の値に対して配列操作を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
映画のコレクションがあり、それぞれに今後の公開イベントのためにネストされたドキュメントの配列が含まれているとします。 ネストされた各ドキュメントには、劇場の総シート数を表す 配列が含まれています。配列の最初のエントリはプレミアム シートの数、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()
を使用してシートの合計数を計算し、その値をticketsBought
lt()
とともに の数と比較します。project()
メソッドは、これらのフィルタリングされた結果を新しいavailableShowtimes
配列として保存します。
Tip
配列の値を特定の型として操作する場合は、 getArray()
メソッドで取得する配列のタイプを指定する必要があります。
この例では、 seats
配列にMqlDocument
型の値が含まれていることを指定し、各配列エントリからネストされたフィールドを抽出できます。
次のコードは、この集計のパイプラインを示しています。
var showtimes = current().<MqlDocument>getArray("showtimes"); asList(project(fields( computed("availableShowtimes", showtimes .filter(showtime -> { var seats = showtime.<MqlInteger>getArray("seats"); var totalSeats = seats.sum(n -> n); var ticketsBought = showtime.getInteger("ticketsBought"); var isAvailable = ticketsBought.lt(totalSeats); return 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
フィールドに記録します。
次のコードは、この集計のパイプラインを示しています。
var temperature = current().getInteger("temperature"); asList(project(fields( 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()
メソッドは Java の三項演算子に似ており、ブール値に基づく単純なブランチには使用する必要があります。 値型でパターン マッチングを実行したり、値に対してその他のチェックを実行したりするなど、より複雑な比較を行うには、 switchOn()
メソッドを使用する必要があります。
Java メソッド | 集計パイプライン演算子 |
---|---|
次の例では、 location
フィールドの値が"California"
であるすべてのドキュメントに一致するパイプラインを示しています。
var location = current().getString("location"); asList(match(expr(location.eq(of("California")))));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $match: { location: { $eq: "California" } } } ]
条件付き操作
このセクションで説明されているメソッドを使用して、条件付き操作を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
メンバーシップ情報を持つカスタマーのコレクションがあるとします。 元々、カスタマーはメンバーかどうかのどちらかでした。 時間と共に、メンバーシップレベルが導入され、同じフィールドが使用されます。 このフィールドに保存される情報は数あるタイプのいずれかになる可能性があり、そのメンバーシップ レベルを示す標準化された値を作成する必要があります。
switchOn()
メソッドは、各句を順番にチェックします。 値が 句で示される型と一致する場合、その句はメンバーシップ レベルに対応する string の値を決定します。 元の値が string の場合、それはメンバーシップ レベルを表し、その値が使用されます。 データ型がブール値の場合、メンバーシップ レベルのGold
またはGuest
のいずれかが返されます。 データ型が配列の場合、 配列内の最新のメンバーシップ レベルに一致する最新の string が返されます。 member
フィールドが不明なタイプの場合、 switchOn()
メソッドはデフォルト値Guest
を提供します。
次のコードは、この集計のパイプラインを示しています。
var member = current().getField("member"); asList(project(fields( computed("membershipLevel", member.switchOn(field -> field .isString(s -> s) .isBoolean(b -> b.cond(of("Gold"), of("Guest"))) .<MqlString>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
タイプの値にカスタム関数を適用できます。
読みやすさを向上させ、コードの再利用を可能にするには、冗長コードを静的メソッドに移動します。 ただし、Java で静的メソッドを直接連鎖させることはできません。 passTo()
メソッドを使用すると、値をカスタム静的メソッドに連鎖させることができます。
Java メソッド | 集計パイプライン演算子 |
---|---|
対応する演算子がありません |
いくつかのベンチマークに対してクラスがどのように実行されているかを判断したいとします。 各クラスの平均最終成績を見つけ、ベンチマーク値と比較します。
次のカスタム メソッドgradeAverage()
は、ドキュメントの配列と、それらのドキュメント間で共有される整数フィールドの名前を受け取ります。 指定された配列内のすべてのドキュメントにわたるそのフィールドの平均を計算し、指定された配列内のすべての要素にわたるそのフィールドの平均を決定します。 evaluate()
メソッドは、指定された値を指定された 2 つの範囲制限と比較し、値の比較方法に基づいて応答stringを生成します。
public static MqlNumber gradeAverage(MqlArray<MqlDocument> students, String fieldName) { var sum = students.sum(student -> student.getInteger(fieldName)); var avg = sum.divide(students.size()); return avg; } public static MqlString evaluate(MqlNumber grade, MqlNumber cutoff1, MqlNumber cutoff2) { var 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()
メソッドを使用する利点の 1 つは、カスタム メソッドを他の集計で再利用できることです。 gradeAverage()
メソッドを使用すると、クラスだけでなく、エントリー年や地区などでフィルタリングされた学生グループの平均成績を見つけることができます。 evaluate()
メソッドを使用して、個々の生徒のパフォーマンス、または学校全体または地区のパフォーマンスなどを評価できます。
passArrayTo()
メソッドはすべての生徒を取得し、 gradeAverage()
メソッドを使用して平均スコアを計算します。 次に、 passNumberTo()
メソッドはevaluate()
メソッドを使用してクラスのパフォーマンスを判断します。 この例では、 project()
メソッドを使用して結果をevaluation
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
var students = current().<MqlDocument>getArray("students"); asList(project(fields( computed("evaluation", students .passArrayTo(students -> gradeAverage(students, "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
タイプ間で変換できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
廃止年を含む学生データのコレクションがあり、string として保存されるとします。 この値は 5 年間で、その値を新しい フィールドに保存します。
parseInteger()
メソッドはgraduationYear
を整数に変換し、 add()
が同期年を計算できるようにします。 addFields()
メソッドは、この結果を新しいreunionYear
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
var graduationYear = current().getString("graduationYear"); asList(addFields( new Field("reunionYear", graduationYear .parseInteger() .add(5)) ));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $addFields: { reunionYear: { $add: [ { $toInt: "$graduationYear" }, 5 ] } } } ]
日付操作
このセクションで説明されている方法を使用して、 MqlDate
型の値に対して日付操作を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
パッケージの配達に関するデータがあり、 "America/New_York"
タイムゾーン内のいずれかの月曜日に発生した配達を一致させたいとします。
deliveryDate
フィールドに、 "2018-01-15T16:00:00Z"
やJan 15, 2018, 12:00
PM EST
などの有効な日付を表す string 値が含まれている場合は、 parseDate()
メソッドを使用して string を日付型に変換できます。
dayOfWeek()
メソッドは、曜日を決定し、 "America/New_York"
パラメータに従って月曜日に基づいて数値に変換します。 eq()
メソッドは、この値を2
と比較します。これは指定されたタイムゾーン パラメータに基づく月曜日に対応します。
次のコードは、この集計のパイプラインを示しています。
var deliveryDate = current().getString("deliveryDate"); asList(match(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
型の値に対してドキュメント操作を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
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"
であるかどうかを確認します。
次のコードは、この集計のパイプラインを示しています。
var address = current().getDocument("mailing.address"); asList(match(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
のいずれかのタイプの値に対してマップ操作を実行できます。
Tip
データが日付やアイテム ID などの任意のキーを値にマッピングする場合は、データをマップとして表現する必要があります。
Java メソッド | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません |
たとえば、各ドキュメントが提供を担当する個々の商品を表す在庫データのコレクションがあるとします。 各ドキュメントには、すべての倉庫のマップと、アイテムの在庫内に現在存在するコピーの数である フィールドが含まれています。 すべての倉庫に存在する商品のコピー数の合計を判別したい場合があります。 このコレクションのドキュメントは、次のようになります。
{ "_id": ..., "item": "notebook" "warehouses": [ { "Atlanta", 50 }, { "Chicago", 0 }, { "Portland", 120 }, { "Dallas", 6 } ] }
entries()
メソッドは、 warehouses
フィールドのマップエントリを配列として返します。 sum()
メソッドは、 getValue()
メソッドで検索された 配列の値に基づいて、アイテムの合計値を計算します。 この例では、 project()
メソッドを使用して、結果を新しいtotalInventory
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
var warehouses = current().getMap("warehouses"); asList(project(fields( computed("totalInventory", warehouses .entries() .sum(v -> v.getValue())) )));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { totalInventory: { $sum: { $getField: { $objectToArray: "$warehouses" }, } } } } ]
string操作
このセクションで説明されているメソッドを使用して、 MqlString
タイプの値に対して string 操作を実行できます。
Java メソッド | 集計パイプライン演算子 |
---|---|
従業員の姓と従業員 ID から、会社の従業員の小文字のユーザー名を生成するとします。
append()
メソッドはfirstName
lastName
フィールドと フィールドを 1 つのユーザー名に結合し、toLower()
メソッドはユーザー名全体を小文字にします。この例では、 project()
メソッドを使用して、結果を新しいusername
フィールドとして保存します。
次のコードは、この集計のパイプラインを示しています。
var lastName = current().getString("lastName"); var employeeID = current().getString("employeeID"); asList(project(fields( computed("username", lastName .append(employeeID) .toLower()) )));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { username: { $toLower: { $concat: ["$lastName", "$employeeID"] } } } } ]
型チェック操作
このセクションで説明されている方法を使用して、 MqlValue
型の値に対して型チェック操作を実行できます。
これらのメソッドはブール値を返しません。 代わりに、メソッドによって指定される型と一致するデフォルト値を指定します。 チェックされた値がメソッドの種類と一致する場合は、チェックされた値が返されます。 それ以外の場合は、指定されたデフォルト値が返されます。 データ型に基づいてブランチ ロジックをプログラムする場合は、 switchOn()
を参照してください。
Java メソッド | 集計パイプライン演算子 |
---|---|
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません | |
対応する演算子がありません |
評価データのコレクションがあるとします。 レビュー スキーマの初期バージョンでは、ユーザーはスター評価なしで負のレビューを送信できました。 スター評価のないこれらの否定的なレビューを、最小値が 1 スターになるように変換します。
isNumberOr()
メソッドは、 rating
の値を返すか、 rating
が数値でないか null の場合は1
の値を返します。 project()
メソッドは、この値を新しいnumericalRating
フィールドとして返します。
次のコードは、この集計のパイプラインを示しています。
var rating = current().getField("rating"); asList(project(fields( computed("numericalRating", rating .isNumberOr(of(1))) )));
次のコードは、Query API で同等の集計パイプラインを提供します。
[ { $project: { numericalRating: { $cond: { if: { $isNumber: "$rating" }, then: "$rating", else: 1 } } } } ]