アグリゲーションフレームワークのサポート

Spring Data MongoDB は、バージョン 2.2 で MongoDB に導入された集約 フレームワークのサポートを提供します。

詳細については、MongoDB の集約フレームワークおよびその他のデータ集約ツールの完全なリファレンスドキュメント (英語) を参照してください。

基本概念

Spring Data MongoDB の集約 フレームワークサポートは、次の主要な抽象化に基づいています: Aggregation (Javadoc) および AggregationResults (Javadoc)

  • Aggregation

    Aggregation は MongoDB aggregate 操作を表し、集約パイプライン命令の記述を保持します。集計は、Aggregation クラスの適切な newAggregation(…) 静的ファクトリメソッドを呼び出すことによって作成されます。このメソッドは、AggregateOperation のリストとオプションの入力クラスを受け取ります。

    実際の集約操作は、目的の出力クラスをパラメーターとして受け取る MongoTemplate の aggregate メソッドによって実行されます。

  • TypedAggregation

    TypedAggregation は、Aggregation と同様に、集約パイプラインの命令と、ドメインプロパティを実際のドキュメントフィールドにマッピングするために使用される入力型への参照を保持します。

    実行時に、潜在的な @Field アノテーションを考慮して、フィールド参照が指定された入力型に対してチェックされます。

3.2 で変更され、存在しないプロパティを参照してもエラーが発生しなくなりました。以前の動作を復元するには、AggregationOptions の strictMapping オプションを使用します。

  • AggregationDefinition

    AggregationDefinition は MongoDB 集約パイプライン操作を表し、この集約ステップで実行する必要がある処理を記述します。AggregationDefinition を手動で作成することもできますが、Aggregate クラスによって提供される静的ファクトリメソッドを使用して AggregateOperation を構築することをお勧めします。

  • AggregationResults

    AggregationResults は、集約操作の結果のコンテナーです。これにより、マッピングされたオブジェクトおよび集約に関するその他の情報への Document の形式で、生の集約結果へのアクセスが提供されます。

    次のリストは、MongoDB 集約 フレームワークの Spring Data MongoDB サポートを使用する標準的な例を示しています。

    import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
    
    Aggregation agg = newAggregation(
        pipelineOP1(),
        pipelineOP2(),
        pipelineOPn()
    );
    
    AggregationResults<OutputType> results = mongoTemplate.aggregate(agg, "INPUT_COLLECTION_NAME", OutputType.class);
    List<OutputType> mappedResult = results.getMappedResults();

入力クラスを newAggregation メソッドの最初のパラメーターとして指定すると、MongoTemplate はこのクラスから入力コレクションの名前を派生することに注意してください。それ以外の場合、入力クラスを指定しない場合は、入力コレクションの名前を明示的に指定する必要があります。入力クラスと入力コレクションの両方が指定された場合は、後者が優先されます。

サポートされている集約操作とステージ

MongoDB 集約 フレームワークは、次の型の集約ステージと操作を提供します。

  • addFields - AddFieldsOperation

  • バケット /bucketAuto - BucketOperation / BucketAutoOperation

  • カウント - CountOperation

  • 高密度化する - DensifyOperation

  • ファセット - FacetOperation

  • geoNear - GeoNearOperation

  • graphLookup - GraphLookupOperation

  • グループ - GroupOperation

  • 限界 - LimitOperation

  • 見上げる - LookupOperation

  • 一致 - MatchOperation

  • マージ - MergeOperation

  • プロジェクト - ProjectionOperation

  • 編集する - RedactOperation

  • replaceRoot - ReplaceRootOperation

  • サンプル - SampleOperation

  • セット - SetOperation

  • setWindowFields - SetWindowFieldsOperation

  • skip - SkipOperation

  • 並べ替え / 並べ替え数 - SortOperation / SortByCountOperation

  • unionWith - UnionWithOperation

  • 設定を解除する - UnsetOperation

  • くつろぐ - UnwindOperation

サポートされていない集約ステージ (MongoDB Atlas の $search (英語) など) は、AggregationOperation のいずれかを実装することで提供できます。Aggregation.stage は、JSON または Bson 表現を提供することでパイプラインステージを登録するためのショートカットです。

Aggregation.stage("""
    { $search : {
        "near": {
          "path": "released",
          "origin": { "$date": { "$numberLong": "..." } } ,
          "pivot": 7
        }
      }
    }
""");

