

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# ベストプラクティス: Neptune を最大限に活用する
<a name="best-practices"></a>

以下に、Amazon Neptune を使用するための一般的な推奨事項をいくつか示します。Amazon Neptune を使用してパフォーマンスを最大にするための推奨事項をすばやく検索できるリファレンスとしてご使用ください。

**Contents**
+ [Amazon Neptune 基本操作ガイドライン](best-practices-general-basic.md)
  + [Amazon Neptune セキュリティベストプラクティス](best-practices-general-security.md)
  + [クラスターで異なるインスタンスクラスを避ける](best-practices-general-basic.md#best-practices-loader-heterogeneous-instances)
  + [一括ロード中の再起動の繰り返しの回避](best-practices-general-basic.md#best-practices-loader-repeated-restarts)
  + [多数の述語がある場合は、OSPG インデックスを有効にします。](best-practices-general-basic.md#best-practices-general-predicates)
  + [可能な限り、実行時間が長いトランザクションを避ける](best-practices-general-basic.md#best-practices-general-long-running-transactions)
  + [Neptune メトリクスを使用するベストプラクティス](best-practices-general-metrics.md)
  + [Neptune クエリのチューニングのベストプラクティス](best-practices-general-basic.md#best-practices-general-tuning)
  + [リードレプリカ全体の負荷分散](best-practices-general-basic.md#best-practices-general-loadbalance)
  + [一時的に大きなインスタンスを使用してロード時間を短縮](best-practices-general-basic.md#best-practices-loader-tempinstance)
  + [リードレプリカにフェイルオーバーしてライターインスタンスのサイズを変更する](best-practices-general-basic.md#best-practices-resize-instance)
  + [データプリフェッチタスク中断エラー後のアップロードの再試行](best-practices-general-basic.md#load-api-reference-status-interrupted)
+ [Neptune で Gremlin を使用するための一般的なベストプラクティス](best-practices-gremlin.md)
  + [Neptune Serverless のハートビート設定](best-practices-gremlin-heartbeat-serverless.md)
  + [DFE エンジンを活用するためのアップサートクエリの構築](best-practices-gremlin.md#best-practices-gremlin-upserts)
  + [Gremlin コードをデプロイするコンテキストでテストする](best-practices-gremlin-console-glv-differences.md)
  + [効率的なマルチスレッドの Gremlin 書き込みの作成](best-practices-gremlin-multithreaded-writes.md)
  + [作成時刻プロパティを使用したレコードの削除](best-practices-gremlin-prune.md)
  + [Groovy の時刻データに対する `datetime( )` メソッドの使用](best-practices-gremlin-datetime.md)
  + [GLV 時刻データのネイティブの日付と時刻の使用](best-practices-gremlin-datetime-glv.md)
+ [Neptune で Gremlin Java クライアントを使用するためのベストプラクティス](best-practices-gremlin-java-client.md)
  + [複数のスレッドにまたがってクライアントオブジェクトを再利用する](best-practices-gremlin-java-reuse.md)
  + [読み取りと書き込みエンドポイントに別々の Gremlin Java クライアントオブジェクトを作成する](best-practices-gremlin-java-separate.md)
  + [複数のリードレプリカエンドポイントを Gremlin Java 接続プールに追加する](best-practices-gremlin-java-multiple.md)
  + [接続制限を回避するためにクライアントを閉じる](best-practices-gremlin-java-close-connections.md)
  + [フェイルオーバー後の新しい接続の作成](best-practices-gremlin-java-new-connection.md)
  + [`maxInProcessPerConnection` と `maxSimultaneousUsagePerConnection` を使用し、値を同じに設定してください。](best-practices-gremlin-java-maxes.md)
  + [文字列ではなくバイトコードとしてサーバーにクエリを送信する](best-practices-gremlin-java-bytecode.md)
  + [クエリによって返った ResultSet または Iterator を常に完全に消費する](best-practices-gremlin-java-resultset.md)
  + [頂点とエッジをバッチで一括追加](best-practices-gremlin-java-batch-add.md)
  + [Java 仮想マシンで DNS キャッシュを無効にする](best-practices-gremlin-java-disable-dns-caching.md)
  + [クエリごとのレベルでタイムアウトを設定する (オプション)](best-practices-gremlin-java-per-query-timeout.md)
  + [`java.util.concurrent.TimeoutException` のトラブルシューティング](best-practices-gremlin-java-exceptions-TimeoutException.md)
+ [OpenCypher と Bolt を使用した Neptune のベストプラクティス](best-practices-opencypher.md)
  + [フェイルオーバー後の新しい接続の作成](best-practices-opencypher.md#best-practices-opencypher-renew-connection)
  + [存続期間の長いアプリケーションの接続処理](best-practices-opencypher.md#best-practices-opencypher-long-connections)
  + [の接続処理 AWS Lambda](best-practices-opencypher.md#best-practices-opencypher-lambda-connections)
  + [クエリでは双方向のエッジを優先する](best-practices-opencypher-directed-edges.md)
  + [Neptune は 1 つのトランザクションでの複数の同時クエリをサポートしていません。](best-practices-opencypher-multiple-queries.md)
  + [完了したら、ドライバーオブジェクトを閉じます](best-practices-opencypher-close-driver.md)
  + [読み取りと書き込みには明示的なトランザクションモードを使用してください。](best-practices-opencypher-use-explicit-txs.md)
    + [読み取り専用トランザクション](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-read-txs)
    + [ミューテーショントランザクション](best-practices-opencypher-use-explicit-txs.md#best-practices-opencypher-mutation-txs)
  + [例外の場合の再試行ロジック](best-practices-opencypher-retry-logic.md)
  + [1 つの SET 句を使用して複数のプロパティを一度に設定する](best-practices-content-0.md)
    + [SET 句を使用して複数のプロパティを一度に削除する](best-practices-content-0.md#best-practices-content-1)
  + [パラメータ化されたクエリを使用する](best-practices-content-2.md)
  + [UNWIND 句でネストされたマップの代わりにフラット化されたマップを使用する](best-practices-content-3.md)
  + [可変長パス (VLP) 式で、より制限の厳しいノードを左側に配置する](best-practices-content-4.md)
  + [詳細な関係名を使用してノードラベルの冗長チェックを回避する](best-practices-content-5.md)
  + [可能な場合はエッジラベルを指定する](best-practices-content-6.md)
  + [可能な場合は WITH 句を使用しない](best-practices-content-7.md)
  + [制限付きフィルターをクエリのできるだけ早い段階で配置する](best-practices-content-8.md)
  + [プロパティが存在するかどうかを明示的に確認する](best-practices-content-9.md)
  + [名前付きパスを使用しない (必須でない限り)](best-practices-content-10.md)
  + [COLLECT(DISTINCT()) を避ける](best-practices-content-11.md)
  + [すべてのプロパティ値を取得するときに、個々のプロパティルックアップよりもプロパティ関数を優先する](best-practices-content-12.md)
  + [クエリの外部で静的計算を実行する](best-practices-content-13.md)
  + [個々のステートメントの代わりに UNWIND を使用したバッチ入力をする](best-practices-content-14.md)
  + [ノード/リレーションシップにカスタム ID を使用する](best-practices-content-15.md)
  + [クエリで \$1id 計算を実行しない](best-practices-content-16.md)
  + [複数のノードの更新/移行](best-practices-merge-multiple-nodes.md)
+ [SPARQL を使用した Neptune のベストプラクティス](best-practices-sparql.md)
  + [デフォルトですべての名前が付いたグラフのクエリの実行](best-practices-sparql-query.md)
  + [ロード用に名前付きのグラフを指定する](best-practices-sparql-graph.md)
  + [クエリで FILTER、FILTER...IN、および VALUES を選択する](best-practices-sparql-batch.md)

# Amazon Neptune 基本操作ガイドライン
<a name="best-practices-general-basic"></a>

以下に示しているのは、基本的な運用についてのガイドラインであり、Neptune の使用時にユーザーが従う必要があります。
+ Neptune DB インスタンスを理解して、パフォーマンスおよびユースケースの要件に合わせて適切なサイズを設定できるようにします。「[Amazon Neptune DB クラスターとインスタンス](feature-overview-db-clusters.md)」を参照してください。
+ CPU、メモリの使用状況をモニタリングする。これは、必要なクエリのパフォーマンスを達成するために、より強力な CPU やメモリ容量を持つ DB インスタンスクラスにいつ移行するべきかを知るのに役立ちます。Amazon CloudWatch は、使用パターンが変更されたり、デプロイメントの最大容量に近づいたりすると、通知するように設定できます。これにより、システムのパフォーマンスと可用性を維持するのに役立ちます。詳細については、「[インスタンスのモニタリング](feature-overview-db-clusters.md#feature-overview-monitoring-instances)」と「[Neptune のモニタリング](monitoring.md)」を参照してください。

  Neptune には独自のメモリマネージャーがあるため、CPU 使用率が高い場合でもメモリ使用率は比較的低いのが一般的です。クエリ実行時にメモリ不足の例外に遭遇するのは、空きメモリを増やす必要があることを示す最も良い目安です。
+ 自動バックアップを有効にして、都合の良いときにバックアップウィンドウを設定します。
+ DB インスタンスのフェイルオーバーをテストすることで、そのプロセスでユースケースにかかる時間を把握します。また、DB インスタンスにアクセスするアプリケーションがフェイルオーバー後に新しい DB インスタンスに自動的に接続できるようにします。
+ 可能であれば、クライアントと Neptune クラスターを同じリージョンと VPC で実行します。VPC ピアリングを使用したクロスリージョン接続では、クエリの応答時間に遅延が生じる可能性があるためです。一桁のミリ秒のクエリ応答の場合、クライアントと Neptune クラスターを同じリージョンと VPC に保持する必要があります。
+ リードレプリカインスタンスを作成するときは、少なくともプライマリライターインスタンスと同じ大きさにする必要があります。これにより、レプリケーションの遅延がチェックされ、レプリカの再起動が回避されます。「[クラスターで異なるインスタンスクラスを避ける](#best-practices-loader-heterogeneous-instances)」を参照してください。
+ 新しいメジャーエンジンバージョンにアップグレードする前に、必ずそのエンジンでアプリケーションをテストしてください。これを行うには、DB クラスターをクローンしてクローンクラスターで新しいエンジンバージョンを実行し、そのクローンでアプリケーションをテストします。
+ フェイルオーバーを容易にするために、すべてのインスタンスを同じサイズにするのが理想的です。

**Topics**
+ [Amazon Neptune セキュリティベストプラクティス](best-practices-general-security.md)
+ [クラスターで異なるインスタンスクラスを避ける](#best-practices-loader-heterogeneous-instances)
+ [一括ロード中の再起動の繰り返しの回避](#best-practices-loader-repeated-restarts)
+ [多数の述語がある場合は、OSPG インデックスを有効にします。](#best-practices-general-predicates)
+ [可能な限り、実行時間が長いトランザクションを避ける](#best-practices-general-long-running-transactions)
+ [Neptune メトリクスを使用するベストプラクティス](best-practices-general-metrics.md)
+ [Neptune クエリのチューニングのベストプラクティス](#best-practices-general-tuning)
+ [リードレプリカ全体の負荷分散](#best-practices-general-loadbalance)
+ [一時的に大きなインスタンスを使用してロード時間を短縮](#best-practices-loader-tempinstance)
+ [リードレプリカにフェイルオーバーしてライターインスタンスのサイズを変更する](#best-practices-resize-instance)
+ [データプリフェッチタスク中断エラー後のアップロードの再試行](#load-api-reference-status-interrupted)

# Amazon Neptune セキュリティベストプラクティス
<a name="best-practices-general-security"></a>

 AWS Identity and Access Management (IAM) アカウントを使用して、Neptune API アクションへのアクセスを制御します。特に、DB インスタンス、セキュリティグループ、オプショングループ、またはパラメータグループなどの Neptune リソースを作成、変更、削除するアクションが対象になります。DB インスタンスのバックアップや復元などの一般的な管理操作を実行するアクションも対象になります。
+ 可能な限り、永続的な認証情報ではなく一時的な認証情報を使用してください。
+ Amazon Relational Database Service (Amazon RDS) リソースを管理する各ユーザーにそれぞれの IAM アカウントを割り当てます。 AWS アカウントのルートユーザーを使用して Neptune リソースを管理しないでください。お客様を含めて全員に IAM ユーザーを作成します。
+ それぞれの職務の実行に最低限必要になる一連のアクセス許可を各ユーザーに付与します。
+ IAM グループを使用して、複数のユーザーのアクセス許可を効果的に管理します。
+ IAM 認証情報のローテーションを定期的に行います。

タグ付けを使用して Neptune リソースへのアクセスを制限する方法については、[Amazon Neptune データベースの保護](security.md)を参照してください。IAM の使用に関する一般的な情報については、[AWS Identity and Access Management](https://docs.aws.amazon.com/IAM/latest/UserGuide/Welcome.html)および*IAM ユーザーガイド*の[IAM ベストプラクティス](https://docs.aws.amazon.com/IAM/latest/UserGuide/IAMBestPractices.html)を参照してください。

## クラスターで異なるインスタンスクラスを避ける
<a name="best-practices-loader-heterogeneous-instances"></a>

DB クラスターに異なるクラスのインスタンスが含まれている場合、時間の経過とともに問題が発生する可能性があります。最も一般的な問題は、レプリケーションのラグが原因で、小さなリーダーインスタンスが再起動を繰り返すサイクルに入る可能性があることです。リーダーノードの DB インスタンスクラスの設定が、ライター DB インスタンスの設定よりも弱い場合、変更のボリュームが大きすぎてリーダーが追いつくことができません。

**重要**  
レプリケーションラグによる再起動が繰り返されないようにするには、すべてのインスタンスが同じインスタンスクラス (サイズ) を持つように DB クラスターを構成します。

ライタインスタンス (プライマリ) と DB クラスター内のリーダー間の遅延は、Amazon CloudWatch のメトリクスの `ClusterReplicaLag` メトリクスを使って確認できます。`VolumeWriteIOPs` メトリクスでは、レプリケーションラグが発生する可能性のあるクラスター内の書き込みアクティビティのバーストを検出することもできます。

## 一括ロード中の再起動の繰り返しの回避
<a name="best-practices-loader-repeated-restarts"></a>

一括ロード中のレプリケーション遅延が原因で、リードレプリカが繰り返し再起動されるサイクルが発生した場合、レプリカは DB クラスター内のライターに追いつけない可能性があります。

リーダーをライターよりも大きくするか、一括ロード中にリーダーを一時的に削除し、完了後に再作成してください。

## 多数の述語がある場合は、OSPG インデックスを有効にします。
<a name="best-practices-general-predicates"></a>

データモデルに Distinct 述語が多数含まれていると (たいていは 1000 以上)、パフォーマンスが低下し、運用コストが高くなる可能性があります。

その場合は、[OSPG インデックス](feature-overview-storage-indexing.md#feature-overview-storage-indexing-osgp)を有効にしてパフォーマンスを改善できます。「[OSGP インデックス](features-lab-mode.md#features-lab-mode-features-osgp-index)」を参照してください。

## 可能な限り、実行時間が長いトランザクションを避ける
<a name="best-practices-general-long-running-transactions"></a>

実行時間が長いトランザクション (読み取り専用または読み取り/書き込み) は、次の種類の予期しない問題を引き起こす可能性があります。

読み取りインスタンスまたは同時書き込みがあるライターインスタンスで実行時間が長いトランザクションは、異なるバージョンのデータが大量に蓄積される可能性があります。これにより、結果の大部分を除外する読み取りクエリのレイテンシーが高くなる可能性があります。

場合によっては、時間の経過とともに蓄積されたバージョンによって、新しい書き込みがスロットルされることがあります。

多くの書き込みを伴う実行時間が長い読み取り/書き込みトランザクションは、インスタンスが再起動した場合に問題を引き起こす可能性があります。インスタンスがメンテナンスイベントまたはクラッシュから再起動すると、コミットされていない書き込みはすべてロールバックされます。このような元に戻す操作は通常、バックグラウンドで実行され、インスタンスの復帰をブロックしませんが、ロールバックされる操作と競合する新しい書き込みは失敗します。

たとえば、前回の実行で接続が切断された後に同じクエリが再試行された場合、インスタンスの再起動時に失敗することがあります。

元に戻す操作に必要な時間は、関連する変更のサイズに比例します。

# Neptune メトリクスを使用するベストプラクティス
<a name="best-practices-general-metrics"></a>

リソース不足やその他の一般的なボトルネックによるパフォーマンスの問題を特定するには、Neptune DB クラスターに適用されるメトリクスをモニタリングできます。

パフォーマンスメトリクスを定期的に監視して、さまざまな時間範囲の平均値、最大値、最小値に関するデータを収集します。これは、いつパフォーマンスが低下しているかを特定するうえで有効です。このデータを使用して、特定のメトリクスしきい値に対して Amazon CloudWatch アラームを設定することにより、しきい値に達した場合に警告されるようにすることができます。

新しい DB クラスターをセットアップし、一般的なワークロードで実行するときは、さまざまな間隔 (1 時間、24 時間、1 週間、2 週間など) でのすべてのパフォーマンスメトリクスの平均値、最大値、最小値を収集します。これにより、正常な状態を把握することができます。それにより、オペレーションのピークおよびオフピークの時間帯を比較して、得られた情報から、いつパフォーマンスが標準レベルを下回っているかを特定し、それに応じてアラームを設定できます。

Neptune メトリクスの表示方法の詳細については、[Amazon CloudWatch を使用した Neptune のモニタリング](cloudwatch.md)を参照してください。

開始時に最も重要なメトリクスは次のとおりです。
+ **BufferCacheHitRatio** — バッファキャッシュから提供されたリクエストの割合 (パーセント)。キャッシュミスにより、クエリの実行に大きなレイテンシーが追加されます。キャッシュヒット率が 99.9% を下回っており、アプリケーションでレイテンシーが問題になる場合は、より多くのデータをメモリにキャッシュするようにインスタンスタイプをアップグレードすることを検討してください。
+ **CPU 使用率** - 使用されているコンピュータの処理能力の割合。クエリパフォーマンスの目標によっては、CPU 使用率に大きな値を設定することをお勧めします。
+ **Freeable memory** — DB インスタンスで使用可能な RAM の量 (メガバイト単位)。Neptune には独自のメモリマネージャーがあるため、このメトリクスは予想よりも低くなる可能性があります。インスタンスクラスをより多くの RAM を持つクラスにアップグレードすることを検討する必要があるという良い兆候となるのは、クエリが頻繁にメモリ不足の例外をスローする場合です。

[**モニタリング**] タブのメトリクスの CPU、メモリ、メトリクスの 75% 地点に赤い線でマークされています。インスタンスのメモリ消費が頻繁にこの限界を超える場合は、ワークロードを確認し、クエリのパフォーマンスを向上させるためにインスタンスをアップグレードすることを検討してください。

## Neptune クエリのチューニングのベストプラクティス
<a name="best-practices-general-tuning"></a>

 Neptune のパフォーマンスを向上させるには、大量のリソースを消費する使用頻度の最も高いクエリをチューニングして、実行コストを下げることをお勧めします。

Gremlin クエリを調整する方法の詳細については、[Gremlin クエリヒント](gremlin-query-hints.md) および [Gremlin クエリのチューニング](gremlin-traversal-tuning.md)を参照してください。SPARQL クエリを調整する方法の詳細については、「[SPARQL クエリヒント](sparql-query-hints.md)」を参照してください。

## リードレプリカ全体の負荷分散
<a name="best-practices-general-loadbalance"></a>

読み込みエンドポイントのラウンドロビンルーティングを実行するには、DNS エントリがポイントするホストを変更します。WebSocket 接続は長期間存続し続けることが多いので、クライアントは新しい接続を作成し、DNS レコードを解決して新しいリードレプリカへの接続を取得する必要があります。

連続するリクエストに対して異なるリードレプリカを取得するには、クライアントが接続するたびに DNS エントリを解決するようにします。このためには、接続を終了し、リーダーエンドポイントに再接続する必要があります。

インスタンスエンドポイントに明示的に接続することで、リードレプリカ全体で負荷を分散することもできます。

## 一時的に大きなインスタンスを使用してロード時間を短縮
<a name="best-practices-loader-tempinstance"></a>

大きなインスタンスサイズで、ロードパフォーマンスが向上します。大きなインスタンスタイプを使用していないが、ロード速度を向上させる場合は、大きいインスタンスを使用してロードし、削除することができます。

**注記**  
次の手順は新しいクラスターを対象としています。既存のクラスターがある場合は、新しい大きなインスタンスを追加して、プライマリ DB インスタンスに昇格させることができます。

**大きいインスタンスサイズを使用してデータをロードするには**

1.  単一の `r5.12xlarge` インスタンスでクラスターを作成します。このインスタンスはプライマリ DB インスタンスです。

1. 同じサイズのリードレプリカを 1 つ以上作成します (`r5.12xlarge`)。

   リードレプリカは小さいサイズで作成できますが、プライマリインスタンスによる書き込みに追いつくのに十分な大きさでない場合は、頻繁に再起動する必要があります。その結果、ダウンタイムによってパフォーマンスが大幅に低下します。

1. Bulk Loader コマンドで、`“parallelism” : “OVERSUBSCRIBE”` を含め、Neptune に利用可能なすべての CPU リソースをロードに使用するように指示します ([Neptune ローダーのリクエストパラメータ](load-api-reference-load.md#load-api-reference-load-parameters) 参照)。ロードオペレーションは I/O が許す限り高速に進み、通常は CPU リソースの 60 ～ 70% を必要とします。

1. Neptune ローダーを使用してデータをロードします。ロードジョブはプライマリ DB インスタンスで実行されます。

1. データのロードが完了したら、追加料金や再起動の問題を繰り返さないように、クラスター内のすべてのインスタンスを同じインスタンスタイプにスケールダウンするようにしてください ([異なるインスタンスサイズを避ける](#best-practices-loader-heterogeneous-instances)参照)。

## リードレプリカにフェイルオーバーしてライターインスタンスのサイズを変更する
<a name="best-practices-resize-instance"></a>

ライターインスタンスを含む DB クラスター内のインスタンスのサイズを変更する最善の方法は、希望のサイズになるようにリードレプリカインスタンスを作成または変更し、そのリードレプリカに意図的にフェイルオーバーすることです。アプリケーションで見られるダウンタイムは、ライターの IP アドレスを変更するのに必要な時間だけであり、約 3 ～ 5 秒にする必要があります。

現在のライターインスタンスをリードレプリカインスタンスに意図的にフェイルオーバーするために使用する Neptune 管理 API は、[FailoverDBCluster](api-clusters.md#FailoverDBCluster) です。Gremlin Java クライアントを使用している場合は、[ここに](best-practices-gremlin-java-new-connection.md)述べるように、フェイルオーバー後に新しいクライアントオブジェクトを作成して、新しい IP アドレスを取得しなければならない場合があります。

以下で説明するように、再起動を繰り返すサイクルを回避するために、すべてのインスタンスを同じサイズに変更してください。

## データプリフェッチタスク中断エラー後のアップロードの再試行
<a name="load-api-reference-status-interrupted"></a>

バルクローダーを使用してデータを Neptune にロードするときに、`LOAD_FAILED` ステータスが発生し、詳細情報のリクエストのレスポンスで、次のような `PARSING_ERROR` および `Data prefetch task interrupted` メッセージが発生することがあります。

```
"errorLogs" : [
  {
    "errorCode" : "PARSING_ERROR",
    "errorMessage" : "Data prefetch task interrupted: Data prefetch task for 11467 failed",
    "fileName" : "s3://amzn-s3-demo-bucket/some-source-file",
    "recordNum" : 0
  }
]
```

このエラーが発生した場合は、バルクアップロードリクエストを再試行します。

このエラーが発生するのは、通常はリクエストやデータが原因で発生しない一時的な中断があった場合であり、一般的にはバルクアップロードリクエストを再実行することで解決できます。

デフォルト設定 (`"mode":"AUTO"` および `"failOnError":"TRUE"`) を使用している場合、ローダーは既に正常にロードされたファイルをスキップし、中断が発生したときにまだロードされていなかったファイルのロードを再開します。

# Neptune で Gremlin を使用するための一般的なベストプラクティス
<a name="best-practices-gremlin"></a>

Neptune で Gremlin グラフトラバーサル言語を使用する際は次の推奨事項に従います。Neptune での Gremlin 利用の詳細については、[Gremlin を使用した Neptune グラフへのアクセス](access-graph-gremlin.md)を参照してください。

**重要**  
TinkerPop バージョン 3.4.11 に変更が加えられ、クエリの処理方法の正確性が向上しましたが、現時点ではクエリのパフォーマンスに重大な影響を与える場合があります。  
たとえば、この種類のクエリの実行速度が大幅に遅くなる可能性があります。  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  out()
```
TinkerPop 3.4.11 の変更により、制限ステップの後の頂点は、最適ではない方法でフェッチされるようになりました。これを回避するには、barrier() ステップを `order().by()` の次の任意のポイントに追加して、クエリを変更できます。例:   

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  barrier().
  out()
```
TinkerPop 3.4.11 が Neptune [エンジンバージョン 1.0.5.0](engine-releases-1.0.5.0.md) で有効になりました。

**Topics**
+ [Neptune Serverless のハートビート設定](best-practices-gremlin-heartbeat-serverless.md)
+ [DFE エンジンを活用するためのアップサートクエリの構築](#best-practices-gremlin-upserts)
+ [Gremlin コードをデプロイするコンテキストでテストする](best-practices-gremlin-console-glv-differences.md)
+ [効率的なマルチスレッドの Gremlin 書き込みの作成](best-practices-gremlin-multithreaded-writes.md)
+ [作成時刻プロパティを使用したレコードの削除](best-practices-gremlin-prune.md)
+ [Groovy の時刻データに対する `datetime( )` メソッドの使用](best-practices-gremlin-datetime.md)
+ [GLV 時刻データのネイティブの日付と時刻の使用](best-practices-gremlin-datetime-glv.md)

# Neptune Serverless のハートビート設定
<a name="best-practices-gremlin-heartbeat-serverless"></a>

Neptune Serverless で Gremlin WebSocket クライアントを使用する場合は、スケーリングイベント中に安定した接続を維持するために、クライアントの ping 間隔を適切に設定する必要があります。Gremlin クライアントは WebSocket 接続を使用し、定期的な ping を送信して接続がアクティブであることを確認します。クライアントは、ping 間隔の期間内にサーバーからのレスポンスを期待します。サーバーが応答しない場合、クライアントは自動的に接続を閉じます。

Neptune で**プロビジョニングされた**インスタンスの場合、ping 間隔を **5 秒**に設定することをお勧めします。Neptune **Serverless クラスター**の場合、スケーリングオペレーション中の潜在的な遅延に対応するために、ping 間隔を少なくとも **20 秒**に設定することをお勧めします。このパラメータは、クライアントがサーバーへの書き込みから ping を送信して接続がまだアクティブであることを確認するまでの待機時間を制御します。

このパラメータの設定は、クライアントの実装によって異なります。

**Java クライアント設定**

Java TinkerPop Gremlin クライアントの場合は、 `keepAliveInterval`パラメータを設定します。

```
Cluster.Builder builder = Cluster.build()
    .addContactPoint(endpoint)
    .keepAliveInterval(20000); // Configure ping interval in milliseconds
```

Java ドライバー設定の詳細については、[Java TinkerPop ドキュメント](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java-configuration)を参照してください。

**Go クライアント設定**

Gremlin Go クライアントの場合は、 `KeepAliveInterval`パラメータを設定します。

```
rc, err := driver.NewDriverRemoteConnection(endpoint,
    func(settings *driver.DriverRemoteConnectionSettings) {
        settings.TraversalSource = "g"
        settings.AuthInfo = auth
        settings.KeepAliveInterval = 20 * time.Second // Configure ping interval
        ...
    })
```

Go ドライバー設定の詳細については、Go [ TinkerPop ドキュメント](https://tinkerpop.apache.org/docs/current/reference/#gremlin-go-configuration)を参照してください。

**JavaScript/Node.js クライアント設定**

JavaScript/Node.js Gremlin クライアントの場合は、 `pingInterval`パラメータを設定します。

```
const gremlin = require('gremlin');
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;

const connection = new DriverRemoteConnection(endpoint, {
    traversalSource: 'g',
    pingInterval: 20000  // Configure ping interval in milliseconds
});
```

JavaScript ドライバー設定の詳細については、[JavaScript TinkerPop ドキュメント](https://tinkerpop.apache.org/docs/current/reference/#gremlin-javascript-configuration)を参照してください。

**Python クライアント設定**

Python Gremlin クライアントの場合、ping 間隔は通常トランスポートレイヤーで管理されます。設定オプションについては、特定のトランスポート実装ドキュメントを参照してください。

```
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection

g = traversal().with_remote(
    DriverRemoteConnection('wss://your-neptune-endpoint:your-neptune-port/gremlin','g',
        transport_factory=lambda: AiohttpTransport(read_timeout=60,
                                                    write_timeout=20,
                                                    heartbeat=20, // Configure heartbeat
                                                    call_from_event_loop=True,
                                                    max_content_length=100*1024*1024,
                                                    ssl_options=ssl.create_default_context(Purpose.CLIENT_AUTH))))
```

Python ドライバー設定の詳細については、[Python TinkerPop ドキュメント](https://tinkerpop.apache.org/docs/current/reference/#gremlin-python-configuration)を参照してください。

この設定により、クライアントは Neptune Serverless スケーリングイベント中に接続の安定性を維持し、不要な接続の閉鎖を防ぎ、アプリケーションの信頼性が向上します。

## DFE エンジンを活用するためのアップサートクエリの構築
<a name="best-practices-gremlin-upserts"></a>

[Gremlin `mergeV()` および `mergeE()` ステップによる効率的なアップサートの実行](gremlin-efficient-upserts.md) DFE エンジンをできるだけ効果的に使用するようにアップサートクエリを構成する方法について説明します。

# Gremlin コードをデプロイするコンテキストでテストする
<a name="best-practices-gremlin-console-glv-differences"></a>

Gremlin では、クライアントがサーバーにクエリを送信する方法が複数あります。WebSocket または Bytecode GLV を使用するか、文字列ベースのスクリプトを使用して Gremlin コンソールを使用します。

Gremlin クエリの実行は、クエリの送信方法によって異なる可能性があることを認識することが重要です。空の結果を返すクエリは、バイトコードモードで送信された場合は成功したものとして扱われますが、スクリプトモードで送信された場合は失敗として扱われます。例えば、スクリプトモードのクエリに `next()` を含むと、`next()` はサーバに送信されますが、ByteCode を使用してクライアントは通常、`next()` 自体を処理します。最初のケースでは、結果が見つからなければクエリは失敗しますが、2 番目のケースでは、結果セットが空であるかどうかにかかわらず、クエリは成功します。

コードを 1 つのコンテキスト (たとえば、一般的にテキスト形式でクエリを送信する Gremlin コンソール) で開発およびテストし、別のコンテキスト (たとえば、Bytecode を使用して Java ドライバを介して) にコードをデプロイすると、コードの動作が開発環境では発生しなかった問題が本番環境で生じる可能性があります。

**重要**  
予期せぬ結果を避けるために、デプロイされる GLV コンテキストで Gremlin コードをテストしてください。

# 効率的なマルチスレッドの Gremlin 書き込みの作成
<a name="best-practices-gremlin-multithreaded-writes"></a>

Gremlin を使用して Neptune にデータをマルチスレッドでロードするためのガイドラインがいくつかあります。

可能な場合は、各スレッドに衝突しないように挿入または変更するための頂点またはエッジのセットを渡します。たとえば、スレッド 1 は 1 〜 50,000 の ID 範囲を、スレッド 2 は 50,001 〜 100,000 の ID 範囲を、というように続きます。これにより、`ConcurrentModificationException` が発生する可能性が低くなります。安全を期すために、すべての書き込みに `try/catch` ブロックを付けてください。失敗した場合は、しばらくしてから再試行できます。

一般に、50〜100 (頂点またはエッジ) の間のバッチサイズでの書き込みはうまく機能します。各頂点に追加されるプロパティがたくさんある場合は、100 よりも 50 に近い数が適しています。一部の実験は役立ちます。したがって、バッチオペレーションされた書き込みは次のようなものを使用できます。

```
g.addV(‘test’).property(id,’1’).as(‘a’).
  addV(‘test’).property(id,’2’).
  addE(‘friend’).to(‘a’).
```

これは、バッチオペレーションごとに繰り返し表示されます。

バッチを使用すると、サーバーへの Gremlin ラウンドトリップごとに 1 つの頂点またはエッジを追加するよりもはるかに効率的です。

言語バリアント (GLV) クライアントを使用している場合は、最初にトラバーサルを作成することによってプログラム的にバッチを作成できます。次にそれに追加して、最後にそれを繰り返します。次に例を示します。

```
  t.addV(‘test’).property(id,’1’).as(‘a’)
  t.addV(‘test’).property(id,’2’)
  t.addE(‘friend’).to(‘a’)
  t.iterate()
```

可能であれば Gremlin 言語バリアントクライアントを使用することをお勧めします。ただし、文字列を連結してバッチを構築することでクエリをテキスト文字列として送信するクライアントと同様のことを実行できます。

クエリに基本的な HTTP ではなく Gremlin クライアントライブラリのいずれかを使用している場合、スレッドはすべて同じクライアント、クラスター、または接続プールを共有する必要があります。最高のスループット (接続プールのサイズ、Gremlin クライアントが使用するワーカースレッドの数などの設定) を得るために設定を調整する必要がある場合もあります。

# 作成時刻プロパティを使用したレコードの削除
<a name="best-practices-gremlin-prune"></a>

頂点のプロパティとして作成時刻を保存し、定期的に削除することで古いレコードを取り除くことができます。

特定の期間データを保存した後にグラフから削除する必要がある場合 (頂点の有効期限)、頂点の作成時にタイムスタンププロパティを保存できます。その後、特定の時間の前に作成されたすべての頂点に対する `drop()` クエリを定期的に発行できます。次に例を示します。

```
g.V().has(“timestamp”, lt(datetime('2018-10-11')))
```

# Groovy の時刻データに対する `datetime( )` メソッドの使用
<a name="best-practices-gremlin-datetime"></a>

Neptune は、Gremlin **Groovy** バリアントで送信されるクエリの日付と時刻を指定する `datetime` メソッドを提供します。これには、Gremlin コンソール、HTTP REST API を使用するテキスト文字列、Groovy を使用する他のシリアル化が含まれます。

**重要**  
これは Gremlin クエリを*テキスト文字列*として送信するメソッド*のみ*に当てはまります。Gremlin 言語バリアントを使用している場合は、その言語のネイティブな日付のクラスと関数を使用する必要があります。詳細については、次のセクション「[GLV 時刻データのネイティブの日付と時刻の使用](best-practices-gremlin-datetime-glv.md)」を参照してください。  
TinkerPop `3.5.2` ([Neptune エンジンリリース 1.1.1.0](engine-releases-1.1.1.0.md) で導入された) から、`datetime` は TinkerPop に欠かせない要素です。

`datetime` メソッドを使用して日付を保存および比較することができます。

```
g.V('3').property('date',datetime('2001-02-08'))
```

```
g.V().has('date',gt(datetime('2000-01-01')))
```

# GLV 時刻データのネイティブの日付と時刻の使用
<a name="best-practices-gremlin-datetime-glv"></a>

Gremlin 言語バリアント (GLV) を使用している場合は、Gremlin 時刻データのプログラミング言語によって提供されるネイティブな日時のクラスと関数を使用する必要があります。

公式の TinkerPop ライブラリはすべて Gremlin 言語バリアントライブラリです。
+  [Go](https://tinkerpop.apache.org/docs/current/reference/#gremlin-go) 
+  [Java](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java) 
+  [Javascript](https://tinkerpop.apache.org/docs/current/reference/#gremlin-javascript) 
+  [.NET](https://tinkerpop.apache.org/docs/current/reference/#gremlin-dotnet) 
+  [Python](https://tinkerpop.apache.org/docs/current/reference/#gremlin-python) 

**重要**  
 このページの内容は、Gremlin 言語バリアントライブラリ (GLV) のみに当てはまります。Gremlin クエリをテキスト文字列として送信するメソッドを使用している場合は、Gremlin の datetime() 関数を使用する必要があります。これには、Gremlin コンソール、HTTP REST API を使用するテキスト文字列、またはドライバーを介して Gremlin 文字列を直接送信することが含まれます。



**Go**  
 以下に、ID「3」を持つ頂点の「date」という 1 つのプロパティを作成する Go の例の一部を示します。これは、Go の time.Now() 関数を使用して生成された日付となる値を設定します。

```
import ( "time" )

g.V('3').property('date', time.Now()).next();
```

Go を使用して Neptune に接続する完全な例については、「[Go を使用して Neptune DB インスタンスに接続する](https://docs.aws.amazon.com//neptune/latest/userguide/access-graph-gremlin-go.html)」を参照してください。

**Java**  
以下に、ID「`3`」を持つ頂点の「`date`」という 1 つのプロパティを作成する Java の例の一部を示します。Java `Date()` コンストラクタを使用して作成された日付となる値を設定します。

```
import java.util.date

g.V('3').property('date', new Date()).next();
```

Java を使用して Neptune に接続するための完全な例については、「[Java クライアントを使用して Neptune DB インスタンスに接続する](access-graph-gremlin-java.md)」を参照してください。

**Node.js (JavaScript)**  
以下に、ID「`3`」を持つ頂点の「`date`」という 1 つのプロパティを作成する JavaScript の例の一部を示します。Node.js `Date()` コンストラクタを使用して作成された日付となる値を設定します。

```
g.V('3').property('date', new Date()).next()
```

Node.js を使用して Neptune に接続するための完全な例については、「[Node.js を使用して Neptune DB インスタンスに接続する](access-graph-gremlin-node-js.md)」を参照してください。

**.NET (C\$1)**  
以下に、ID「`3`」を持つ頂点の「`date`」という 1 つのプロパティを作成する C\$1 の例の一部を示します。.NET `DateTime.UtcNow` プロパティを使用して作成された日付となる値を設定します。

```
Using System;

g.V('3').property('date', DateTime.UtcNow).next()
```

C\$1 を使用して Neptune に接続するための完全な例については、「[.NET を使用して Neptune DB インスタンスに接続する](access-graph-gremlin-dotnet.md)」を参照してください。

**Python**  
以下に、ID「`3`」を持つ頂点の「`date`」という 1 つのプロパティを作成する Python の例の一部を示します。Python `datetime.now()` メソッドを使用して作成された日付となる値を設定します。

```
import datetime

g.V('3').property('date',datetime.datetime.now()).next()
```

Python を使用して Neptune に接続するための完全な例については、「[Python を使用して Neptune DB インスタンスに接続する](access-graph-gremlin-python.md)」を参照してください。

# Neptune で Gremlin Java クライアントを使用するためのベストプラクティス
<a name="best-practices-gremlin-java-client"></a>

Neptune で Gremlin Java クライアントを使用する場合は、以下の推奨事項に従ってください。これらのベストプラクティスは、Java ドライバーを使用する際のパフォーマンスの最適化、接続の効果的な管理、一般的な落とし穴の回避に役立ちます。

Neptune Serverless のハートビート間隔の設定については、「」を参照してください[Neptune Serverless のハートビート設定](best-practices-gremlin-heartbeat-serverless.md)。

**Topics**
+ [複数のスレッドにまたがってクライアントオブジェクトを再利用する](best-practices-gremlin-java-reuse.md)
+ [読み取りと書き込みエンドポイントに別々の Gremlin Java クライアントオブジェクトを作成する](best-practices-gremlin-java-separate.md)
+ [複数のリードレプリカエンドポイントを Gremlin Java 接続プールに追加する](best-practices-gremlin-java-multiple.md)
+ [接続制限を回避するためにクライアントを閉じる](best-practices-gremlin-java-close-connections.md)
+ [フェイルオーバー後の新しい接続の作成](best-practices-gremlin-java-new-connection.md)
+ [`maxInProcessPerConnection` と `maxSimultaneousUsagePerConnection` を使用し、値を同じに設定してください。](best-practices-gremlin-java-maxes.md)
+ [文字列ではなくバイトコードとしてサーバーにクエリを送信する](best-practices-gremlin-java-bytecode.md)
+ [クエリによって返った ResultSet または Iterator を常に完全に消費する](best-practices-gremlin-java-resultset.md)
+ [頂点とエッジをバッチで一括追加](best-practices-gremlin-java-batch-add.md)
+ [Java 仮想マシンで DNS キャッシュを無効にする](best-practices-gremlin-java-disable-dns-caching.md)
+ [クエリごとのレベルでタイムアウトを設定する (オプション)](best-practices-gremlin-java-per-query-timeout.md)
+ [`java.util.concurrent.TimeoutException` のトラブルシューティング](best-practices-gremlin-java-exceptions-TimeoutException.md)

# 複数のスレッドにまたがってクライアントオブジェクトを再利用する
<a name="best-practices-gremlin-java-reuse"></a>

複数のスレッド間で同じクライアント (または `GraphTraversalSource` )オブジェクトを再利用します。つまり、スレッドごとではなく、アプリケーションで `org.apache.tinkerpop.gremlin.driver.Client` クラスの共有インスタンスを作成します。`Client` オブジェクトはスレッドセーフで、初期化にはかなりのオーバーヘッドがかかります。

これは、`GraphTraversalSource` にも当てはまります。この場合、`Client` オブジェクトが内部で作成されます。たとえば、次のコードでは、新しい `Client` オブジェクトがインスタンス化されます。

```
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; 

  /////

GraphTraversalSource traversal = traversal()
                                   .withRemote(DriverRemoteConnection.using(cluster));
```

# 読み取りと書き込みエンドポイントに別々の Gremlin Java クライアントオブジェクトを作成する
<a name="best-practices-gremlin-java-separate"></a>

ライターエンドポイントへの書き込みと 1 つ以上の読み取り専用エンドポイントからの読み込みを実行するだけで、パフォーマンスが向上します。

```
Client readerClient = Cluster.build("https://reader-endpoint")
          ...
          .connect()

Client writerClient = Cluster.build("https://writer-endpoint")
          ...
          .connect()
```

# 複数のリードレプリカエンドポイントを Gremlin Java 接続プールに追加する
<a name="best-practices-gremlin-java-multiple"></a>

Gremlin Java `Cluster` オブジェクトを作成するとき、`.addContactPoint()` メソッドを使用して複数のリードレプリカインスタンスを接続プールの接点に追加することができます。

```
Cluster.Builder readerBuilder = Cluster.build()
          .port(8182)
          .minConnectionPoolSize(…)
          .maxConnectionPoolSize(…)
          ………
          .addContactPoint("reader-endpoint-1")
          .addContactPoint("reader-endpoint-2")
```

# 接続制限を回避するためにクライアントを閉じる
<a name="best-practices-gremlin-java-close-connections"></a>

WebSocket 接続がサーバーで終了し、その接続に関連付けられているリソースがすべて解放されるように、終了時にはクライアントを閉じることが重要です。`client.close( )` は内部で呼び出されるため、`Cluster.close( )` を使用してクラスターを閉じるとリソースは自動的に開放されます。

クライアントが正しく閉じられていない場合、Neptune は 20～25 分後にアイドル状態の WebSocket 接続をすべて終了します。ただし、終了後に WebSocket 接続を明示的に閉じず、ライブ接続の数が [WebSocket 同時接続制限数](limits.md#limits-websockets)に達すると、HTTP `429` エラーコードで追加の接続が拒否されます。そうなった場合、Neptune インスタンスを再起動して接続を閉じる必要があります。

`cluster.close()` を呼び出すアドバイスは、Java AWS Lambda 関数には適用されません。詳細については、「[AWS Lambda 関数で Gremlin WebSocket 接続を管理する](lambda-functions-websocket-connections.md)」を参照してください。

# フェイルオーバー後の新しい接続の作成
<a name="best-practices-gremlin-java-new-connection"></a>

フェイルオーバーが発生した場合、クラスター DNS 名が IP アドレスに解決されるため、Gremlin Driver は古いライターへの接続を継続する可能性があります。このような場合、フェイルオーバー後に新しい `Client` オブジェクトを作成できます。

# `maxInProcessPerConnection` と `maxSimultaneousUsagePerConnection` を使用し、値を同じに設定してください。
<a name="best-practices-gremlin-java-maxes"></a>

`maxInProcessPerConnection` パラメータと `maxSimultaneousUsagePerConnection` パラメータのいずれも、単一の WebSocket 接続で送信できる同時クエリの最大数に関連しています。これらのパラメータは、内部的に相互に関連しており、もう一方のパラメータなしで一方を変更すると、クライアントがクライアント接続プールから接続を取得しようとしている間にタイムアウトになる可能性があります。

処理中のデフォルト最小値と同時使用値を維持し、`maxInProcessPerConnection` と `maxSimultaneousUsagePerConnection` を同じ値に設定することをお勧めします。

これらのパラメータを設定する値は、クエリの複雑さとデータモデルの関数です。クエリで大量のデータが返るユースケースでは、クエリあたりの接続帯域幅が大きくなるため、パラメータの値を小さくし、`maxConnectionPoolSize` の値を大きくする必要があります。

対照的に、クエリで少量のデータが返るケースでは、`maxInProcessPerConnection` および `maxSimultaneousUsagePerConnection` は、`maxConnectionPoolSize` よりも大きい値に設定する必要があります。

# 文字列ではなくバイトコードとしてサーバーにクエリを送信する
<a name="best-practices-gremlin-java-bytecode"></a>

クエリを送信する際、文字列ではなくバイトコードを使用する利点があります。
+ **無効なクエリ構文を早期に検出する:** バイトコードのバリアントを使用すると、コンパイル段階で無効なクエリ構文を検出することができます。文字列ベースのバリエーションを使用した場合は、クエリがサーバーに送信され、エラーが返るまで無効な構文を検出することはできません。
+ **文字列ベースのパフォーマンスのペナルティを回避する:** WebSocket または HTTP を使用しているかどうかにかかわらず、文字列ベースのクエリ送信では、頂点がデタッチされます。つまり、Vertex オブジェクトは、ID、Label、および Vertex に関連付けられているすべてのプロパティで構成されます ([要素のプロパティ](http://tinkerpop.apache.org/docs/current/reference/#_properties_of_elements)参照)。

  プロパティが不要な場合には、これにより、サーバー上で不要な計算が生じる可能性があります。たとえば、顧客がクエリ `g.V("hakuna#1")` を使用して ID ("hakuna\$11) の頂点を取得する場合。クエリが文字列ベースの送信として送信された場合、サーバーではこの頂点の ID、ラベル、およびすべてのプロパティの取得が行われます。クエリがバイトコード送信として送信された場合、サーバーでは頂点の ID とラベルの取得のみ行われます。

つまり、このようなクエリは送信しません。

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final Client client = cluster.connect();
      List<Result> results = client.submit("g.V().has('name','pumba').out('friendOf').id()").all().get();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

代わりに、次のようにバイトコードを使用してクエリを送信します。

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
      List<Object> verticesWithNamePumba = g.V().has("name", "pumba").out("friendOf").id().toList();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

# クエリによって返った ResultSet または Iterator を常に完全に消費する
<a name="best-practices-gremlin-java-resultset"></a>

クライアントオブジェクトでは常に `ResultSet` (文字列ベースの送信の場合)、または `GraphTraversal` より返るイテレータを完全に消費する必要があります。クエリ結果が完全に消費されない場合、サーバーはそれらを保持し、クライアントがそれらを消費し終わるまで待機します。

アプリケーションが部分的な結果セットしか必要としない場合は、クエリで `limit(X)` ステップを使用して、サーバーで生成される結果の数を制限することができます。

# 頂点とエッジをバッチで一括追加
<a name="best-practices-gremlin-java-batch-add"></a>

Neptune DB へのクエリはすべて、単一のトランザクションの範囲内で実行されます。ただし、セッションを使用する場合は除きます。つまり、gremlin クエリを使用して大量のデータを挿入する必要がある場合は、それらを50〜100 のバッチサイズでまとめてバッチ処理すると、ロード用に作成されるトランザクションの数が減り、パフォーマンスが向上します。

例として、データベースに 5 つの頂点を追加すると、次のようになります。

```
// Create a GraphTraversalSource for the remote connection
final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
// Add 5 vertices in a single query
g.addV("Person").property(T.id, "P1")
 .addV("Person").property(T.id, "P2")
 .addV("Person").property(T.id, "P3")
 .addV("Person").property(T.id, "P4")
 .addV("Person").property(T.id, "P5").iterate();
```

同様に、 を使用してエッジをバッチ追加できます`addE`。を使用して`V()`、既存の頂点を各エッジのソースとターゲットとして参照します。

```
// Add edges in a single batched query
g.V("P1").addE("knows").to(V("P2"))
 .V("P2").addE("knows").to(V("P3"))
 .V("P3").addE("knows").to(V("P4"))
 .V("P4").addE("knows").to(V("P5")).iterate();
```

頂点とエッジの作成を 1 つのバッチにまとめることもできます。を使用して新しく作成された頂点`as()`にラベルを付けて、同じトラバーサルにエッジを追加するときに参照できるようにします。

```
// Add vertices and edges together in a single query
g.addV("Person").property(T.id, "P1").as("p1")
 .addV("Person").property(T.id, "P2").as("p2")
 .addV("Person").property(T.id, "P3").as("p3")
 .addE("knows").from("p1").to("p2")
 .addE("knows").from("p2").to("p3").iterate();
```

# Java 仮想マシンで DNS キャッシュを無効にする
<a name="best-practices-gremlin-java-disable-dns-caching"></a>

複数のリードレプリカ間でリクエストを負荷分散する環境では、Java 仮想マシン (JVM) で DNS キャッシュを無効にし、[クラスターオブジェクト](https://tinkerpop.apache.org/javadocs/current/core/org/apache/tinkerpop/gremlin/driver/Cluster.html)の作成中に Neptune のリーダーエンドポイントを指定する必要があります。JVM DNS キャッシュを無効にすると、すべてのリードレプリカにリクエストが分散されるように、新しい接続ごとに DNS が再度解決されます。これは、アプリケーションの初期化コードで次の行で実行できます。

```
java.security.Security.setProperty("networkaddress.cache.ttl", "0");
```

ただし、ロードバランシングのためのより完全で堅牢なソリューションは、GitHub の [Amazon Gremlin Java クライアントコード](https://github.com/awslabs/amazon-neptune-tools/tree/master/neptune-gremlin-client)で提供されます。Amazon Java Gremlin クライアントはクラスタートポロジを認識し、Neptune クラスター内のインスタンスのセット全体に接続とリクエストを公平に分散します。そのクライアントを使用する Java Lambda 関数のサンプルについては[このブログ投稿](https://aws.amazon.com/blogs/database/load-balance-graph-queries-using-the-amazon-neptune-gremlin-client/)を参照してください。

# クエリごとのレベルでタイムアウトを設定する (オプション)
<a name="best-practices-gremlin-java-per-query-timeout"></a>

Neptune では、パラメータグループオプション `neptune_query_timeout` を使用して、クエリのタイムアウトを設定することができます ([パラメータ](parameters.md) 参照)。グローバルタイムアウトは、次のようなコードで上書きすることもできます。

```
  final Cluster cluster = Cluster.build("localhost")
                                 .port(8182)
                                 .maxInProcessPerConnection(32)
                                 .maxSimultaneousUsagePerConnection(32)
                                 .serializer(Serializers.GRAPHBINARY_V1D0)
                                 .create();

  try {
      final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
      List<Object> verticesWithNamePumba = g.with(ARGS_EVAL_TIMEOUT, 500L).V().has("name", "pumba").out("friendOf").id().toList();
      System.out.println(verticesWithNamePumba);
  } finally {
      cluster.close();
  }
```

また、文字列ベースのクエリ送信の場合、コードは次のようになります。

```
  RequestOptions options = RequestOptions.build().timeout(500).create();
  List<Result> result = client.submit("g.V()", options).all().get();
```

**注記**  
特にサーバーレスインスタンスでは、クエリのタイムアウト値を高く設定しすぎると、予期しないコストが発生する可能性があります。妥当なタイムアウト設定がないと、クエリは予想よりもずっと長く実行され続け、予想もしなかったコストが発生する可能性があります。これは、クエリの実行中に大規模で高価なインスタンスタイプにスケールアップする可能性があるサーバーレスインスタンスに特に当てはまります。  
予想される実行時間に対応し、異常に長い実行でもタイムアウトが発生するだけのクエリタイムアウト値を使用することで、このような予期しない出費を回避できます。  
 Neptune エンジンバージョン 1.3.2.0 以降、Neptune は新しい neptune\$1lab\$1mode パラメータとして `StrictTimeoutValidation` をサポートしています。このパラメータの値が `Enabled` である場合、リクエストオプションまたはクエリヒントとして指定されたクエリごとのタイムアウト値は、パラメータグループでグローバルに設定された値を超えることはできません。このような場合、Neptune は `InvalidParameterException` をスローします。  
 この設定は、値が の場合、「/status」エンドポイントのレスポンスで確認できます`Disabled`。エンジンバージョン では`1.3.2.0`、このパラメータのデフォルト値は です`Disabled`。エンジンバージョン 以降`1.4.0.0`、 `Enabled` `StrictTimeoutValidation`パラメータはデフォルトで です。  
 複数のタイムアウト設定が設定されている場合にタイムアウトの優先順位を決定する方法の詳細については、[neptune\$1query\$1timeout](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout) パラメータのドキュメントを参照してください。

# `java.util.concurrent.TimeoutException` のトラブルシューティング
<a name="best-practices-gremlin-java-exceptions-TimeoutException"></a>

Gremlin Javaクライアントは、WebSocket 接続の 1 つのスロットが使用可能になるのを待っている間、クライアント自体で Gremlin 要求がタイムアウトしたとき `java.util.concurrent.TimeoutException` をスローします。このタイムアウト期間は、`maxWaitForConnection` クライアント側の設定可能なパラメータにより制御します。

**注記**  
クライアントでタイムアウトしたリクエストはサーバーに送信されないため、`GremlinRequestsPerSec` のような、サーバーでキャプチャされたメトリクスには反映されません。

この種のタイムアウトは、通常、次の 2 つの方法のいずれかで発生します。
+ **サーバが実際に最大容量に達した。**この場合、サーバ上のキューがいっぱいになります。この状態は、[MainRequestQueuePendingRequests](cw-metrics.md#cw-metrics-available) CloudWatch メトリクスのモニタリングで検出できます。サーバーが処理できる並列クエリの数は、インスタンスのサイズによって異なります。

  `MainRequestQueuePendingRequests` メトリクスが、サーバー上の保留中のリクエストのビルドアップを表示しなければ、サーバーはリクエストをさらに処理でき、タイムアウトはクライアント側のスロットリングによって生じます。
+ **クライアントによるリクエストのスロットル。**これは通常、クライアント構成設定を変更することで修正できます。

  クライアントが送信できる並列要求の最大数は、おおむね次のように推定できます。

  ```
  maxParallelQueries = maxConnectionPoolSize * Max( maxSimultaneousUsagePerConnection, maxInProcessPerConnection )
  ```

  クライアントに `maxParallelQueries` 以上を送信すると `java.util.concurrent.TimeoutException` 例外の原因になります。通常、いくつかの方法で修正できます。
  + *接続タイムアウト時間を増やす。*アプリケーションでレイテンシーが重要でない場合は、クライアントの `maxWaitForConnection` 設定を増やします。その後、クライアントはタイムアウトするまで待機時間が長くなり、代わりにレイテンシーが増加する可能性があります。
  + *接続あたりの最大リクエスト数を増やす。*これにより、同じ WebSocket 接続を使用してより多くのリクエストを送信できます。クライアントの `maxSimultaneousUsagePerConnection` および `maxInProcessPerConnection` 設定を増やしてこれを行います。これらの設定には通常、同じ値が設定されています。
  + *接続プール内の接続数を増やす。*クライアントの `maxConnectionPoolSize` 設定を増やしてこれを行います。各接続でメモリとオペレーティングシステムのファイルディスクリプタが使用され、初期化時に SSL および WebSocket ハンドシェイクが必要になるため、リソース消費が増加するという対価が伴います。

# OpenCypher と Bolt を使用した Neptune のベストプラクティス
<a name="best-practices-opencypher"></a>

Neptune で openCypher クエリ言語と Bolt プロトコルを使用する場合は、これらのベストプラクティスに従ってください。Neptune で openCypher を使用する方法については、[openCypher でNeptune グラフにアクセスする](access-graph-opencypher.md) を参照してください。

**Topics**
+ [フェイルオーバー後の新しい接続の作成](#best-practices-opencypher-renew-connection)
+ [存続期間の長いアプリケーションの接続処理](#best-practices-opencypher-long-connections)
+ [の接続処理 AWS Lambda](#best-practices-opencypher-lambda-connections)
+ [クエリでは双方向のエッジを優先する](best-practices-opencypher-directed-edges.md)
+ [Neptune は 1 つのトランザクションでの複数の同時クエリをサポートしていません。](best-practices-opencypher-multiple-queries.md)
+ [完了したら、ドライバーオブジェクトを閉じます](best-practices-opencypher-close-driver.md)
+ [読み取りと書き込みには明示的なトランザクションモードを使用してください。](best-practices-opencypher-use-explicit-txs.md)
+ [例外の場合の再試行ロジック](best-practices-opencypher-retry-logic.md)
+ [1 つの SET 句を使用して複数のプロパティを一度に設定する](best-practices-content-0.md)
+ [パラメータ化されたクエリを使用する](best-practices-content-2.md)
+ [UNWIND 句でネストされたマップの代わりにフラット化されたマップを使用する](best-practices-content-3.md)
+ [可変長パス (VLP) 式で、より制限の厳しいノードを左側に配置する](best-practices-content-4.md)
+ [詳細な関係名を使用してノードラベルの冗長チェックを回避する](best-practices-content-5.md)
+ [可能な場合はエッジラベルを指定する](best-practices-content-6.md)
+ [可能な場合は WITH 句を使用しない](best-practices-content-7.md)
+ [制限付きフィルターをクエリのできるだけ早い段階で配置する](best-practices-content-8.md)
+ [プロパティが存在するかどうかを明示的に確認する](best-practices-content-9.md)
+ [名前付きパスを使用しない (必須でない限り)](best-practices-content-10.md)
+ [COLLECT(DISTINCT()) を避ける](best-practices-content-11.md)
+ [すべてのプロパティ値を取得するときに、個々のプロパティルックアップよりもプロパティ関数を優先する](best-practices-content-12.md)
+ [クエリの外部で静的計算を実行する](best-practices-content-13.md)
+ [個々のステートメントの代わりに UNWIND を使用したバッチ入力をする](best-practices-content-14.md)
+ [ノード/リレーションシップにカスタム ID を使用する](best-practices-content-15.md)
+ [クエリで \$1id 計算を実行しない](best-practices-content-16.md)
+ [複数のノードの更新/移行](best-practices-merge-multiple-nodes.md)

## フェイルオーバー後の新しい接続の作成
<a name="best-practices-opencypher-renew-connection"></a>

フェイルオーバーが発生した場合、DNS 名が特定の IP アドレスに解決されるため、Bolt ドライバーは新しいアクティブなライターインスタンスではなく古いライターインスタンスに接続し続けることがあります。

これを防ぐには、フェイルオーバー後に `Driver` オブジェクトを閉じて、再接続します。

## 存続期間の長いアプリケーションの接続処理
<a name="best-practices-opencypher-long-connections"></a>

コンテナ内や Amazon EC2 インスタンスで実行されるアプリケーションなど、長期間有効なアプリケーションを構築する場合は、`Driver` オブジェクトを一度インスタンス化し、そのオブジェクトをアプリケーションの存続期間中再利用します。`Driver` オブジェクトはスレッドセーフで、初期化にはかなりのオーバーヘッドがかかります。

## の接続処理 AWS Lambda
<a name="best-practices-opencypher-lambda-connections"></a>

Bolt ドライバーは、接続オーバーヘッドと管理要件のため、 AWS Lambda 関数内での使用はお勧めしません。代わりに [HTTPS エンドポイント](access-graph-opencypher-queries.md)を使用してください。

# クエリでは双方向のエッジを優先する
<a name="best-practices-opencypher-directed-edges"></a>

Neptune がクエリの最適化を行う場合、双方向のエッジでは、最適なクエリプランを作成することが難しくなります。最適ではないプランでは、エンジンが不必要な作業を行う必要があり、その結果、パフォーマンスが低下します。

そのため、可能な限り、双方向のエッジではなく有向エッジを使用してください。例えば 

```
MATCH p=(:airport {code: 'ANC'})-[:route]->(d) RETURN p)
```

ではなく、を使用します。

```
MATCH p=(:airport {code: 'ANC'})-[:route]-(d) RETURN p)
```

ほとんどのデータモデルは実際には両方向のエッジをトラバースする必要はないため、有向エッジを使用するように切り替えることでクエリのパフォーマンスを大幅に向上させることができます。

データモデルで双方向のエッジをトラバースする必要がある場合は、`MATCH` パターン内の最初のノード (左側) をフィルタリングの制限が最も厳しいノードにします。

「`routes` 空港と `ANC` 空港の間のすべてのルートを見つけて」という例を考えてみましょう。`ANC` 空港から出発する場合、このクエリは次のようになります。

```
MATCH p=(src:airport {code: 'ANC'})-[:route]-(d) RETURN p
```

最も制限の厳しいノードがパターン内の最初のノード (左側) に配置されるため、エンジンは最小限の作業でクエリを満たすことができます。その後、エンジンはクエリを最適化できます。

これは、次のように、パターンの最後で `ANC` 空港をフィルタリングするよりもはるかに望ましい方法です。

```
MATCH p=(d)-[:route]-(src:airport {code: 'ANC'}) RETURN p
```

最も制限の厳しいノードがパターン内の最初に配置されない場合、エンジンはクエリを最適化できず、結果を得るために追加の検索を実行する必要があるため、追加の作業を行う必要があります。

# Neptune は 1 つのトランザクションでの複数の同時クエリをサポートしていません。
<a name="best-practices-opencypher-multiple-queries"></a>

Bolt ドライバー自体ではトランザクション内での同時クエリが可能ですが、Neptune は同時に実行されているトランザクション内の複数のクエリをサポートしていません。その代わり、Neptune では、トランザクション内の複数のクエリを順次実行し、各クエリの結果を次のクエリが開始される前に完全に処理する必要があります。

以下の例は、Bolt を使用して 1 つのトランザクションで複数のクエリを連続して実行する方法を示しています。これにより、次のクエリが始まる前にそれぞれの結果が完全に消費されます。

```
final String query = "MATCH (n) RETURN n";

try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
  try (Session session = driver.session(readSessionConfig)) {
    try (Transaction trx = session.beginTransaction()) {
      final Result res_1 = trx.run(query);
      Assert.assertEquals(10000, res_1.list().size());
      final Result res_2 = trx.run(query);
      Assert.assertEquals(10000, res_2.list().size());
    }
  }
}
```

# 完了したら、ドライバーオブジェクトを閉じます
<a name="best-practices-opencypher-close-driver"></a>

Bolt 接続がサーバーによって閉じられ、その接続に関連付けられているリソースがすべて解放されるように、終了時にはクライアントを閉じることが重要です。`driver.close()` を使用してドライバーを閉じると、この処理が自動的に行われます。

ドライバーが正しく閉じられていない場合、Neptune は 20 分後に、または IAM 認証を使用している場合は 10 日後に、アイドル状態の Bolt 接続をすべて終了します。

Neptune がサポートする Bolt の同時接続数は 1000 個までです。終了後に接続を明示的に閉じず、ライブ接続の数が 1000 という制限数に達すると、新しい接続試行は失敗します。

# 読み取りと書き込みには明示的なトランザクションモードを使用してください。
<a name="best-practices-opencypher-use-explicit-txs"></a>

Neptune と Bolt ドライバーでトランザクションを使用するときは、読み取りトランザクションと書き込みトランザクションの両方のアクセスモードを適切な設定に明示的に設定するのが最善です。

## 読み取り専用トランザクション
<a name="best-practices-opencypher-read-txs"></a>

読み取り専用トランザクションでは、セッションを構築するときに適切なアクセスモード構成を渡さないと、デフォルトの分離レベル、つまりミューテーションクエリ分離が使用されます。そのため、読み取り専用トランザクションでは、アクセスモードを `read` に明示的に設定することが重要です。

**自動コミットによる読み取りトランザクションの例:**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.READ)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
  throw e;
} finally {
  driver.close()
}
```

**読み取りトランザクションの例:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.READ)
  .build();
driver.session(sessionConfig).readTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

いずれの場合も、[`SNAPSHOT` 分離は](transactions-isolation-levels.md) [Neptune の読み取り専用トランザクションセマンティクスを使用して実現されます](transactions-neptune.md#transactions-neptune-read-only)。

リードレプリカは読み取り専用クエリしか受け付けないため、リードレプリカに送信されるクエリはすべて `SNAPSHOT` 分離セマンティクスで実行されます。

読み取り専用トランザクションには、ダーティリードや繰り返し不可能なリードはありません。

## ミューテーショントランザクション
<a name="best-practices-opencypher-mutation-txs"></a>

ミューテーションクエリでは、書き込みトランザクションを作成するための 3 つの異なるメカニズムがあり、それぞれを以下に示します。

**暗黙的な書き込みトランザクションの例:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
driver.session(sessionConfig).writeTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

**自動コミット書き込みトランザクションの例:**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.Write)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
    throw e;
} finally {
    driver.close()
}
```

**明示的な書き込みトランザクションの例:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
Transaction beginWriteTransaction = driver.session(sessionConfig).beginTransaction();
  (Add your application code here)
beginWriteTransaction.commit();
driver.close();
```

**書き込みトランザクションの分離レベル**
+ ミューテーションクエリの一部として行われる読み取りは、`READ COMMITTED` トランザクション分離のもとで実行されます。
+ ミューテーションクエリの一部として行われる読み取りにはダーティリードはありません。
+ ミューテーションクエリを読み込むと、レコードとレコード範囲はロックされます。
+ つまり、インデックスの範囲がミューテーショントランザクションによって読み取られた場合、この範囲は読み取りトランザクションが終了するまで同時トランザクションによって変更されないという強力な保証があります。

ミューテーションクエリはスレッドセーフではありません。

コンフリクトについては、[ロック待機タイムアウトを使用した競合の解決](transactions-neptune.md#transactions-neptune-conflicts) を参照してください。

ミューテーションクエリは、失敗しても自動的に再試行されません。

# 例外の場合の再試行ロジック
<a name="best-practices-opencypher-retry-logic"></a>

再試行を許可するすべての例外については、`ConcurrentModificationException` エラーなどの一時的な問題をより適切に処理するために、再試行までの待機時間を徐々に長くする[エクスポネンシャルバックオフおよび再試行戦略](https://docs.aws.amazon.com/general/latest/gr/api-retries.html)を使用するのが一般的には最適です。以下は、エクスポネンシャルバックオフおよび再試行のパターンの例を示しています。

```
public static void main() {
  try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
    retriableOperation(driver, "CREATE (n {prop:'1'})")
        .withRetries(5)
        .withExponentialBackoff(true)
        .maxWaitTimeInMilliSec(500)
        .call();
  }
}

protected RetryableWrapper retriableOperation(final Driver driver, final String query){
  return new RetryableWrapper<Void>() {
    @Override
    public Void submit() {
      log.info("Performing graph Operation in a retry manner......");
      try (Session session = driver.session(writeSessionConfig)) {
        try (Transaction trx =  session.beginTransaction()) {
            trx.run(query).consume();
            trx.commit();
        }
      }
      return null;
    }

    @Override
    public boolean isRetryable(Exception e) {
      if (isCME(e)) {
        log.debug("Retrying on exception.... {}", e);
        return true;
      }
      return false;
    }

    private boolean isCME(Exception ex) {
      return ex.getMessage().contains("Operation failed due to conflicting concurrent operations");
    }
  };
}



/**
 * Wrapper which can retry on certain condition. Client can retry operation using this class.
 */
@Log4j2
@Getter
public abstract class RetryableWrapper<T> {

  private long retries = 5;
  private long maxWaitTimeInSec = 1;
  private boolean exponentialBackoff = true;

  /**
   * Override the method with custom implementation, which will be called in retryable block.
   */
  public abstract T submit() throws Exception;

  /**
   * Override with custom logic, on which exception to retry with.
   */
  public abstract boolean isRetryable(final Exception e);

  /**
   * Define the number of retries.
   *
   * @param retries -no of retries.
   */
  public RetryableWrapper<T> withRetries(final long retries) {
    this.retries = retries;
    return this;
  }

  /**
   * Max wait time before making the next call.
   *
   * @param time - max polling interval.
   */
  public RetryableWrapper<T> maxWaitTimeInMilliSec(final long time) {
    this.maxWaitTimeInSec = time;
    return this;
  }

  /**
   * ExponentialBackoff coefficient.
   */
  public RetryableWrapper<T> withExponentialBackoff(final boolean expo) {
    this.exponentialBackoff = expo;
    return this;
  }

  /**
   * Call client method which is wrapped in submit method.
   */
  public T call() throws Exception {
    int count = 0;
    Exception exceptionForMitigationPurpose = null;
    do {
      final long waitTime = exponentialBackoff ? Math.min(getWaitTimeExp(retries), maxWaitTimeInSec) : 0;
      try {
          return submit();
      } catch (Exception e) {
        exceptionForMitigationPurpose = e;
        if (isRetryable(e) && count < retries) {
          Thread.sleep(waitTime);
          log.debug("Retrying on exception attempt - {} on exception cause - {}", count, e.getMessage());
        } else if (!isRetryable(e)) {
          log.error(e.getMessage());
          throw new RuntimeException(e);
        }
      }
    } while (++count < retries);

    throw new IOException(String.format(
          "Retry was unsuccessful.... attempts %d. Hence throwing exception " + "back to the caller...", count),
          exceptionForMitigationPurpose);
  }

  /*
   * Returns the next wait interval, in milliseconds, using an exponential backoff
   * algorithm.
   */
  private long getWaitTimeExp(final long retryCount) {
    if (0 == retryCount) {
      return 0;
    }
    return ((long) Math.pow(2, retryCount) * 100L);
  }
}
```

# 1 つの SET 句を使用して複数のプロパティを一度に設定する
<a name="best-practices-content-0"></a>

 複数の SET 句を使用して個々のプロパティを設定する代わりに、マップを使用してエンティティに複数のプロパティを一度に設定します。

 次を使用できます。

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n += {property1 : 'value1',
property2 : 'value2',
property3 : 'value3'}
```

 次は推奨しません。

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n.property1 = 'value1'
SET n.property2 = 'value2'
SET n.property3 = 'value3'
```

 SET 句は、単一のプロパティまたはマップのいずれかを受け入れます。1 つのエンティティで複数のプロパティを更新する場合、マップで 1 つの SET 句を使用すると、複数のオペレーションをせずに 1 回のオペレーションで更新でき、より効率的に実行できます。

## SET 句を使用して複数のプロパティを一度に削除する
<a name="best-practices-content-1"></a>

 openCypher 言語を使用する場合、REMOVE を使用してエンティティからプロパティを削除します。Neptune では、削除する各プロパティに個別のオペレーションが必要で、クエリレイテンシーが追加されます。代わりに、マップで SET を使用してすべてのプロパティ値を `null` に設定することもできます。これは Neptune ではプロパティの削除と同等です。1 つのエンティティの複数のプロパティを削除する必要がある場合、Neptune のパフォーマンスが向上します。

 を使用します。

```
WITH {prop1: null, prop2: null, prop3: null} as propertiesToRemove 
MATCH (n) 
SET n += propertiesToRemove
```

次は推奨しません。

```
MATCH (n) 
REMOVE n.prop1, n.prop2, n.prop3
```

# パラメータ化されたクエリを使用する
<a name="best-practices-content-2"></a>

 openCypher を使用してクエリを実行するときは、常にパラメータ化されたクエリを使用することをお勧めします。クエリエンジンは、クエリプランキャッシュなどの機能に対してパラメータ化されたクエリを繰り返し活用できます。この場合、異なるパラメータを使用して同じパラメータ化された構造を繰り返し呼び出すと、キャッシュされたプランを活用できます。パラメータ化されたクエリに対して生成されたクエリプランはキャッシュされるとともに、100 ミリ秒以内に完了し、パラメータタイプが NUMBER、BOOLEAN、STRING のいずれかの場合にのみ再利用されます。

 を使用します。

```
MATCH (n:foo) WHERE id(n) = $id RETURN n
```

パラメータの入力例:

```
parameters={"id": "first"}
parameters={"id": "second"}
parameters={"id": "third"}
```

次は推奨しません。

```
MATCH (n:foo) WHERE id(n) = "first" RETURN n
MATCH (n:foo) WHERE id(n) = "second" RETURN n
MATCH (n:foo) WHERE id(n) = "third" RETURN n
```

# UNWIND 句でネストされたマップの代わりにフラット化されたマップを使用する
<a name="best-practices-content-3"></a>

 ディープネスト構造によって、クエリエンジンが最適なクエリプランを生成する機能が制限される可能性があります。この問題を部分的に軽減するために、以下の定義されたパターンにより、以下のシナリオに対して最適なプランが作成されます。
+  シナリオ 1: NUMBER、STRING、BOOLEAN を含む Cypher リテラルのリストで UNWIND を使用する。
+  シナリオ 2: Cypher リテラル (NUMBER、STRING、BOOLEAN) のみを含む、フラット化されたマップのリストで UNWIND を使用する。

 UNWIND 句を含むクエリを記述する場合は、上記の推奨事項を使用してパフォーマンスを向上させます。

シナリオ 1 の例:

```
UNWIND $ids as x
MATCH(t:ticket {`~id`: x})
```

パラメータの入力例:

```
parameters={
  "ids": [1, 2, 3]
}
```

 シナリオ 2 の例は、CREATE または MERGE を行うノードのリストを生成することです。複数のステートメントを発行する代わりに、次のパターンを使用してプロパティをフラット化されたマップのセットとして定義します。

```
UNWIND $props as p
CREATE(t:ticket {title: p.title, severity:p.severity})
```

パラメータの入力例:

```
parameters={
  "props": [
    {"title": "food poisoning", "severity": "2"},
    {"title": "Simone is in office", "severity": "3"}
  ]
}
```

次のようなネストされたノードオブジェクトは推奨しません。

```
UNWIND $nodes as n
CREATE(t:ticket n.properties)
```

パラメータの入力例:

```
parameters={
  "nodes": [
    {"id": "ticket1", "properties": {"title": "food poisoning", "severity": "2"}},
    {"id": "ticket2", "properties": {"title": "Simone is in office", "severity": "3"}}
  ]
}
```

# 可変長パス (VLP) 式で、より制限の厳しいノードを左側に配置する
<a name="best-practices-content-4"></a>

 可変長パス (VLP) クエリでは、クエリエンジンは、式の左側または右側でトラバーサルを開始するように選択して評価を最適化します。決定は、左右のパターンのカーディナリティに基づいています。カーディナリティは、指定されたパターンに一致するノードの数です。
+  右のパターンのカーディナリティが 1 の場合、右側が開始点になります。
+  左右のカーディナリティが 1 の場合、両側で拡張がチェックされ、拡張が小さい側で開始されます。拡張は、VLP 式の左側にあるノードと右側にあるノードの発信エッジまたは着信エッジの数です。最適化のこの部分は、VLP 関係が一方向で、関係タイプが指定されている場合にのみ使用されます。
+  それ以外の場合は、左側が開始点になります。

 VLP 式のチェーンの場合、この最適化は最初の式にのみ適用できます。他の VLPs は左側から評価されます。例えば、(a)、(b) のカーディナリティを 1、(c) のカーディナリティを 1 より大きくします。
+  `(a)-[*1..]->(c)`: 評価は (a) で始まります。
+  `(c)-[*1..]->(a)`: 評価は (a) で始まります。
+  `(a)-[*1..]-(c)`: 評価は (a) で始まります。
+  `(c)-[*1..]-(a)`: 評価は (a) で始まります。

 次に、(a) の着信エッジを 2 にし、(a) の発信エッジを 3 にし、(b) の着信エッジを 4 にし、(b) の発信エッジを 5 にします。
+  `(a)-[*1..]->(b)`: (a) の送信エッジが (b) の送信エッジよりも小さいため、評価は (a) で始まります。
+  `(a)<-[*1..]-(b)`: (a) の着信エッジが (b) の発信エッジよりも小さいため、評価は (a) で始まります。

 原則として、より制限の厳しいパターンを VLP 式の左側に配置します。

# 詳細な関係名を使用してノードラベルの冗長チェックを回避する
<a name="best-practices-content-5"></a>

 パフォーマンスを最適化する場合、ノードパターン専用のリレーションシップラベルを使用すると、ノードのラベルフィルタリングを削除できます。2 つの `person` ノード間の関係を定義するために、関係 `likes` のみが使用されるグラフモデルを考えてみましょう。このパターンを見つけるために、次のクエリを記述できます。

```
MATCH (n:person)-[:likes]->(m:person)
RETURN n, m
```

 n と m の `person` ラベルチェックは冗長です。これは、どちらもタイプ `person` の場合にのみ表示されるように関係を定義しているためです。パフォーマンスを最適化するために、次のようにクエリを記述できます。

```
MATCH (n)-[:likes]->(m)
RETURN n, m
```

 このパターンは、プロパティが 1 つのノードラベルに排他的である場合にも適用できます。`person` ノードのみがプロパティ `email` を持っていると仮定します。したがって、ノードラベルの `person` との一致の検証は冗長です。このクエリを 

```
MATCH (n:person)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 のように記述するよりも、次のように記述するほうが効率的です。

```
MATCH (n)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 このパターンは、パフォーマンスが重要で、モデリングプロセスでこれらのエッジラベルが他のノードラベルを含むパターンに再利用されないことを確認するチェックがある場合にのみ採用する必要があります。`company` などの別のノードラベルに後で `email` プロパティを導入する場合、これらの 2 つのバージョンのクエリでは結果が異なります。

# 可能な場合はエッジラベルを指定する
<a name="best-practices-content-6"></a>

 パターンでエッジを指定するときは、可能な限りエッジラベルを指定することをお勧めします。次のクエリの例を考えてみましょう。このクエリは、ある都市に住むすべての人々と、その都市を訪ねたすべての人々をリンクするために使用されます。

```
MATCH (person)-->(city {country: "US"})-->(anotherPerson)
RETURN person, anotherPerson
```

 グラフモデルが複数のエッジラベルを使用してユーザーを都市以外のノードにリンクする場合、終了ラベルを指定しないと、Neptune は後で破棄される追加のパスを評価する必要があります。上記のクエリでは、エッジラベルが指定されていないため、エンジンは最初により多くの作業を行い、次に値をフィルタリングして正しい結果を取得します。上記のクエリのより適切なバージョンは次のとおりです。

```
MATCH (person)-[:livesIn]->(city {country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

 これは、評価に役立つだけでなく、クエリプランナーがより優れたプランを作成することも可能にします。このベストプラクティスを冗長ノードラベルチェックと組み合わせて、都市ラベルチェックを削除し、クエリを次のように記述することもできます。

```
MATCH (person)-[:livesIn]->({country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

# 可能な場合は WITH 句を使用しない
<a name="best-practices-content-7"></a>

 openCypher の WITH 句は、その前にあるすべてが実行され、結果の値がクエリの残りの部分に渡される境界として機能します。WITH 句は、中間集計が必要な場合や結果の数を制限したい場合に必要ですが、それ以外の場合は WITH 句の使用を避ける必要があります。一般的なガイダンスでは、これらの単純な WITH 句 (集計、順序、制限なし) を削除して、クエリプランナーがクエリ全体で作業してグローバルに最適なプランを作成できるようにします。例えば、`India` に住むすべてのユーザーを返すクエリを作成したとします。

```
MATCH (person)-[:lives_in]->(city)
WITH person, city
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

 上記のバージョンでは、WITH 句は `(person)-[:lives_in]->(city)` より前のパターン `(city)-[:part_of]->(country {name: 'India'})` (より制限的) の配置を制限します。そのため、このプランは準最適となります。このクエリを最適化するには、WITH 句を削除し、プランナーが最適なプランを計算できるようにします。

```
MATCH (person)-[:lives_in]->(city)
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

# 制限付きフィルターをクエリのできるだけ早い段階で配置する
<a name="best-practices-content-8"></a>

 すべてのシナリオで、クエリへのフィルターの早期配置は、クエリプランが考慮する必要がある中間ソリューションを減らすのに役立ちます。つまり、クエリの実行に必要なメモリとコンピューティングリソースが少なくなります。

 次の例は、これらの影響を理解するのに役立ちます。`India` に住むすべてのユーザーを返すクエリを記述するとします。クエリの 1 つのバージョンは次のとおりです。

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WITH country, collect(n.firstName + " "  + n.lastName) AS result
WHERE country.name = 'India'
RETURN result
```

 上記のバージョンのクエリは、このユースケースを達成するための最適な方法ではありません。フィルター `country.name = 'India'` はクエリパターンの後半に表示されます。まず、すべての人物とその住んでいる場所を収集し、国ごとにグループ化してから、`country.name = India` のグループのみをフィルタリングします。`India` に住んでいるユーザーのみをクエリし、収集集約を実行する最適な方法です。

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WHERE country.name = 'India'
RETURN collect(n.firstName + " "  + n.lastName) AS result
```

 原則として、変数の導入後できるだけ早くフィルターを配置します。

# プロパティが存在するかどうかを明示的に確認する
<a name="best-practices-content-9"></a>

 openCypher のセマンティクスに基づけば、プロパティにアクセスするとそれはオプションの結合と同等になり、プロパティが存在しない場合でもすべての行を保持する必要があります。グラフスキーマに基づいて、そのエンティティに特定のプロパティが常に存在することがわかっている場合、そのプロパティの存在を明示的にチェックすることで、クエリエンジンは最適なプランを作成し、パフォーマンスを向上させることができます。

 タイプ `person` のノードに常にプロパティ `name` があるグラフモデルを考えてみましょう。次の例は推奨されません: 

```
MATCH (n:person)
RETURN n.name
```

 IS NOT NULL チェックを使用して、クエリ内のプロパティの存在を明示的に検証します: 

```
MATCH (n:person)
WHERE n.name IS NOT NULL
RETURN n.name
```

# 名前付きパスを使用しない (必須でない限り)
<a name="best-practices-content-10"></a>

 クエリの名前付きパスには常に追加コストがかかります。これにより、レイテンシーとメモリ使用量の増加というペナルティが発生する可能性があります。次のクエリについて考えます。

```
MATCH p = (n)-[:commentedOn]->(m)
WITH p, m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH p, m, n, distinct(o) as o1
RETURN p, m.name, n.name, o1.name
```

 上記のクエリでは、ノードのプロパティのみを知りたいと仮定すると、パス「p」を使用する必要はありません。名前付きパスを変数として指定することで、DISTINCT を使用した集約オペレーションは時間とメモリ使用量の両方でコストがかかります。上記のクエリのより最適化されたバージョンは次のとおりです。

```
MATCH (n)-[:commentedOn]->(m)
WITH m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH m, n, distinct(o) as o1
RETURN m.name, n.name, o1.name
```

# COLLECT(DISTINCT()) を避ける
<a name="best-practices-content-11"></a>

**注記**  
エンジンバージョン [1.4.7.0](engine-releases-1.4.7.0.md) 以降、この推奨書き換えは不要になりました。

 COLLECT(DISTINCT()) は、個別の値を含むリストが作成されるたびに使用されます。COLLECT は集約関数であり、グループ化は同じステートメントに射影されている追加のキーに基づいて行われます。DISTINCT を使用すると、入力は複数のチャンクに分割され、各チャンクは削減する 1 つのグループを示します。グループ数が増えると、パフォーマンスに影響が及びます。Neptune では、リストを実際に収集/形成する前に DISTINCT を実行する方がはるかに効率的です。これにより、グループ化はチャンク全体のグループ化キーで直接実行できます。

 次のクエリについて考えます。

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH n, collect(distinct(p.post_id)) as post_list
RETURN n, post_list
```

 このクエリを記述する最適な方法は次のとおりです。

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH DISTINCT n, p.post_id as postId
WITH n, collect(postId) as post_list
RETURN n, post_list
```

# すべてのプロパティ値を取得するときに、個々のプロパティルックアップよりもプロパティ関数を優先する
<a name="best-practices-content-12"></a>

 `properties()` 関数は、エンティティのすべてのプロパティを含むマップを返すために使用され、プロパティを個別に返すよりもはるかに効率的です。

 `Person` ノードに、`firstName`、`lastName`、`age`、`dept`、および `company` の 5 つのプロパティが含まれていると仮定すると、次のクエリが推奨されます。

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN properties(n) as personDetails
```

 以下を使用することは推奨されません。

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN n.firstName, n.lastName, n.age, n.dept, n.company
    
=== OR ===
    
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN {firstName: n.firstName, lastName: n.lastName, age: n.age, 
department: n.dept, company: n.company} as personDetails
```

# クエリの外部で静的計算を実行する
<a name="best-practices-content-13"></a>

 クライアント側で静的計算 (単純な数学/文字列演算) を解決することをお勧めします。作成者より 1 歳年上または年下のすべてのユーザーを検索する場合の例を考えてみます。

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= ($age + 1)
RETURN m
```

 ここで、`$age` はパラメータを介してクエリに挿入され、固定値に追加されます。その後、この値は `p.age` と比較されます。代わりに、クライアント側で追加を行い、計算値をパラメータ \$1ageplusone として渡すことをお勧めします。これにより、クエリエンジンは最適化されたプランを作成し、各受信行の静的計算を回避できます。これらのガイドラインに従うと、より効率的なクエリは次のとおりです。

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= $ageplusone
RETURN m
```

# 個々のステートメントの代わりに UNWIND を使用したバッチ入力をする
<a name="best-practices-content-14"></a>

 異なる入力に対して同じクエリを実行する必要がある場合は、入力ごとに 1 つのクエリを実行するのではなく、入力バッチに対してクエリを実行する方がはるかにパフォーマンスが高くなります。

 ノードのセットでマージする場合は、入力ごとにマージクエリを実行することができます。

```
MERGE (n:Person {`~id`: $id})
SET n.name = $name, n.age = $age, n.employer = $employer
```

 パラメータの入力例: 

```
params = {id: '1', name: 'john', age: 25, employer: 'Amazon'}
```

 上記のクエリは、入力ごとに実行する必要があります。このアプローチは有効ですが、大量の入力セットに対しては、数多くのクエリの実行が必要となる場合があります。このシナリオでは、バッチ処理によってサーバーで実行されるクエリの数を減らせるほか、全体的なスループットを向上させることができます。

 次のパターンを使用します。

```
UNWIND $persons as person
MERGE (n:Person {`~id`: person.id})
SET n += person
```

 パラメータの入力例: 

```
params = {persons: [{id: '1', name: 'john', age: 25, employer: 'Amazon'}, 
{id: '2', name: 'jack', age: 28, employer: 'Amazon'},
{id: '3', name: 'alice', age: 24, employer: 'Amazon'}...]}
```

 ワークロードに最適なバッチサイズを判断するには、さまざまなサイズで試すことをお勧めします。

# ノード/リレーションシップにカスタム ID を使用する
<a name="best-practices-content-15"></a>

 Neptune では、ノードと関係に明示的に ID を割り当てることができます。ID が有用であるためには、データセット内でグローバルに一意であり、決定論的である必要があります。プロパティと同様、決定論的な ID はルックアップまたはフィルタリングメカニズムとして使用できます。ただし、クエリ実行の観点から見れば、プロパティを使用するよりも ID を使用することが最適と言えます。カスタム ID の使用にはいくつかの利点があります。
+  既存のエンティティのプロパティは null にできますが、ID が存在する必要があります。これにより、クエリエンジンは実行時に最適化された結合を使用できます。
+  同時ミューテーションクエリを実行する場合、ID を使用してノードにアクセスすると、[同時変更例外](https://docs.aws.amazon.com//neptune/latest/userguide/transactions-exceptions.html) (CMEs) の可能性が大幅に低下します。これは、一意性が強制されるため、プロパティよりも ID に対するロックがより少ないためです。
+  ID を使用すると、プロパティとは異なり、Neptune が ID の一意性を強制するため、重複データが作成される可能性を回避できます。

 次のクエリ例では、カスタム ID を使用しています。

**注記**  
 プロパティ `~id` は ID を指定するために使用されますが、`id` は単に他のプロパティとして保存されます。

```
CREATE (n:Person {`~id`: '1', name: 'alice'})
```

 カスタム ID を使用しない場合: 

```
CREATE (n:Person {id: '1', name: 'alice'})
```

 後者のメカニズムを使用する場合、一意性の強制はなく、後でクエリを実行できます。

```
CREATE (n:Person {id: '1', name: 'john'})
```

 これにより、`john` という名前の 2 番目のノード `id=1` が作成されます。このシナリオでは、`id=1` を持つノードが 2 つあり、それぞれに異なる名前 (alice と john) が付けられています。

# クエリで \$1id 計算を実行しない
<a name="best-practices-content-16"></a>

 クエリでカスタム ID を使用する場合は、常にクエリの外部で静的計算を実行し、パラメータにこれらの値を指定します。静的値を指定すると、エンジンはルックアップを最適化し、これらの値のスキャンとフィルタリングを回避できます。

 データベースに存在するノード間にエッジを作成する方法の 1 つは次のとおりです。

```
UNWIND $sections as section
MATCH (s:Section {`~id`: 'Sec-' + section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 パラメータの入力例: 

```
parameters={sections: [{id: '1'}, {id: '2'}]}
```

 上記のクエリでは、セクションの `id` がクエリで計算されています。計算が動的であるため、エンジンは ID を静的にインライン化できず、すべてのセクションノードをスキャンすることになります。その後、エンジンは必要なノードのポストフィルタリングを実行します。これは、データベースに多数のセクションノードがある場合にコストがかかる可能性があります。

 これを実現するより良い方法は、データベースに渡される ID に `Sec-` を付加することです。

```
UNWIND $sections as section
MATCH (s:Section {`~id`: section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 パラメータの入力例: 

```
parameters={sections: [{id: 'Sec-1'}, {id: 'Sec-2'}]}
```

# 複数のノードの更新/移行
<a name="best-practices-merge-multiple-nodes"></a>

 複数のノードで `MERGE` または `CREATE` クエリを実行する場合は、ノードごとに MERGE/CREATE 句を使用するのではなく、単一の MERGE/CREATE 句と組み合わせて `UNWIND` を使用することをお勧めします。1 つのノードに 1 つの句を使用するクエリは、各行を最適化する必要があるため、実行プランが非効率になります。これにより、クエリの実行時間の大部分が、実際の更新ではなく静的処理に費やされます。

 ノードあたりの 1 つの句は、ノード数の増加に合わせてスケールされないため、最適ではありません。

```
MERGE (p1:Person {name: 'NameA'})
ON CREATE SET p1 += {prop1: 'prop1V1', prop2: 'prop2V1'}
MERGE (p2:Person {name: 'NameB'})
ON CREATE SET p2 += {prop1: 'prop1V2', prop2: 'prop2V2'}
MERGE (p3:Person {name: 'NameC'})
ON CREATE SET p3 += {prop1: 'prop1V3', prop2: 'prop1V3'}
```

 `UNWIND` を 1 つの MERGE/CREATE 句と組み合わせて使用すると、同じ動作でもより最適な実行プランが可能になります。これを念頭に置いて、変更されたクエリは次のようになります。

```
## If not using custom id for nodes/relationship
UNWIND [{name: 'NameA', prop1: 'prop1V1', prop2: 'prop2V1'}, {name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {name: props.name})
ON CREATE SET p = props

## If using custom id for nodes/relationship
UNWIND [{`~id`: '1', 'name': 'NameA', 'prop1: 'prop1V1', prop2: 'prop2V1'}, {`~id`: '2', name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {`~id`: '3', name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {`~id`: props.id})
ON CREATE SET p = removeKeyFromMap(props, '~id')
```

# SPARQL を使用した Neptune のベストプラクティス
<a name="best-practices-sparql"></a>

Neptune で SPARQL クエリ言語を使用する場合は、これらのベストプラクティスに従ってください。Neptune で SPARQL を使用する方法については、[SPARQL を使用した Neptune グラフへのアクセス](access-graph-sparql.md)を参照してください。

**Topics**
+ [デフォルトですべての名前が付いたグラフのクエリの実行](best-practices-sparql-query.md)
+ [ロード用に名前付きのグラフを指定する](best-practices-sparql-graph.md)
+ [クエリで FILTER、FILTER...IN、および VALUES を選択する](best-practices-sparql-batch.md)

# デフォルトですべての名前が付いたグラフのクエリの実行
<a name="best-practices-sparql-query"></a>

Amazon Neptune はすべてのトリプルを名前が付いたグラフに関連付けます。デフォルトグラフはすべての名前が付いたグラフの総合として定義されます。

`GRAPH` キーワードや構成 (`FROM NAMED` など) を使用してグラフを明示的に指定せずに SPARQL クエリを送信すると、Neptune は常に DB インスタンスのすべてのトリプルを考慮します。たとえば、次のクエリはすべてのトリプルを Neptune SPARQL エンドポイントから返します。

```
SELECT * WHERE { ?s ?p ?o }
```

1 つ以上のグラフに表されるトリプルは、1 度だけ返されます。

デフォルトグラフ仕様についての詳細は、SPARQL 1.1 クエリ言語仕様の「[RDF データセット](https://www.w3.org/TR/sparql11-query/#rdfDataset)」セクションを参照してください。

# ロード用に名前付きのグラフを指定する
<a name="best-practices-sparql-graph"></a>

Amazon Neptune はすべてのトリプルを名前が付いたグラフに関連付けます。トリプルのロード、挿入あるいは更新時に名前が付いたグラフを指定しない場合、Neptune は URI `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph` が定義するフォールバックの名前付きグラフを使用します。

Neptune バルクローダーを使用する場合、`parserConfiguration: namedGraphUri` パラメータを使用して、すべてのトリプル (または 4 番目の位置に空白がある四角形) を使用するように名前付きのグラフを指定できます。Neptune ローダー `Load` コマンド構文についての詳細は、[Neptune ローダーコマンド](load-api-reference-load.md)を参照してください。

# クエリで FILTER、FILTER...IN、および VALUES を選択する
<a name="best-practices-sparql-batch"></a>

SPARQL クエリに値を挿入するには、`FILTER`、`FILTER...IN`、`VALUES` の 3 つの基本的な方法があります。

たとえば、単一のクエリで複数の人から友人を検索するとします。`FILTER` を使用して、クエリを次のように構築します。

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. FILTER (?s = ex:person1 || ?s = ex:person2)}
```

これにより、`?s` または `ex:person1` への `ex:person2` バウンドがあり、`foaf:knows` というラベル付きの出ていく辺があるグラフにあるすべてのトリプルを返します。

また、同等の結果を返す `FILTER...IN` を使用してクエリを作成することができます。

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. FILTER (?s IN (ex:person1, ex:person2))}
```

この場合は同等の結果を返す `VALUES` を使用してクエリを作成することもできます。

```
  PREFIX ex: <https://www.example.com/>
  PREFIX foaf : <http://xmlns.com/foaf/0.1/>

  SELECT ?s ?o
  WHERE {?s foaf:knows ?o. VALUES ?s {ex:person1 ex:person2}}
```

多くの場合、これらのクエリは意味的に同等ですが、2 つの `FILTER` バリアントが `VALUES` バリエーションと異なる場合があります。
+ 最初のケースは、同じ個人を 2 回挿入するなど、重複した値を挿入する場合です。この場合、`VALUES` クエリには、結果の重複内容が含まれています。このような重複は、`DISTINCT` 句に `SELECT` を追加して、明示的に排除することができます。しかし、重複した値の挿入に対して、クエリ結果での重複が必要になるような状況が発生する可能性があります。

  ただし、`FILTER` バージョンと `FILTER...IN` バージョンでは、同じ値が繰り返し現れるとき、1 回だけ値を抽出します。
+ 2 番目のケースは `VALUES` が常に完全一致を実行する一方で、`FILTER` ではタイププロモーションが適用され、あいまい一致を実行する場合があることに関係しています。

  たとえば、`"2.0"^^xsd:float` などのリテラルを値に含める場合、`VALUES` クエリは、リテラル値とデータ型を含め、このリテラルに正確に一致します。

  対照的に、`FILTER` では、これらの数値リテラルに対してあいまい一致が出されます。一致には、同じ値で、`xsd:double` など、数値データ型が異なるリテラルを含めることができます。
**注記**  
`FILTER` と `VALUES` の動作に、文字列リテラルまたは URI を列挙する際の違いはありません。

`FILTER` と `VALUES` の違いは、最適化と生成されるクエリ評価戦略に影響を与える可能性があります。ユースケースであいまい一致を必要としない限り、`VALUES` を使用することをお勧めします。これにより、型キャストに関連する特殊なケースを確認するのを避けることができます。その結果、`VALUES` では、高速で動作し、低コストの効率的なクエリを作成できることがよくあります。