首页 > 解决方案 > AWS Dynamodb 文档客户端 UpdateCommand

问题描述

AWS DynamoDB 文档中的UpdateCommand文档没有很好的记录且难以使用。

我想使用更新命令来获取一个 Javascript 对象并将其中的所有键插入到与主键匹配的对象上。

看着InsertCommand我认为它会像这样简单:

async updateItem(tableName: string, primaryKeyName: string, id: string, item: { [key: string]: any }) {
    const input: UpdateCommandInput = {
      Key: {
        [primaryKeyName]: id,
      },
      AttributeUpdates: item,
      TableName: tableName,
    };

    const command = new UpdateCommand(input);

    return await this.client.send(command);
  }

但这似乎失败了。

标签: node.jstypescriptamazon-web-servicesamazon-dynamodb

解决方案


现在,当我环顾四周时,很难找到文档,但我设法通过几个不同的来源将它拼凑在一起。

我创建了一个函数,它将获取一个对象,然后用主键引用的对象上的所有这些属性进行覆盖。我希望这对其他难以弄清楚如何UpdateCommand工作的人有所帮助。

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
  UpdateCommandInput,
  UpdateCommand,
  DynamoDBDocumentClient,
} from '@aws-sdk/lib-dynamodb';

export class DynamoDBService {
  private client: DynamoDBDocumentClient;
  constructor() {
    const client = new DynamoDBClient({
      region: config.get(<<your_region>>),
    });
    const marshallOptions = {
      // Whether to automatically convert empty strings, blobs, and sets to `null`.
      convertEmptyValues: false, // false, by default.
      // Whether to remove undefined values while marshalling.
      removeUndefinedValues: false, // false, by default.
      // Whether to convert typeof object to map attribute.
      convertClassInstanceToMap: true, // false, by default.
    };
    this.client = DynamoDBDocumentClient.from(client, { marshallOptions });
  }

  /**
   * Takes a javascript object and transforms it into update expressions on the dynamodb object.
   *
   * It translates all of the actions to SET which will overwrite any attribute that is already there.
   * It works best with simple types but can also serialise arrays and objects.
   *
   * https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-dynamodb-utilities.html
   *
   * @param tableName Name of the table to update on
   * @param primaryKeyName The primary key name to update on
   * @param id The primary key value
   * @param item The item to update
   */
  async upsertProperties(tableName: string, primaryKeyName: string, id: string, item: { [key: string]: any }) {
    const updatedItem = this.removePrimaryKey(primaryKeyName, item);
    const { updateExpression, expressionAttribute, expressionAttributeNames } =
      this.createUpdateExpressions(updatedItem);
    const input: UpdateCommandInput = {
      Key: {
        [primaryKeyName]: id,
      },
      UpdateExpression: `SET ${updateExpression.join(', ')}`,
      ExpressionAttributeValues: expressionAttribute,
      ExpressionAttributeNames: expressionAttributeNames,
      TableName: tableName,
    };

    const command = new UpdateCommand(input);
    return await this.client.send(command);
  }

  /**
   * We alias properties to be sure we can insert reserved names (status fx).
   *
   * It is a bit complicated:
   * updateExpression: The actual update state, example: SET #alias = :placeholder
   * expressionAttribute: The value to insert in placeholder example: :placeholder = value
   * expressionAttributeNames: Are the aliases properties to avoid clashe: #alias = key
   *
   * So in the end it ties together and both the inserted value and alias are fixed.
   */
  private createUpdateExpressions(item: { [key: string]: any }) {
    const updateExpression: string[] = [];
    const expressionAttribute: { [key: string]: any } = {};
    const expressionAttributeNames: { [key: string]: any } = {};
    Object.keys(item).map((key) => {
      const placeholder = `:p${key}`;
      const alias = `#a${key}`;
      updateExpression.push(`${alias} = ${placeholder}`);
      expressionAttribute[placeholder] = item[key];
      expressionAttributeNames[alias] = key;
    });
    return { updateExpression, expressionAttribute, expressionAttributeNames };
  }

  /**
   * Remove primary key from the object or else we cannot make an
   * update statement because the primary key would be overwritten
   * which violates the insert.
   *
   * Copy to new object to ensure we don't manipulate the reference.
   */
  private removePrimaryKey(primaryKeyName: string, item: { [key: string]: any }) {
    const itemWithoutPrimaryKey = { ...item };
    delete itemWithoutPrimaryKey[primaryKeyName];
    return itemWithoutPrimaryKey;
  }
}


推荐阅读