このドキュメントの記載時点では、Spring Data MongoDB では次の集計演算子がサポートされています。

表 1: Spring Data MongoDB で現在サポートされている集計演算子

集合集計演算子

setEquals, setIntersection, setUnion, setDifference, setIsSubset, anyElementTrue, allElementsTrue

グループ / アキュムレータ集計演算子

addToSet, bottom, bottomN, covariancePop, covarianceSamp, expMovingAvg, first, firstN, last, lastN max, maxN, min, minN, avg, push, sum, top, topN, count (*), median, percentile, stdDevPop, stdDevSamp

算術集計演算子

abs, acos, acosh, add (* via plus), asin, asin, atan, atan2, atanh, ceil, cos, cosh, derivative, divide, exp, floor, integral, ln, log, log10, mod, multiply, pow, round, sqrt, subtract (* via minus), sin, sinh, tan, tanh, trunc

文字列集約演算子

concat, substr, toLower, toUpper, strcasecmp, indexOfBytes, indexOfCP, regexFind, regexFindAll, regexMatch, replaceAll, replaceOne, split`, strLenBytes, strLenCP, substrCP, trim, ltrim, rtim

比較集計演算子

eq (* via is), gt, gte, lt, lte, ne

配列集約演算子

arrayElementAt, arrayToObject, concatArrays, filter, first, in, indexOfArray, isArray, last, range`, reverseArray, reduce, size, sortArray, slice, zip

リテラル演算子

literal

日付集計演算子

dateSubstract, dateTrunc, dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateAdd, dateDiff, dateToString, dateFromString, dateFromParts, dateToParts, isoDayOfWeek, isoWeek, isoWeekYear, tsIncrement, tsSecond

変数演算子

map

条件付き集計演算子

cond, ifNull, switch

型集計演算子

type

集計演算子の変換

convert, degreesToRadians, toBool, toDate, toDecimal, toDouble, toInt, toLong, toObjectId, toString

オブジェクト集約演算子

objectToArray, mergeObjects, getField, setField

スクリプト集約演算子

function, accumulator

※操作は Spring Data MongoDB によりマッピングまたは追加されます。

ここにリストされていない集計操作は、現在 Spring Data MongoDB ではサポートされていないことに注意してください。比較集計演算子は Criteria 式として表されます。

射影式

射影式は、特定の集計ステップの結果であるフィールドを定義するために使用されます。射影式は、String オブジェクトのリストまたは集計フレームワーク Fields オブジェクトを渡すことにより、Aggregation クラスの project メソッドを通じて定義できます。and(String) メソッドを使用して流れるような API を介して追加のフィールドを使用して射影を継承したり、as(String) メソッドを使用してエイリアスを作成したりできます。集計フレームワークの Fields.field 静的ファクトリメソッドを使用して、エイリアスを持つフィールドを定義することもできます。これを使用して、新しい Fields インスタンスを構築できます。後の集計段階での射影フィールドへの参照は、含まれるフィールドまたはそのエイリアス (新しく定義されたフィールドとそのエイリアスを含む) のフィールド名に対してのみ有効です。射影に含まれていないフィールドは、後の集計段階では参照できません。次のリストは射影式の例を示しています。

例 1: 射影表現例
// generates {$project: {name: 1, netPrice: 1}}
project("name", "netPrice")

// generates {$project: {thing1: $thing2}}
project().and("thing1").as("thing2")

// generates {$project: {a: 1, b: 1, thing2: $thing1}}
project("a","b").and("thing1").as("thing2")
例 2: 射影とソートを使用した多段階集計
// generates {$project: {name: 1, netPrice: 1}}, {$sort: {name: 1}}
project("name", "netPrice"), sort(ASC, "name")

// generates {$project: {name: $firstname}}, {$sort: {name: 1}}
project().and("firstname").as("name"), sort(ASC, "name")

// does not work
project().and("firstname").as("name"), sort(ASC, "firstname")

プロジェクト操作のその他の例は、AggregationTests クラスにあります。射影式に関する詳細については、MongoDB Aggregation フレームワークリファレンスドキュメントの対応するセクション (英語) を参照してください。

ファセット分類

バージョン 3.4 の時点で、MongoDB は集約 フレームワークを使用したファセット分類をサポートしています。ファセット分類では、完全な分類エントリを作成するために結合される意味論的なカテゴリ (一般またはサブジェクト固有のいずれか) が使用されます。集約パイプラインを流れるドキュメントはバケットに分類されます。多面的な分類により、入力ドキュメントを複数回取得することなく、同じ入力ドキュメントのセットに対してさまざまな集計を行うことができます。

