條件表達式 - AWS AppSync

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

條件表達式

當您使用PutItemUpdateItemDeleteItem DynamoDB 作業在 DynamoDB 中變更物件時,您可以選擇性地指定條件運算式,以根據執行作業前已在 DynamoDB 中的物件狀態控制請求是否成功。

AWS AppSync DynamoDB 解析器允許在、和DeleteItem請求對應文件中指定條件運算式 PutItemUpdateItem,以及在條件失敗且物件未更新時應遵循的策略。

範例 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 解析器會針對 GraphQL 回應區段中的data欄位中的突變和 DynamoDB 中物件的目前值傳回錯誤。error不過, AWS AppSync DynamoDB 解析器提供一些額外的功能,可協助開發人員處理一些常見的邊緣案例:

  • 如果 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

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

strategy

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

Reject

突變失敗,並且 GraphQL 回應error區段中某個data欄位中 DynamoDB 中物件的突變和目前值出現錯誤。

Custom

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

lambdaArn

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

處理條件檢查失敗

依預設,當條件檢查失敗時, AWS AppSync DynamoDB 解析器會針對 GraphQL 回應區段中的data欄位中的突變和 DynamoDB 中物件的目前值傳回錯誤。error不過, AWS AppSync DynamoDB 解析器提供一些額外的功能,可協助開發人員處理一些常見的邊緣案例:

  • 如果 AWS AppSync DynamoDB 解析器可以判斷 DynamoDB 中的目前值是否符合所需的結果,它會將作業視為無論如何都成功。

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

此程序的流程圖為:

檢查所需的結果

當條件檢查失敗時, AWS AppSync DynamoDB 解析器會執行 DynamoDB 請求,以從 GetItem 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 } }

    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 解析器會針對突變傳回錯誤,而 DynamoDB 中物件的目前值也會傳回 GraphQL 回應區段中的data欄位中。error從 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", "data": { "Name": "Steve", "theVersion": 8 }, ... } ] }

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

遵循「自定義」策略

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

  • reject 變動。這會告訴 AWS AppSync DynamoDB 解析器的行為如同已設定策略一樣Reject,傳回 DynamoDB 中物件的突變錯誤和目前值,如上一節所述。

  • discard 變動。這會告知 AWS AppSync DynamoDB 解析程式以無訊息方式忽略條件檢查失敗,並傳回 DynamoDB 中的值。

  • retry 變動。這會告訴 AWS AppSync DynamoDB 解析器使用新的請求對應文件重試突變。

Lambda 叫用要求

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 } }

    無法指定使用不同的操作或索引鍵。 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) };