Dynamodbでネストされた属性を効率的にクエリするにはどうすればよいですか?
以下のようなドキュメント構造があり、関連情報を(参照するのではなく)ドキュメント自体に保存できます。
セミナーはコースと一緒に照会される可能性が高いため、コースにネストされたセミナーを保存することは理にかなっています(それらはすべてコース固有です。つまり、コースには多くのセミナーがあり、セミナーはコースに属しています)。
移行元のCouchDBでは、クエリ用にネストされた属性を投影するビューを作成できました。トップレベルの属性ではないものをdynamodbセカンダリインデックスに投影できないことを理解しているため、このアプローチは機能しないようです。
これにより、質問に戻ります。ネストされた属性をインデックスのキーとして使用できない場合、スキャンせずに効率的にクエリするにはどうすればよいですか?
たとえば、ネルソンマンデラシアターへの平均出席を取得したい場合、「ネルソンマンデラシアター」の場所にあるすべてのセミナーのregistrations
とattendees
の値をクエリできません。スキャンに頼る?
{
“course_id”: “ABC-1234567”,
“course_name”: “Statistics 101”,
“tutors”: [“Cognito-sub-1”, “Cognito-sub-2”],
“seminars”: [
{
“seminar_id”: “XXXYYY-12345”,
“Epoch_time”: “123456789”,
“duration”: “5400”,
“location”: “Nelson Mandela Theatre”,
“name”: “How to lie with statistics”,
“registrations”: “92”,
“attendees”: “61”
},
{
“seminar_id”: “BBBCCC-44444”,
“Epoch_time”: “155555555”,
“duration”: “5400”,
“location”: “Nelson Mandela Theatre”,
“name”: “Statistical significance for dog owners”,
“registrations”: “244”,
“attendees”: “240”
},
{
“seminar_id”: “XXXAAA-54321”,
“Epoch_time”: “223456789”,
“duration”: “4000”,
“location”: “Starbucks”,
“name”: “Is feral cat population growth a leading indicator for the S&P 500?”,
“registrations”: “40”
}
]
}
{
“course_id”: “CJX-5553389”,
“course_name”: “Cat Health 101”,
“tutors”: [“Cognito-sub-4”, “Cognito-sub-9”],
“seminars”: [
{
“seminar_id”: “TTRHJK-43278”,
“Epoch_time”: “123456789”,
“duration”: “5400”,
“location”: “Catwoman Hall”,
“name”: “Emotional support octopi for cats”,
“registrations”: “88”,
“attendees”: “87”
},
{
“seminar_id”: “BBBCCC-44444”,
“Epoch_time”: “123666789”,
“duration”: “5400”,
“location”: “Nelson Mandela Theatre”,
“name”: “Statistical significance for cat owners”,
“registrations”: “44”,
“attendees”: “44”
}
]
}
ネストされた属性(Dynamodbのドキュメントデータタイプなど)のインデックスは作成できません。
ドキュメントタイプ–ドキュメントタイプは、JSONドキュメントで見られるような、ネストされた属性を持つ複雑な構造を表すことができます。ドキュメントタイプはリストとマップです。
クエリAPI:-
クエリ操作は、主キー属性値のみを検索し、キー属性値の比較演算子のサブセットをサポートして、検索プロセスを絞り込みます。
スキャンAPI:-
スキャン操作はテーブル全体をスキャンします。完全なスキャン後に、結果に適用するフィルターを指定して、返される値を絞り込むことができます。
Query API
を使用するには、ハッシュキーの値が必要です。 OPには、ハッシュキー値が使用可能であるという情報はありません。 OPに従って、データはDynamodb location
データ型内にあるList
属性によって照会される必要があります。今、オプションはGSIを見ることです。
[〜#〜] gsi [〜#〜] の詳細をお読みください。ルールの1つは、GSIは最上位の属性のみを使用して作成できることです。そのため、場所を使用してインデックスを作成することはできません。
したがって、Query APIを使用するためにGSIを作成することも除外されています。
インデックスキー属性は、ベーステーブルのトップレベルの文字列、数値、またはバイナリ属性で構成できます。他のスカラー型、ドキュメント型、およびセット型は許可されていません。
上記の理由により、ハッシュキーの値が利用できない場合、Query APIを使用してlocation
属性に基づいてデータを取得することはできません。
ハッシュキーの値が利用可能な場合、FilterExpression
を使用してデータをフィルタリングできます。複合リストのデータ型に存在するデータをフィルタリングする唯一の方法は、CONTAINS
関数です。 CONTAINS
関数を使用するには、オカレンスのすべての属性がデータと一致する必要があります(つまり、seminar_id、場所、期間、その他すべての属性)。そのため、現在のデータモデルを使用してOPで言及されているユースケースを満たすことは、絶対に不可能です。
提案された代替ソリューション:-
以下で説明するようにデータ構造を再モデル化することは、問題を解決するためのオプションになる可能性があります。 Query APIを使用してユースケースを満たすために利用できる他のソリューションは絶対にありません。
メインテーブル:-
コースID-ハッシュキー
セミナーID-ソートキー
GSI:-
セミナーの場所-ハッシュキー
コースID-ソートキー
DynamoDBテーブルでは、各キー値は一意である必要があります。ただし、 グローバルセカンダリインデックス のキー値は一意である必要はありません。
これで、GSIでクエリAPIを使用して、Seminar location
がNelson Mandela Theatre
と等しいデータを取得できます。値がわかっている場合は、クエリAPIでコースIDを使用できます。クエリAPIは、結果セットに複数のアイテムを与える可能性があります。一部の非キー属性に基づいてデータをさらにフィルタリングする場合は、FilterExpression
を使用できます。
スキャンストアでオブジェクトを機能させるには、{"language": "[{\" language\":\" Male\"、\" proficiency\":\" Female\"}のような文字列形式でオブジェクトを機能させることができます。 ] "}` `そして、スキャン操作言語を実行できます:{含む:"男性 "}
クライアント側でJSON.parse(language)を実行できます
ドキュメントパス を使用して値をフィルタリングできます。セミナーの場所をドキュメントのパスとして使用します。
私はまだDynamoDBでの経験はありませんが、次のプロジェクトで使用することを計画しているので、DynamoDBの研究を始めました。
AWSドキュメントから理解できる限り、あなたの質問への答えは、ネストされた属性に対して効率的にクエリを実行することは不可能です。
ベストプラクティス 、具体的には DynamoDBでセカンダリインデックスを使用するためのベストプラクティス を見ると、次のように、同じパーティションキーで異なるラインタイプを使用することが適切なアプローチであると理解できます。 ここ 。次に、同じcourse_idの下に、一般的な並べ替えキー(sk)があります。最初のレジスタはコースのデータを含むsk = 'Details'を持ち、次に「seminar-1」などのその他のレジスタとそのデータを持ちます。次に、クエリするセミナーのプロパティをSGI(Secondary Global Index)として設定します。これは、テーブルごとに5つのSGIしか持つことができないことを念頭に置いてください。
それが役に立てば幸い。
これは here の例で、フィルター式を使用しています。スキャン操作を使用していますが、スキャンの代わりにクエリに同様のものを適用できます( [〜#〜] api [〜#〜] を見てください):
{
"TableName": "MyTable",
"FilterExpression": "#k_Compatible.#k_RAM = :v_Compatible_RAM",
"ExpressionAttributeNames": {
"#k_Compatible": "Compatible",
"#k_RAM": "RAM"
},
"ExpressionAttributeValues": {
":v_Compatible_RAM": "RAM1"
}
}