バケット

バケット操作では、指定された式とバケット境界に基づいて、受信ドキュメントをバケットと呼ばれるグループに分類します。バケット操作には、グループ化フィールドまたはグループ化式が必要です。これらは、Aggregate クラスの bucket() および bucketAuto() メソッドを使用して定義できます。BucketOperation および BucketAutoOperation は、入力ドキュメントの集計式に基づいて累積を公開できます。with … () メソッドと andOutput(String) メソッドを使用すると、流れるような API を通じて追加パラメーターを使用してバケット操作を継承できます。as(String) メソッドを使用して、操作に別名を付けることができます。各バケットは出力内でドキュメントとして表されます。

BucketOperation は、定義された境界セットを使用して、受信ドキュメントをこれらのカテゴリにグループ化します。境界を並べ替える必要があります。次のリストは、バケット操作の例をいくつか示しています。

例 3: バケット操作の例
// generates {$bucket: {groupBy: $price, boundaries: [0, 100, 400]}}
bucket("price").withBoundaries(0, 100, 400);

// generates {$bucket: {groupBy: $price, default: "Other" boundaries: [0, 100]}}
bucket("price").withBoundaries(0, 100).withDefault("Other");

// generates {$bucket: {groupBy: $price, boundaries: [0, 100], output: { count: { $sum: 1}}}}
bucket("price").withBoundaries(0, 100).andOutputCount().as("count");

// generates {$bucket: {groupBy: $price, boundaries: [0, 100], 5, output: { titles: { $push: "$title"}}}
bucket("price").withBoundaries(0, 100).andOutput("title").push().as("titles");

BucketAutoOperation は、ドキュメントを指定された数のバケットに均等に分散することを目的として境界を決定します。BucketAutoOperation はオプションで、計算された境界エッジが推奨される丸め番号または 10 の累乗で終了するようにするために使用する優先数値 [Wikipedia] (英語) 系列を指定する粒度値を受け取ります。次のリストは、バケット操作の例を示しています。

例 4: バケット操作の例
// generates {$bucketAuto: {groupBy: $price, buckets: 5}}
bucketAuto("price", 5)

// generates {$bucketAuto: {groupBy: $price, buckets: 5, granularity: "E24"}}
bucketAuto("price", 5).withGranularity(Granularities.E24).withDefault("Other");

// generates {$bucketAuto: {groupBy: $price, buckets: 5, output: { titles: { $push: "$title"}}}
bucketAuto("price", 5).andOutput("title").push().as("titles");

バケットに出力フィールドを作成するには、バケット操作で AggregationExpression ~ andOutput() および SpEL 式~ andOutputExpression() を使用できます。

バケット式の詳細については、MongoDB Aggregation フレームワークリファレンスドキュメントの $bucket セクション (英語) および $bucketAuto セクション (英語) を参照してください。

多面的な集約

複数の集計パイプラインを使用して、単一の集計ステージ内の複数のディメンション (またはファセット) にわたるデータを特徴付ける多面的な集計を作成できます。多面的な集計では、データの参照と分析をガイドする複数のフィルターと分類が提供されます。ファセットの一般的な実装は、製品の価格、メーカー、サイズ、その他の要素にフィルターを適用して検索結果を絞り込む方法を提供しているオンライン小売業者の数です。

FacetOperation は、Aggregation クラスの facet() メソッドを使用して定義できます。and() メソッドを使用して、複数の集計パイプラインでカスタマイズできます。各サブパイプラインには出力ドキュメント内に独自のフィールドがあり、その結果がドキュメントの配列として保存されます。

サブパイプラインは、グループ化する前に入力ドキュメントを投影およびフィルタリングできます。一般的な使用例には、分類前の日付部分の抽出や計算が含まれます。次のリストは、ファセット操作の例を示しています。

例 5: ファセット操作の例
// generates {$facet: {categorizedByPrice: [ { $match: { price: {$exists : true}}}, { $bucketAuto: {groupBy: $price, buckets: 5}}]}}
facet(match(Criteria.where("price").exists(true)), bucketAuto("price", 5)).as("categorizedByPrice"))

// generates {$facet: {categorizedByCountry: [ { $match: { country: {$exists : true}}}, { $sortByCount: "$country"}]}}
facet(match(Criteria.where("country").exists(true)), sortByCount("country")).as("categorizedByCountry"))

