GraphQLクエリの最適化

メモ

これらの最適化の推奨事項を適用する前に、最高のパフォーマンスを実現するには、GraphQL フィルタリングでのページングと並べ替えの際にコンテンツフラグメントを更新することを検討してください。

同じモデルを共有するコンテンツフラグメントが多数ある AEM インスタンスでは、GraphQL のリストクエリに(リソースの点で)コストがかかる場合があります。

これは、GraphQL クエリ内で使用されているモデルを共有する​すべての​フラグメントを、メモリに読み込む必要があるためです。これは時間とメモリの両方を消費します。 結果セット全体をメモリに読み込んだ​後に​のみ、(最終的な)結果セット内の項目数を減らす可能性のあるフィルタリングを適用できます。

これにより、小さな結果セットでもパフォーマンスが低下するというインプレッションを与える可能性があります。ただし、実際には、フィルタリングを適用する前に内部で処理する必要があるので、初期結果セットのサイズが原因で速度が低下します。

パフォーマンスとメモリの問題を減らすには、この初期結果セットをできるだけ小さく保つ必要があります。

AEM には、GraphQL クエリを最適化する 2 つの方法があります。

各アプローチには、独自のユースケースと制限があります。 このドキュメントでは、ハイブリッドフィルタリングとページングに関する情報と、GraphQL クエリを最適化するベストプラクティスについて説明します。

ハイブリッドフィルタリング

ハイブリッドフィルタリングは、JCR フィルタリングと AEM フィルタリングを組み合わせます。

結果セットを AEM フィルタリング用のメモリに読み込む前に、(クエリ制約の形式で)JCR フィルターを適用します。 これは、JCR フィルターによって余分な結果が先に削除されるので、メモリに読み込まれる結果セットを減らすためです。

メモ

技術的な理由(柔軟性、フラグメントのネストなど)により、AEM はフィルタリング全体を JCR に委任できません。

この方法では、GraphQL フィルターが提供する柔軟性を維持しながら、可能な限り多くのフィルタリングを JCR に委任できます。

ページング

AEM の GraphQL では、次の 2 種類のページネーションに対応しています。

  • 制限/オフセットベースのページネーション
    これは、リストクエリに使用されます。これらは次の値で終わります
    List(例:articleList)。
    これを使用するには、最初に返す項目(offset)と返す項目の数(limit またはページサイズ)を指定する必要があります。

  • カーソルベースのページネーションfirst および after で表される)
    これにより、項目ごとに一意の ID が提供されます。「カーソル」とも呼ばれます。
    クエリでは、前のページの最後の項目のカーソルとページサイズ(返される項目の最大数)を指定します。

    カーソルベースのページネーションはリストベースのクエリのデータ構造内に収まらないので、AEM では Paginated クエリタイプ(例: articlePaginated)を導入しました。 使用するデータ構造とパラメーターは、GraphQL Cursor ConnectionSpecification に準拠します。

    メモ

    AEM は現在、前方ページングをサポートしています(after/first パラメーターを使用)。

    後方ページング(before/last パラメーターを使用)はサポートされていません。

並べ替え

並べ替えは、すべての並べ替え条件が最上位のフラグメントに関連している場合にのみ効率的です。

並べ替え順に、ネストされたフラグメントに配置された 1 つ以上のフィールドが含まれている場合、最上位モデルを共有するすべてのフラグメントをメモリに読み込む必要があります。 これにより、パフォーマンスが低下します。

メモ

最上位フィールドでの並べ替えも、(小さくても)パフォーマンスに影響を与えます。

ベストプラクティス

すべての最適化での主な目的は、初期結果セットを減らすことです。 ここに示すベストプラクティスで、その方法を説明します。 組み合わせることができます(推奨)。

最上位のプロパティのみをフィルター

現在、JCR レベルでのフィルタリングは、最上位のフラグメントに対してのみ機能します。

フィルターがネストされたフラグメントのフィールドに対応する場合、AEM はフォールバックして、基になるモデルを共有するすべてのフラグメントを(メモリに)読み込む必要があります。

最上位のフラグメントのフィールドとネストされたフラグメントのフィールドのフィルター式を、AND 演算子と組み合わせることで、このような GraphQL クエリを引き続き最適化できます。

