條件表達式 - AWS AppSync GraphQL

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

條件表達式

當您使用 PutItemUpdateItem和 DynamoDB 操作來變更 DeleteItem DynamoDB 中的物件時,您可以選擇指定條件表達式,以根據在執行操作之前在 DynamoDB 中已存在的物件狀態來控制請求是否成功。

The AWS AppSync DynamoDB 解析程式允許在 PutItem、 中指定條件表達式UpdateItem,並DeleteItem請求映射文件,以及如果條件失敗且未更新物件時要遵循的策略。

範例 1

以下 PutItem 映射文件沒有條件表達式。因此,即使具有相同索引鍵的項目已存在,也會將項目放入 DynamoDB,藉此覆寫現有項目。

{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } } }

範例 2

下列PutItem映射文件確實具有條件表達式,只有在 DynamoDB 中不存在具有相同索引鍵的項目時,才會允許操作成功。

{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "condition" : { "expression" : "attribute_not_exists(id)" } }

根據預設,如果條件檢查失敗, AWS AppSync DynamoDB 解析程式會傳回變動的錯誤。不過, AWS AppSync DynamoDB 解析程式提供一些額外的功能,以協助開發人員處理一些常見的邊緣案例:

  • If AWS AppSync DynamoDB 解析程式可以判斷 DynamoDB 中的目前值是否符合所需的結果,它將操作視為成功。

  • 您可以設定解析程式來叫用自訂 Lambda 函數,以決定 AWS AppSync DynamoDB 解析程式應如何處理失敗,而不是傳回錯誤。

處理條件檢查失敗一節將更詳細地說明這些內容。

如需 DynamoDB 條件表達式的詳細資訊,請參閱 DynamoDB ConditionExpressions 文件

指定條件

PutItemUpdateItemDeleteItem 要求映射文件都允許可指定選用的 condition 區段。若省略,則不會有條件檢查。若指定,條件必須為 true,操作才會成功。

condition 區段的結構如下:

"condition" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value }, "equalsIgnore" : [ "version" ], "consistentRead" : true, "conditionalCheckFailedHandler" : { "strategy" : "Custom", "lambdaArn" : "arn:..." } }

下列欄位指定條件:

expression

更新表達式本身。如需如何編寫條件表達式的詳細資訊,請參閱 DynamoDB ConditionExpressions 文件。必須指定此欄位。

expressionNames

表達式屬性名稱預留位置的替代,形式為索引鍵值對。索引鍵對應至表達式中使用的名稱預留位置,且值必須是對應於 DynamoDB 中項目屬性名稱的字串。此欄位為選用的,應只能填入於表達式中所用表達式屬性名稱預留位置的替代。

expressionValues

表達式屬性值預留位置的替代,形式為索引值對。鍵對應用於表達式的值預留位置,值必須是類型值。如需如何指定「類型值」的詳細資訊,請參閱類型系統 (請求映射)。此必須指定。此欄位為選用的,應只能填入用於表達式中表達式屬性值預留位置的替代。

其餘欄位會告知 AWS AppSync DynamoDB 解析程式如何處理條件檢查失敗:

equalsIgnore

當條件檢查在使用 PutItem操作時失敗時, AWS AppSync DynamoDB 解析程式會將目前在 DynamoDB 中的項目與其嘗試寫入的項目進行比較。如果兩者相同,則操作視為成功。您可以使用 equalsIgnore 欄位來指定執行該比較時 AWS AppSync 應忽略的屬性清單。例如,如果唯一的差異是version屬性,則會將操作視為成功。此欄位為選用欄位。

consistentRead

當條件檢查失敗時, AWS AppSync 會使用強式一致讀取,從 DynamoDB 取得項目的目前值。您可以使用此欄位,指示 AWS AppSync DynamoDB 解析程式改用最終一致讀取。此欄位為選用,預設值為 true

conditionalCheckFailedHandler

本節可讓您指定 AWS AppSync DynamoDB 解析程式在將 DynamoDB 中的目前值與預期結果進行比較後,如何處理條件檢查失敗。此區段為選用。若省略,則會預設為 Reject 策略。

strategy

AWS AppSync DynamoDB 解析程式在將 DynamoDB 中的目前值與預期結果進行比較後所採用的策略。此欄位為必填,且採用下列可能值:

Reject

變動會失敗,並會將錯誤新增至 GraphQL 回應。

Custom

The AWS AppSync DynamoDB 解析程式會叫用自訂 Lambda 函數,以決定如何處理條件檢查失敗。當 strategy 設定為 CustomlambdaArn 欄位必須包含要叫用 Lambda 函數的 ARN。