// generates {$facet: {categorizedByYear: [
//     { $project: { title: 1, publicationYear: { $year: "publicationDate"}}},
//     { $bucketAuto: {groupBy: $price, buckets: 5, output: { titles: {$push:"$title"}}}
// ]}}
facet(project("title").and("publicationDate").extractYear().as("publicationYear"),
      bucketAuto("publicationYear", 5).andOutput("title").push().as("titles"))
  .as("categorizedByYear"))

ファセット操作の詳細については、MongoDB Aggregation フレームワークリファレンスドキュメントの $facet セクション (英語) を参照してください。

数で並べ替え

カウントによる並べ替え操作では、指定された式の値に基づいて受信ドキュメントをグループ化し、各グループ内のドキュメントの数を計算し、結果をカウントで並べ替えます。ファセット分類を使用するときに並べ替えを適用するための便利なショートカットを提供します。カウントによる並べ替え操作には、グループ化フィールドまたはグループ化式が必要です。次のリストは、カウントによる並べ替えの例を示しています。

例 6: カウントによる並べ替えの例
// generates { $sortByCount: "$country" }
sortByCount("country");

カウントによる並べ替え操作は、次の BSON (バイナリ JSON) と同等です。

{ $group: { _id: <expression>, count: { $sum: 1 } } },
{ $sort: { count: -1 } }

射影式での Spring 式のサポート

ProjectionOperation クラスおよび BucketOperation クラスの andExpression メソッドを通じて、射影式での SpEL 式の使用をサポートします。この機能を使用すると、目的の式を SpEL 式として定義できます。クエリを実行すると、SpEL 式は対応する MongoDB 射影式部分に変換されます。この配置により、複雑な計算を非常に簡単に表現できるようになります。

SpEL 式を使用した複雑な計算

次の SpEL 式を考えてみましょう。

1 + (q + 1) / (q - 1)

前述の式は、次の射影式部分に変換されます。

{ "$add" : [ 1, {
    "$divide" : [ {
        "$add":["$q", 1]}, {
        "$subtract":[ "$q", 1]}
    ]
}]}

集約 フレームワークの例 5 および集約 フレームワークの例 6 で、より詳しいコンテキストの例を確認できます。サポートされている SpEL 式構成体のその他の使用例は、SpelExpressionTransformerUnitTests で見つけることができます。

サポートされている SpEL 変換
SpEL 式 Mongo 表情パーツ

a == b

{$eq: [$a、$b] }

a != b

{$ne: [$a、$b] }

a > b

{$gt: [$a、$b] }

a >= b

{$gte: [$a、$b] }

a < b

{$lt: [$a、$b] }

⇐ b

{$lte: [$a、$b] }

a + b

{$add: [$a、$b] }

a - b

{$subtract: [$a、$b] }

a * b

{$multiply: [$a、$b] }

a/b

{$divide: [$a、$b] }

a^b

{$pow: [$a、$b] }

a % b

{$mod: [$a、$b] }

a&&b

{$and: [$a、$b] }

| | b

{$or: [$a、$b] }

!a

{$not: [$a] }

前の表に示した変換に加えて、new などの標準 SpEL 操作を使用して、(たとえば) 配列を作成し、その名前 (その後に 括弧 で使用する引数が続く) を介して式を参照することができます。次の例は、この方法で配列を作成する方法を示しています。

// { $setEquals : [$a, [5, 8, 13] ] }
.andExpression("setEquals(a, new int[]{5, 8, 13})");

集約 フレームワークの例

このセクションの例では、MongoDB 集約 フレームワークと Spring Data MongoDB の使用パターンを示します。

集約 フレームワークの例 1

この入門例では、タグのリストを集約して、MongoDB コレクション ( tags と呼ばれる) から特定のタグの出現数を降順でソートして取得します。この例では、グループ化、並べ替え、射影 (選択)、巻き戻し (結果の分割) の使用箇所を示します。

class TagCount {
 String tag;
 int n;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

Aggregation agg = newAggregation(
    project("tags"),
    unwind("tags"),
    group("tags").count().as("n"),
    project("n").and("tag").previousOperation(),
    sort(DESC, "n")
);

AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

前述のリストでは、次のアルゴリズムが使用されています。

  1. newAggregation 静的ファクトリメソッドを使用して新しい集約を作成し、これに集約操作のリストを渡します。これらの集約操作は、Aggregation の集約パイプラインを定義します。