コンテンツ構造の使用

AEM では、通常、リポジトリ構造を使用して、処理するコンテンツの範囲を絞り込むことをお勧めします。

この方法は、GraphQL クエリにも適用する必要があります。

これを実行するには、最上位フラグメントの _path フィールドにフィルターを適用します。

{
  someList(filter: {
    _path: {
      _expressions: [
        {
          value: "/content/dam/some/sub/path/",
          _operator: STARTS_WITH
        }
      ]
    }
  }) {
    items {
      # ...
    }
  }
}
メモ

最高のパフォーマンスを得るには、value の末尾に / を付ける必要があります。

ページングの使用

ページングを使用して、初期の結果セットを減らすこともできます(特に、リクエストでフィルタリングと並べ替えを使用しない場合)。

ネストされたフラグメントをフィルターまたは並べ替える場合でも、AEM は大量のフラグメントをメモリに読み込む必要があるので、ページ分割されたクエリの処理に時間がかかる場合があります。 したがって、フィルタリングとページングを組み合わせる場合は、(前述のように)フィルタリングのルールを考慮してください。

ページングの場合、ページ分割された結果が常に明示的または暗黙的に並べ替えられるので、並べ替えも同様に重要です。

最初の数ページのみを取得したい場合、...List クエリと ...Paginated クエリの使用に大きな違いはありません。 ただし、アプリケーションで 1~2 ページ以上のページを読みたい場合は、...Paginated クエリを使用することをお勧めします。後のページで、パフォーマンスが著しく向上します。

フィルター式の論理演算

ネストされたフラグメントをフィルタリングする場合でも、JCR フィルタリングを活用できます。AND 演算子を使用して組み合わされた最上位フィールドに付随するフィルターを指定して行います。

一般的なユースケースは、最上位フラグメントの _path フィールドでフィルターを使用してクエリの範囲を制限し、最上位またはネストされたフラグメント上の追加フィールドでフィルタリングすることです。

この場合、様々なフィルター式が AND で組み合わされます。したがって、_path のフィルターにより、初期の結果セットが効果的に制限されます。 最上位フィールドのその他すべてのフィルターも、AND で組み合わせた場合を除き、初期結果セットを減らすのに役立ちます。

ネストされたフラグメントが含まれている場合、OR で組み合わされたフィルター式を最適化できません。OR 式は、ネストされたフラグメントが含まれて​いない​場合にのみ最適化できます。

複数行のテキストフィールドに対するフィルタリングの回避

複数行のテキストフィールド(html、markdown、plaintext、json)のフィールドは、JCR クエリでフィルタリングできません。これらのフィールドの内容をその場で計算する必要があるからです。

それでも複数行のテキストフィールドに対してフィルタリングする必要がある場合は、フィルター式を追加して、初期の結果セットのサイズを制限し、AND と組み合わせることを検討してください。_path フィールドに対してフィルタリングして範囲を制限することも、適切なアプローチです。

仮想フィールドに対するフィルタリングの回避

仮想フィールド(_ で始まるほとんどのフィールド)は、GraphQL クエリの実行中に計算されるので、JCR ベースのフィルタリングの範囲外です。

重要な例外は _path フィールです。コンテンツが適切に構造化されている場合は、初期結果セットのサイズを効果的に削減するために使用できます(コンテンツ構造の使用を参照)。

:フィルタリング除外

JCR レベルでフィルター式を評価できない場合が他にもいくつかあります(したがって、最高のパフォーマンスを実現するには回避する必要があります)。

  • _sensitiveness フィルターオプションを使用し、_sensitiveness0.0 以外に設定されている Float 値の式をフィルタリングします。

  • _ignoreCase フィルターオプションを使用して、String 値の式をフィルタリングします。

  • null 値のフィルタリング。

  • _apply: ALL_OR_EMPTY を使用して配列をフィルタリングします。

  • _apply: INSTANCES_instances: 0 を使用して配列をフィルタリングします。

  • CONTAINS_NOT 演算子を使用して式をフィルタリングします。

  • NOT_AT 演算子を使用する CalendarDate または Time 値の式をフィルタリングします。

このページ