

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 条件表达式
<a name="js-aws-appsync-resolver-reference-dynamodb-condition-expressions"></a>

在您使用 `PutItem`、`UpdateItem` 和 `DeleteItem` DynamoDB 操作变更 DynamoDB 中的对象时，您可以选择指定一个条件表达式，以根据执行操作之前 DynamoDB 中的已有对象状态控制请求是否应成功。

D AWS AppSync ynamoDB 函数允许`PutItem`在`UpdateItem`、`DeleteItem`和请求对象中指定条件表达式，还允许在条件失败且对象未更新时遵循的策略。

## 示例 1
<a name="js-id19"></a>

以下 `PutItem` 请求对象没有条件表达式。因此，即使已存在具有相同键的项目，它也会将项目放置在 DynamoDB 中，从而覆盖现有的项目。

```
import { util } from '@aws-appsync/utils';
export function request(ctx) {
  const { foo, bar, ...values} = ctx.args
  return {
    operation: 'PutItem',
    key: util.dynamodb.toMapValues({foo, bar}),
    attributeValues: util.dynamodb.toMapValues(values),
  };
}
```

## 示例 2
<a name="js-id20"></a>

以下 `PutItem` 对象确实具有一个条件表达式，只有在 DynamoDB 中*不* 存在具有相同键的项目时，该操作才会成功。

```
import { util } from '@aws-appsync/utils';
export function request(ctx) {
  const { foo, bar, ...values} = ctx.args
  return {
    operation: 'PutItem',
    key: util.dynamodb.toMapValues({foo, bar}),
    attributeValues: util.dynamodb.toMapValues(values),
    condition: { expression: "attribute_not_exists(id)" }
  };
}
```

默认情况下，如果条件检查失败，则 AWS AppSync DynamoDB 函数会为突变提供错误。

但是， AWS AppSync DynamoDB 函数提供了一些其他功能来帮助开发人员处理一些常见的边缘情况：
+ 如果 D AWS AppSync ynamoDB 函数可以确定 DynamoDB 中的当前值与所需结果相匹配，则它会将该操作视为成功了。
+ 您可以将函数配置为调用自定义 Lambda 函数来决定 DynamoDB 函数应如何处理故障 AWS AppSync ，而不是返回错误。