  2. project 操作を使用して、入力コレクションから tags フィールド (文字列の配列) を選択します。

  3. unwind 操作を使用して、tags 配列内のタグごとに新しいドキュメントを生成します。

  4. group 操作を使用して、出現数を集約する tags 値ごとにグループを定義します (count 集約演算子を使用し、結果を n という新しいフィールドに収集します)。

  5. n フィールドを選択し、前のグループ操作 (したがって previousOperation() の呼び出し) から生成された ID フィールドのエイリアスを tag という名前で作成します。

  6. sort 操作を使用して、結果のタグのリストを出現回数に基づいて降順に並べ替えます。

  7. MongoTemplate で aggregate メソッドを呼び出し、作成された Aggregation を引数として MongoDB に実際の集計操作を実行させます。

入力コレクションは、aggregate メソッドの tags パラメーターとして明示的に指定されることに注意してください。入力コレクションの名前が明示的に指定されていない場合、その名前は newAggreation メソッドの最初のパラメーターとして渡される入力クラスから派生されます。

集約 フレームワークの例 2

この例は、MongoDB Aggregation フレームワークドキュメントの状態ごとの最大都市と最小都市 (英語) 例に基づいています。さまざまな MongoDB バージョンで安定した結果を生成するために、追加の並べ替えを追加しました。ここでは、集計フレームワークを使用して、各状態の人口別に最小都市と最大都市を返したいと考えています。この例では、グループ化、並べ替え、射影 (選択) を示します。

class ZipInfo {
   String id;
   String city;
   String state;
   @Field("pop") int population;
   @Field("loc") double[] location;
}

class City {
   String name;
   int population;
}

class ZipInfoStats {
   String id;
   String state;
   City biggestCity;
   City smallestCity;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,
    group("state", "city")
       .sum("population").as("pop"),
    sort(ASC, "pop", "state", "city"),
    group("state")
       .last("city").as("biggestCity")
       .last("pop").as("biggestPop")
       .first("city").as("smallestCity")
       .first("pop").as("smallestPop"),
    project()
       .and("state").previousOperation()
       .and("biggestCity")
          .nested(bind("name", "biggestCity").and("population", "biggestPop"))
       .and("smallestCity")
          .nested(bind("name", "smallestCity").and("population", "smallestPop")),
    sort(ASC, "state")
);

AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class);
ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);

ZipInfo クラスは、指定された入力コレクションの構造をマップすることに注意してください。ZipInfoStats クラスは、目的の出力形式で構造体を定義します。

前述のリストでは次のアルゴリズムが使用されています。

  1. group 操作を使用して、入力コレクションからグループを定義します。グループ化条件は、state フィールドと city フィールドの組み合わせであり、グループの ID 構造を形成します。sum 演算子を使用して、グループ化された要素から population プロパティの値を集約し、結果を pop フィールドに保存します。

  2. sort 操作を使用して、popstatecity フィールドで中間結果を昇順に並べ替えます。結果の最小の都市が一番上に、最大の都市が結果の一番下になります。state および city の並べ替えは、(Spring Data MongoDB が処理した) グループ ID フィールドに対して暗黙的に実行されることに注意してください。

  3. group 操作を再度使用して、中間結果を state でグループ化します。state は再び暗黙的にグループ ID フィールドを参照することに注意してください。project 操作では、それぞれ last(…) オペレーターと first(…​) オペレーターを呼び出して、最大都市と最小都市の名前と人口数を選択します。

  4. 前の group 操作から state フィールドを選択します。state は再び暗黙的にグループ ID フィールドを参照することに注意してください。暗黙的に生成された ID が表示されることを望まないため、and(previousOperation()).exclude() を使用して前の操作から ID を除外します。出力クラスにネストされた City 構造体を設定したいため、ネストされたメソッドを使用して適切なサブドキュメントを出力する必要があります。

  5. sort 操作で、結果として得られる StateStats のリストを状態名で昇順に並べ替えます。

入力コレクションの名前は、newAggregation メソッドの最初のパラメーターとして渡される ZipInfo クラスから派生することに注意してください。

集約 フレームワークの例 3

この例は、MongoDB Aggregation フレームワークドキュメントの人口 1,000 万人を超える状態 (英語) 例に基づいています。さまざまな MongoDB バージョンで安定した結果を生成するために、追加の並べ替えを追加しました。ここでは、集計フレームワークを使用して、人口が 1,000 万を超えるすべての状態を返したいと考えています。この例では、グループ化、並べ替え、一致 (フィルタリング) を示します。

