sql >> データベース >  >> NoSQL >> MongoDB

Spring Data MongoDB:プロジェクションとアグリゲーション

    1。概要

    Spring Data MongoDBは、MongoDBネイティブクエリ言語にシンプルで高レベルの抽象化を提供します。この記事では、プロジェクションとアグリゲーションフレームワークのサポートについて説明します。

    このトピックを初めて使用する場合は、紹介記事「SpringDataMongoDBの概要」を参照してください。

    2。投影

    MongoDBでは、プロジェクションは、データベースからドキュメントの必須フィールドのみをフェッチする方法です。これにより、データベースサーバーからクライアントに転送する必要のあるデータの量が減り、パフォーマンスが向上します。

    Spring Data MongDBを使用すると、プロジェクションは MongoTemplateの両方で使用できます。 およびMongoRepository。

    先に進む前に、使用するデータモデルを見てみましょう。

    @Document
    public class User {
        @Id
        private String id;
        private String name;
        private Integer age;
        
        // standard getters and setters
    }

    2.1。 MongoTemplateを使用したプロジェクション

    include() およびexclude() フィールドのメソッド クラスは、フィールドをそれぞれ含める場合と除外する場合に使用されます。

    Query query = new Query();
    query.fields().include("name").exclude("id");
    List<User> john = mongoTemplate.find(query, User.class);

    これらのメソッドをチェーン化して、複数のフィールドを含めたり除外したりできます。 @Idとしてマークされたフィールド ( _id データベース内)は、明示的に除外されていない限り、常にフェッチされます。

    除外されたフィールドはnull レコードがプロジェクションでフェッチされるときのモデルクラスインスタンス。フィールドがプリミティブ型またはそのラッパークラスである場合、除外されたフィールドの値はプリミティブ型のデフォルト値です。

    たとえば、文字列 null int /整数 0になります およびboolean /ブール値 false

    したがって、上記の例では、 name フィールドはJohnになります 、 id null および年齢 0になります。

    2.2。 MongoRepositoryを使用したプロジェクション

    MongoRepositoriesを使用している間、フィールド @Query 注釈はJSON形式で定義できます:

    @Query(value="{}", fields="{name : 1, _id : 0}")
    List<User> findNameAndExcludeId();

    結果は、MongoTemplateを使用した場合と同じになります。 value =” {}” フィルタがないことを示しているため、すべてのドキュメントがフェッチされます。

    3。 集約

    MongoDBの集計は、データを処理して計算結果を返すために構築されました。データは段階的に処理され、1つの段階の出力が次の段階への入力として提供されます。変換を適用し、段階的にデータに対して計算を行うこの機能により、集計は分析のための非常に強力なツールになります。

    Spring Data MongoDBは、3つのクラス Aggregation を使用して、ネイティブ集計クエリの抽象化を提供します。 集計クエリAggregationOperationをラップします 個々のパイプラインステージとAggregationResultsをラップします これは、集計によって生成された結果のコンテナです。

    実行して集計するには、まず、 Aggregationで静的ビルダーメソッドを使用して集計パイプラインを作成します クラス、次に Aggregationのインスタンスを作成します newAggregation()を使用する Aggregationのメソッド クラスを作成し、最後に MongoTemplateを使用して集計を実行します :

    MatchOperation matchStage = Aggregation.match(new Criteria("foo").is("bar"));
    ProjectionOperation projectStage = Aggregation.project("foo", "bar.baz");
            
    Aggregation aggregation 
      = Aggregation.newAggregation(matchStage, projectStage);
    
    AggregationResults<OutType> output 
      = mongoTemplate.aggregate(aggregation, "foobar", OutType.class);

    両方のMatchOperation およびProjectionOperation AggregationOperationを実装します 。他の集約パイプラインにも同様の実装があります。 OutType 期待される出力のデータモデルです。

    次に、主要な集計パイプラインと演算子をカバーするために、いくつかの例とその説明を見ていきます。

    この記事で使用するデータセットには、MongoDBリポジトリからダウンロードできる米国内のすべての郵便番号の詳細がリストされています。

    zips というコレクションにインポートした後、サンプルドキュメントを見てみましょう。 テストで データベース。

    {
        "_id" : "01001",
        "city" : "AGAWAM",
        "loc" : [
            -72.622739,
            42.070206
        ],
        "pop" : 15338,
        "state" : "MA"
    }

    わかりやすくするため、またコードを簡潔にするために、次のコードスニペットでは、すべての静的を想定します。 集計の方法 クラスは静的にインポートされます。

    3.1。人口の降順で人口が1,000万人を超えるすべての州を取得する

    ここに3つのパイプラインがあります:

    1. $ group すべての郵便番号の人口を合計する段階
    2. $ match 人口が1,000万人を超える州を除外する段階
    3. $ sort 人口の降順ですべてのドキュメントを並べ替えるステージ

    期待される出力には、フィールド _idがあります。 状態およびフィールドとしてstatePop 州の総人口と。このためのデータモデルを作成し、集計を実行してみましょう:

    public class StatePoulation {
     
        @Id
        private String state;
        private Integer statePop;
     
        // standard getters and setters
    }

    @Id アノテーションは_idをマッピングします 出力から状態までのフィールド モデル内:

    GroupOperation groupByStateAndSumPop = group("state")
      .sum("pop").as("statePop");
    MatchOperation filterStates = match(new Criteria("statePop").gt(10000000));
    SortOperation sortByPopDesc = sort(Sort.by(Direction.DESC, "statePop"));
    
    Aggregation aggregation = newAggregation(
      groupByStateAndSumPop, filterStates, sortByPopDesc);
    AggregationResults<StatePopulation> result = mongoTemplate.aggregate(
      aggregation, "zips", StatePopulation.class);

    AggregationResults クラスはIterableを実装します したがって、それを繰り返して結果を印刷することができます。

    出力データモデルが不明な場合は、標準のMongoDBクラス Document 使用できます。

    3.2。平均的な都市人口で最小の州を取得する

    この問題には、次の4つの段階が必要です。

    1. $ group 各都市の総人口を合計する
    2. $ group 各州の平均人口を計算するには
    3. $ sort 州を平均都市人口の昇順で並べ替える段階
    4. $ limit 平均都市人口が最も少ない最初の州を取得する

    必ずしも必須ではありませんが、追加の $ projectを使用します StatePopulationに従ってドキュメントを再フォーマットするステージ データモデル。

    GroupOperation sumTotalCityPop = group("state", "city")
      .sum("pop").as("cityPop");
    GroupOperation averageStatePop = group("_id.state")
      .avg("cityPop").as("avgCityPop");
    SortOperation sortByAvgPopAsc = sort(Sort.by(Direction.ASC, "avgCityPop"));
    LimitOperation limitToOnlyFirstDoc = limit(1);
    ProjectionOperation projectToMatchModel = project()
      .andExpression("_id").as("state")
      .andExpression("avgCityPop").as("statePop");
    
    Aggregation aggregation = newAggregation(
      sumTotalCityPop, averageStatePop, sortByAvgPopAsc,
      limitToOnlyFirstDoc, projectToMatchModel);
    
    AggregationResults<StatePopulation> result = mongoTemplate
      .aggregate(aggregation, "zips", StatePopulation.class);
    StatePopulation smallestState = result.getUniqueMappedResult();

    この例では、最終段階で出力ドキュメントの数を1に制限しているため、結果に含まれるドキュメントは1つだけであることがすでにわかっています。そのため、 getUniqueMappedResult()を呼び出すことができます。 必要なStatePopulationを取得するには インスタンス。

    もう1つの注意点は、 @Id に依存するのではなく、 マップするアノテーション_id 言うまでもなく、私たちは投影段階で明示的にそれを行いました。

    3.3。最大および最小の郵便番号で州を取得する

    この例では、次の3つの段階が必要です。

    1. $ group 各州の郵便番号の数を数える
    2. $ sort 郵便番号の数で州を並べ替える
    3. $ group $ first を使用して、最大および最小の郵便番号で州を検索します および$last オペレーター
    GroupOperation sumZips = group("state").count().as("zipCount");
    SortOperation sortByCount = sort(Direction.ASC, "zipCount");
    GroupOperation groupFirstAndLast = group().first("_id").as("minZipState")
      .first("zipCount").as("minZipCount").last("_id").as("maxZipState")
      .last("zipCount").as("maxZipCount");
    
    Aggregation aggregation = newAggregation(sumZips, sortByCount, groupFirstAndLast);
    
    AggregationResults<Document> result = mongoTemplate
      .aggregate(aggregation, "zips", Document.class);
    Document document= result.getUniqueMappedResult();

    ここではモデルを使用していませんが、ドキュメントを使用しています すでにMongoDBドライバーで提供されています。


    1. Redisでソートされたセットの範囲から新しいセットを作成する

    2. .save()とupdate()の使用のマングースの違い

    3. Spring Boot+Redis-奇妙なキーを生成する

    4. DynamoDBとMongoDBNoSQL