lambdaArn

要叫用之 Lambda 函數的 ARN,決定 AWS AppSync DynamoDB 解析程式應如何處理條件檢查失敗。只有在 strategy 設定為 Custom 時,此欄位才必須指定。如需如何使用此功能的詳細資訊,請參閱處理條件檢查失敗

處理條件檢查失敗

根據預設,當條件檢查失敗時, AWS AppSync DynamoDB 解析程式會傳回 DynamoDB 中物件的變動和目前值的錯誤。不過, AWS AppSync DynamoDB 解析程式提供一些額外的功能,以協助開發人員處理一些常見的邊緣案例:

  • If AWS AppSync DynamoDB 解析程式可以判斷 DynamoDB 中的目前值是否符合所需的結果,它將操作視為成功。

  • 您可以設定解析程式來叫用自訂 Lambda 函數,以決定 AWS AppSync DynamoDB 解析程式應如何處理失敗,而不是傳回錯誤。

此程序的流程圖為:

檢查所需結果

當條件檢查失敗時, AWS AppSync DynamoDB 解析程式會執行 GetItem DynamoDB 請求,從 DynamoDB 取得項目的目前值。在預設情況下,它會使用強式一致性讀取,但這可使用 condition 區塊中的 consistentRead 欄位來設定,並和預期結果進行比較:

  • 對於 PutItem操作, AWS AppSync DynamoDB 解析程式會將目前的值與其嘗試寫入的值進行比較,從比較中排除 equalsIgnore 中列出的任何屬性。如果項目相同,它會將操作視為成功,並傳回從 DynamoDB 擷取的項目。否則,其將按照設定的策略。

    例如,如果 PutItem 要求映射文件外觀如下:

    { "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "attributeValues" : { "name" : { "S" : "Steve" }, "version" : { "N" : 2 } }, "condition" : { "expression" : "version = :expectedVersion", "expressionValues" : { ":expectedVersion" : { "N" : 1 } }, "equalsIgnore": [ "version" ] } }

    而目前在 DynamoDB 中的項目外觀如下:

    { "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }

    The AWS AppSync DynamoDB 解析程式會將嘗試寫入的項目與目前值進行比較,查看唯一差異是 version 欄位,但因為它設定為忽略 version 欄位,它會將操作視為成功,並傳回從 DynamoDB 擷取的項目。

  • 對於 DeleteItem操作, AWS AppSync DynamoDB 解析程式會檢查以確認項目已從 DynamoDB 傳回。如果沒有項目傳回,則操作視為成功。否則,其將按照設定的策略。

  • 對於 UpdateItem操作, AWS AppSync DynamoDB 解析程式沒有足夠的資訊來判斷目前 DynamoDB 中的項目是否符合預期結果,因此遵循設定的策略。

如果 DynamoDB 中物件的目前狀態與預期結果不同, AWS AppSync DynamoDB 解析程式會遵循已設定的策略,以拒絕變動或叫用 Lambda 函數來決定下一步該怎麼做。

遵循「拒絕」策略

遵循Reject策略時, AWS AppSync DynamoDB 解析程式會傳回變動的錯誤。

例如,假設變動要求如下:

mutation { updatePerson(id: 1, name: "Steve", expectedVersion: 1) { Name theVersion } }

如果 DynamoDB 傳回的項目如下所示:

{ "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }

而回應映射範本如下所示:

{ "id" : $util.toJson($context.result.id), "Name" : $util.toJson($context.result.name), "theVersion" : $util.toJson($context.result.version) }

GraphQL 回應的外觀如下所示:

{ "data": null, "errors": [ { "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)" "errorType": "DynamoDB:ConditionalCheckFailedException", ... } ] }

此外,如果傳回物件中的任何欄位皆由其他解析程式填入,且變動成功,則在 error 區段中傳回物件時,不會解析這些物件。

遵循「自訂」策略

遵循Custom策略時, AWS AppSync DynamoDB 解析程式會叫用 Lambda 函數來決定接下來要做什麼。Lambda 函數會選擇下列其中一個選項:

  • reject 變動。這可讓 AWS AppSync DynamoDB 解析程式表現得好像已設定的策略是 Reject,傳回 DynamoDB 中物件的變動錯誤和目前值,如上一節所述。

  • discard 變動。這可讓 AWS AppSync DynamoDB 解析程式以無提示方式忽略條件檢查失敗,並在 DynamoDB 中傳回 值。

  • retry 變動。這會通知 AWS AppSync DynamoDB 解析程式使用新的請求映射文件重試變動。

Lambda 叫用要求

The AWS AppSync DynamoDB 解析程式會叫用 中指定的 Lambda 函數lambdaArn。它會使用資料來源上所設定的相同 service-role-arn。叫用承載的結構如下:

{ "arguments": { ... }, "requestMapping": {... }, "currentValue": { ... }, "resolver": { ... }, "identity": { ... } }

欄位定義如下:

arguments

來自 GraphQL 變動的引數。這與可在 $context.arguments 中要求映射文件取得的引數相同。

requestMapping

此操作的要求映射文件。

currentValue

DynamoDB 中物件的目前值。

resolver

有關 AWS AppSync 解析程式的資訊。

identity

發起人的相關資訊。這與可在 $context.identity 中要求映射文件取得的身分資訊相同。

承載的完整範例:

{ "arguments": { "id": "1", "name": "Steve", "expectedVersion": 1 }, "requestMapping": { "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "1" } }, "attributeValues" : { "name" : { "S" : "Steve" }, "version" : { "N" : 2 } }, "condition" : { "expression" : "version = :expectedVersion", "expressionValues" : { ":expectedVersion" : { "N" : 1 } }, "equalsIgnore": [ "version" ] } }, "currentValue": { "id" : { "S" : "1" }, "name" : { "S" : "Steve" }, "version" : { "N" : 8 } }, "resolver": { "tableName": "People", "awsRegion": "us-west-2", "parentType": "Mutation", "field": "updatePerson", "outputType": "Person" }, "identity": { "accountId": "123456789012", "sourceIp": "x.x.x.x", "user": "AIDAAAAAAAAAAAAAAAAAA", "userArn": "arn:aws:iam::123456789012:user/appsync" } }

Lambda 叫用回應

Lambda 函數可以檢查調用承載,並套用任何商業邏輯,以決定 AWS AppSync DynamoDB 解析程式應如何處理故障。處理條件檢查失敗有三個選項:

  • reject 變動。此選項的回應承載必須具有此架構:

    { "action": "reject" }

    這可讓 AWS AppSync DynamoDB 解析程式表現得好像已設定的策略為 Reject,傳回 DynamoDB 中物件的變動錯誤和目前值,如上節所述。

  • discard 變動。此選項的回應承載必須具有此架構:

    { "action": "discard" }

    這可讓 AWS AppSync DynamoDB 解析程式以無提示方式忽略條件檢查失敗,並在 DynamoDB 中傳回值。

  • retry 變動。此選項的回應承載必須具有此架構:

    { "action": "retry", "retryMapping": { ... } }

    這會通知 AWS AppSync DynamoDB 解析程式使用新的請求映射文件重試變動。retryMapping 區段的結構取決於 DynamoDB 操作,並且是該操作完整請求映射文件的子集。

    若是 PutItemretryMapping 區段的結構如下。如需 attributeValues 欄位的描述,請參閱 PutItem

    { "attributeValues": { ... }, "condition": { "equalsIgnore" = [ ... ], "consistentRead" = true } }

    若是 UpdateItemretryMapping 區段的結構如下。如需 update 區段的描述,請參閱 UpdateItem

    { "update" : { "expression" : "someExpression" "expressionNames" : { "#foo" : "foo" }, "expressionValues" : { ":bar" : ... typed value } }, "condition": { "consistentRead" = true } }

    若是 DeleteItemretryMapping 區段的結構如下。

    { "condition": { "consistentRead" = true } }

    無法指定使用不同的操作或索引鍵。The AWS AppSync DynamoDB 解析程式僅允許對相同物件重試相同的操作。此外,condition 區段不允許指定 conditionalCheckFailedHandler。如果重試失敗, AWS AppSync DynamoDB 解析程式會遵循 Reject策略。

此為 Lambda 函數處理失敗 PutItem 要求的範例。商業邏輯的重點是發起人為何。如果是由 提出jeffTheAdmin,則會重試請求,expectedVersion並從目前 DynamoDB 中的項目更新 version和 。否則,它會拒絕變動。

exports.handler = (event, context, callback) => { console.log("Event: "+ JSON.stringify(event)); // Business logic goes here. var response; if ( event.identity.user == "jeffTheAdmin" ) { response = { "action" : "retry", "retryMapping" : { "attributeValues" : event.requestMapping.attributeValues, "condition" : { "expression" : event.requestMapping.condition.expression, "expressionValues" : event.requestMapping.condition.expressionValues } } } response.retryMapping.attributeValues.version = { "N" : event.currentValue.version.N + 1 } response.retryMapping.condition.expressionValues[':expectedVersion'] = event.currentValue.version } else { response = { "action" : "reject" } } console.log("Response: "+ JSON.stringify(response)) callback(null, response) };