class StateStats {
   @Id String id;
   String state;
   @Field("totalPop") int totalPopulation;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class,
    group("state").sum("population").as("totalPop"),
    sort(ASC, previousOperation(), "totalPop"),
    match(where("totalPop").gte(10 * 1000 * 1000))
);

AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class);
List<StateStats> stateStatsList = result.getMappedResults();

前述のリストでは次のアルゴリズムが使用されています。

  1. 入力コレクションを state フィールドごとにグループ化し、population フィールドの合計を計算して、結果を新しいフィールド "totalPop" に格納します。

  2. "totalPop" フィールドに加えて、前のグループ操作の ID 参照によって中間結果を昇順に並べ替えます。

  3. Criteria クエリを引数として受け入れる match 操作を使用して、中間結果をフィルター処理します。

入力コレクションの名前は、newAggregation メソッドの最初のパラメーターとして渡される ZipInfo クラスから派生することに注意してください。

集約 フレームワークの例 4

この例では、射影演算での単純な算術演算の使用を示します。

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .and("netPrice").plus(1).as("netPricePlus1")
        .and("netPrice").minus(1).as("netPriceMinus1")
        .and("netPrice").multiply(1.19).as("grossPrice")
        .and("netPrice").divide(2).as("netPriceDiv2")
        .and("spaceUnits").mod(2).as("spaceUnitsMod2")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

入力コレクションの名前は、newAggregation メソッドの最初のパラメーターとして渡される Product クラスから派生することに注意してください。

集約 フレームワークの例 5

この例では、射影演算で SpEL 式から派生した単純な算術演算を使用する方法を示します。

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("netPrice + 1").as("netPricePlus1")
        .andExpression("netPrice - 1").as("netPriceMinus1")
        .andExpression("netPrice / 2").as("netPriceDiv2")
        .andExpression("netPrice * 1.19").as("grossPrice")
        .andExpression("spaceUnits % 2").as("spaceUnitsMod2")
        .andExpression("(netPrice * 0.8  + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge")

);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

集約 フレームワークの例 6

この例では、射影演算で SpEL 式から派生した複雑な算術演算を使用する方法を示します。

メモ: addExpression メソッドに渡される追加パラメーターは、その位置に応じてインデクサー式を使用して参照できます。この例では、パラメーター配列の最初のパラメーターを [0] で参照します。SpEL 式が MongoDB 集計フレームワーク式に変換されると、外部パラメーター式はそれぞれの値に置き換えられます。

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

double shippingCosts = 1.2;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("(netPrice * (1-discountRate)  + [0]) * (1+taxRate)", shippingCosts).as("salesPrice")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

SpEL 式内でドキュメントの他のフィールドを参照することもできることに注意してください。

集約 フレームワークの例 7

この例では、条件付き射影を使用します。$cond リファレンスドキュメント (英語) から派生したものです。

public class InventoryItem {

  @Id int id;
  String item;
  String description;
  int qty;
}

public class InventoryItemProjection {

  @Id int id;
  String item;
  String description;
  int qty;
  int discount
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
  project("item").and("discount")
    .applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
      .then(30)
      .otherwise(20))
    .and(ifNull("description", "Unspecified")).as("description")
);

AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, "inventory", InventoryItemProjection.class);
List<InventoryItemProjection> stateStatsList = result.getMappedResults();

このワンステップ集計では、inventory コレクションによる射影操作が使用されます。250 以上の qty を持つすべての在庫品目に対して条件付き操作を使用して、discount フィールドを射影します。2 番目の条件付き射影が description フィールドに対して実行されます。Unspecified 記述は、description フィールドがないすべての項目、または null 記述がある項目に適用されます。

MongoDB 3.6 以降では、条件式を使用して射影からフィールドを除外することができます。

例 7: 条件付き集計射影
TypedAggregation<Book> agg = Aggregation.newAggregation(Book.class,
  project("title")
    .and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle")     (1)
        .equalToValue(""))                                                          (2)
        .then("$$REMOVE")                                                           (3)
        .otherwiseValueOf("author.middle")                                          (4)
    )
	.as("author.middle"));
1 フィールド author.middle の値が
2 値が含まれていない
3 次に、$$REMOVE (英語) を使用してフィールドを除外します。
4 それ以外の場合は、フィールド値 author.middle を追加します。