在[处理条件检查失败](#condition-check)一节中更详细地介绍了这些内容。

[有关 DynamoDB 条件表达式的更多信息，请参阅 DynamoDB 文档。 ConditionExpressions ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html)

## 指定条件
<a name="js-aws-appsync-resolver-reference-dynamodb-condition-specification"></a>

`PutItem`、`UpdateItem` 和 `DeleteItem` 请求对象都允许指定可选的 `condition` 部分。如果省略，则不会进行条件检查。如果指定，条件必须为 true，操作才能成功。

`condition` 部分具有以下结构：

```
type ConditionCheckExpression = {
  expression: string;
  expressionNames?: { [key: string]: string};
  expressionValues?: { [key: string]: any};
  equalsIgnore?: string[];
  consistentRead?: boolean;
  conditionalCheckFailedHandler?: {
    strategy: 'Custom' | 'Reject';
    lambdaArn?: string;
  };
};
```

下列字段指定条件：

** `expression` **  
更新表达式本身。有关如何编写条件表达式的更多信息，请参阅 [DynamoDB 文档 ConditionExpressions ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html)。必须指定该字段。

** `expressionNames` **  
以键值对形式替换表达式属性名称占位符。键对应于 *expression* 中使用的名称占位符，值必须是与 DynamoDB 中的项目的属性名称对应的字符串。该字段是可选的，只应填充 *expression* 中使用的表达式属性名称占位符的替换内容。

** `expressionValues` **  
以键值对形式替换表达式属性值占位符。键对应于 expression 中使用的值占位符，而值必须为类型化值。有关如何指定“类型化值”的更多信息，请参阅[类型系统（请求映射）](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-typed-values-request)。必须指定此值。该字段是可选的，只应填充 expression 中使用的表达式属性值占位符的替换内容。

其余字段告诉 AWS AppSync DynamoDB 函数如何处理条件检查失败：

** `equalsIgnore` **  
当使用`PutItem`操作时条件检查失败时，D AWS AppSync ynamoDB 函数会将当前在 DynamoDB 中的项目与它尝试写入的项目进行比较。如果两者相同，则它会将操作视为已成功。您可以使用该`equalsIgnore`字段来指定在执行比较时 AWS AppSync 应忽略的属性列表。例如，如果唯一的区别是 `version` 属性，则会将操作视为成功。该字段是可选的。

** `consistentRead` **  
当条件检查失败时，使用强一致性读取从 DynamoDB AWS AppSync 获取项目的当前值。您可以使用此字段告诉 AWS AppSync DynamoDB 函数改用最终一致性读取。该字段是可选的，默认值为 `true`。

** `conditionalCheckFailedHandler` **  
本节允许您指定 D AWS AppSync ynamoDB 函数在将 DynamoDB 中的当前值与预期结果进行比较后如何处理条件检查失败。此部分是可选的。如果省略，它默认为 `Reject` 策略。    
** `strategy` **  
 AWS AppSync DynamoDB 函数在将 DynamoDB 中的当前值与预期结果进行比较后采取的策略。该字段为必填字段，有以下可能的值：    
** `Reject` **  
变更失败，向 GraphQL 响应中添加一个错误。  
** `Custom` **  
 AWS AppSync DynamoDB 函数调用自定义 Lambda 函数来决定如何处理条件检查失败。当 `strategy` 设置为 `Custom` 时，`lambdaArn` 字段必须包含要调用的 Lambda 函数的 ARN。  
** `lambdaArn` **  
要调用的 Lambda 函数的 ARN，用于确定 DynamoDB 函数应如何处理条件 AWS AppSync 检查失败。当 `strategy` 设置为 `Custom` 时，必须指定该字段。有关如何使用该功能的更多信息，请参阅[处理条件检查失败](#condition-check)。

## 处理条件检查失败
<a name="js-aws-appsync-resolver-reference-dynamodb-condition-handling"></a>

当条件检查失败时，D AWS AppSync ynamoDB 函数可以使用该实用程序传递突变的错误和对象的当前值。`util.appendError`但是， AWS AppSync DynamoDB 函数提供了一些其他功能来帮助开发人员处理一些常见的边缘情况：
+ 如果 D AWS AppSync ynamoDB 函数可以确定 DynamoDB 中的当前值与所需结果相匹配，则它会将该操作视为成功了。
+ 您可以将函数配置为调用自定义 Lambda 函数来决定 DynamoDB 函数应如何处理故障 AWS AppSync ，而不是返回错误。

此过程的流程图为：

![\[Flowchart showing process for transforming requests with mutation attempts and value checks.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/DynamoDB-condition-check-failure-handling.png)


### 检查所需的结果
<a name="js-checking-for-the-desired-result"></a>

当条件检查失败时，D AWS AppSync ynamoDB 函数会执行 Dyn `GetItem` amoDB 请求，以从 DynamoDB 获取项目的当前值。默认情况下，它将使用强一致性读取，但这可以使用 `condition` 数据块中的 `consistentRead` 字段进行配置，并将当前值与预期结果进行比较：
+ 对于该`PutItem`操作，D AWS AppSync ynamoDB 函数将当前值与其尝试写入的值进行比较，不包括比较`equalsIgnore`中列出的任何属性。如果项目相同，则将操作视为成功并返回从 DynamoDB 中检索的项目。否则，它将遵循所配置的策略。

  例如，如果 `PutItem` 请求对象如下所示：

  ```
  import { util } from '@aws-appsync/utils';
  export function request(ctx) {
    const { id, name, version} = ctx.args
    return {
      operation: 'PutItem',
      key: util.dynamodb.toMapValues({foo, bar}),
      attributeValues: util.dynamodb.toMapValues({ name, version: version+1 }),
      condition: { 
        expression: "version = :expectedVersion",
        expressionValues: util.dynamodb.toMapValues({':expectedVersion': version}),
        equalsIgnore: ['version']
      }
    };
  }
  ```

  当前位于 DynamoDB 中的项目如下所示：

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

  D AWS AppSync ynamoDB 函数会将其尝试写入的项目与当前值进行比较，发现唯一的区别在于字段，但是由于它配置为忽略`version``version`该字段，因此它会将操作视为成功并返回从 DynamoDB 检索到的项目。
+ 对于该`DeleteItem`操作， AWS AppSync DynamoDB 函数会进行检查以验证是否从 DynamoDB 返回了项目。如果没有返回项目，它会将该操作视为已成功。否则，它将遵循所配置的策略。
+ 对于该`UpdateItem`操作，D AWS AppSync ynamoDB 函数没有足够的信息来确定 DynamoDB 中当前的项目是否与预期结果匹配，因此遵循了配置的策略。

如果 DynamoDB 中对象的当前状态与预期结果不同，则 D AWS AppSync ynamoDB 函数将遵循配置的策略，拒绝变更或调用 Lambda 函数来确定下一步要做什么。

### 遵循“reject”策略
<a name="js-following-the-reject-strategy"></a>

遵循`Reject`策略时， AWS AppSync DynamoDB 函数会返回变异错误。

例如，给定以下变更请求：

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

如果从 DynamoDB 返回的项目如下所示：

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

函数响应处理程序如下所示：

```
import { util } from '@aws-appsync/utils';
export function response(ctx) {
  const { version, ...values } = ctx.result;
  const result = { ...values, theVersion: version };
  if (ctx.error) {
    if (error) {
      return util.appendError(error.message, error.type, result, null);
    }
  }
  return result
}
```

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”策略
<a name="js-following-the-custom-strategy"></a>

遵循`Custom`策略时， AWS AppSync DynamoDB 函数会调用 Lambda 函数来决定下一步要做什么。Lambda 函数选择下列选项之一：
+  `reject` 变更。这告诉 D AWS AppSync ynamoDB 函数的行为就像配置的策略`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 函数可以检查调用负载并应用任何业务逻辑来决定 DynamoDB 函数应如何处理 AWS AppSync 故障。有三种选项可用于处理条件检查失败：
+  `reject` 变更。此选项的响应负载必须具有此结构：

  ```
  {
      "action": "reject"
  }
  ```

  这告诉 D AWS AppSync ynamoDB 函数的行为就像配置的策略`Reject`一样，返回变异错误以及该对象在 DynamoDB 中的当前值，如上一节所述。
+  `discard` 变更。此选项的响应负载必须具有此结构：

  ```
  {
      "action": "discard"
  }
  ```

  这会告诉 AWS AppSync DynamoDB 函数以静默方式忽略条件检查失败，并在 DynamoDB 中返回该值。
+  `retry` 变更。此选项的响应负载必须具有此结构：

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

  这会告诉 AWS AppSync DynamoDB 函数使用新的请求对象重试变更。`retryMapping` 部分的结构取决于 DynamoDB 操作，并且是该操作的完整请求对象的子集。

  对于 `PutItem`，`retryMapping` 部分具有以下结构。有关该`attributeValues`字段的说明，请参见[PutItem](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html#aws-appsync-resolver-mapping-template-reference-dynamodb-putitem)。

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

  对于 `UpdateItem`，`retryMapping` 部分具有以下结构。有关该`update`部分的说明，请参见[UpdateItem](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html#aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem)。

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

  对于 `DeleteItem`，`retryMapping` 部分具有以下结构。

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

  无法指定要使用的不同的操作或键。 AWS AppSync DynamoDB 函数仅允许对同一个对象重试相同的操作。另外，`condition` 部分不允许指定 `conditionalCheckFailedHandler`。如果重试失败，则 Dynamo AWS AppSync DB 函数将遵循策略。`Reject`

在下面的示例中，Lambda 函数处理失败的 `PutItem` 请求。业务逻辑将查看进行调用的用户。如果调用是由 `jeffTheAdmin` 进行的，则会重试该请求，并从当前位于 DynamoDB 的项目中更新 `version` 和 `expectedVersion`。否则，业务逻辑将拒绝变更。

```
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)
};
```