【问题标题】:How to dynamically update an attribute in a dynamodb item?如何动态更新 dynamodb 项目中的属性?
【发布时间】:2019-04-24 08:25:31
【问题描述】:

我使用 Node js 在 dynamodb 中创建了一个项目,该项目具有多个属性,例如品牌、类别、折扣、有效性等。我使用 uuid 为每个项目生成 id。现在假设我要更新项目的有效性属性,在这种情况下,我当前正在发送整个 json 对象,并将有效性值修改为新值。

这绝对不是最优的,请帮我找到一个最优的解决方案。

const params = {
    TableName: process.env.PRODUCT_TABLE,
    Key: {
      id: event.pathParameters.id,
    },
    ExpressionAttributeNames: {
      '#discount': 'discount',
    },
    ExpressionAttributeValues: {
      ':brand': data.brand,
      ':category': data.category,
      ':discount': data.discount,
      ':denominations': data.denominations,
      ":validity": data.validity,
      ":redemption": data.redemption    
    },
    UpdateExpression: 'SET #discount = :discount, denominations = :denominations, brand = :brand, category = :category, validity = :validity, redemption = :redemption',
    ReturnValues: 'ALL_NEW',
  };

我只想发送我想用新值更新的属性,如果我想将有效期从 6 个月更改为 8 个月,我应该发送如下内容: { “有效期”:“8个月” } 它应该更新项目的有效性属性。 同样应该适用于该项目的任何其他属性。

'use strict';

const AWS = require('aws-sdk');

const dynamoDb = new AWS.DynamoDB.DocumentClient();

module.exports.update = (event, context, callback) => {
  const data = JSON.parse(event.body);

  let attr = {};
  let nameobj = {};
  let exp = 'SET #';
  let arr = Object.keys(data);
  let attrname = {};

  arr.map((key) => {attr[`:${key}`]=data[key]});

  arr.map((key) => {
    exp += `${key} = :${key}, `
  });

  arr.map((key) => {nameobj[`#${key}`]=data[key]});

  attrname = {
    [Object.keys(nameobj)[0]] : nameobj[Object.keys(nameobj)[0]]
  }

  const params = {
    TableName: process.env.PRODUCT_TABLE,
    Key: {
      id: event.pathParameters.id,
    },
    ExpressionAttributeNames: attrname,
    ExpressionAttributeValues: attr,
    UpdateExpression: exp,
    ReturnValues: 'ALL_NEW',
  };

  // update the todo in the database
  dynamoDb.update(params, (error, result) => {
    // handle potential errors
    if (error) {
      console.error(error);
      callback(null, {
        statusCode: error.statusCode || 501,
        headers: { 'Content-Type': 'text/plain' },
        body: 'Couldn\'t update the card',
      });
      return;
    }

    // create a response
    const response = {
      statusCode: 200,
      body: JSON.stringify(result.Attributes),
    };
    callback(null, response);
  });
};

【问题讨论】:

    标签: amazon-dynamodb crud


    【解决方案1】:

    与其他cmets相反,这是很有可能的,使用UpdateItem动作。

    Language agnostic API docs

    JavaScript specific API docs

    如果您想动态创建查询,请尝试以下操作:

    const generateUpdateQuery = (fields) => {
        let exp = {
            UpdateExpression: 'set',
            ExpressionAttributeNames: {},
            ExpressionAttributeValues: {}
        }
        Object.entries(fields).forEach(([key, item]) => {
            exp.UpdateExpression += ` #${key} = :${key},`;
            exp.ExpressionAttributeNames[`#${key}`] = key;
            exp.ExpressionAttributeValues[`:${key}`] = item
        })
        exp.UpdateExpression = exp.UpdateExpression.slice(0, -1);
        return exp
    }
    
    let data = {
        'field' : { 'subfield': 123 },
        'other': '456'
    }
    
    let expression = generateUpdateQuery(data)
    
    let params = {
        // Key, Table, etc..
        ...expression
    }
    
    console.log(params)
    

    输出:

    { 
        UpdateExpression: 'set #field = :field, #other = :other',
        ExpressionAttributeNames: {
            '#field': 'field',
            '#other': 'other'
        },
        ExpressionAttributeValues: {
            ':field': { 
                'subfield': 123
            },
            ':other': '456'
        } 
    }
    

    【讨论】:

    • 感谢理查德!我对缺少使用 JS Dynamo SDK 的代码示例感到惊讶
    • 没问题。我可能会稍后更新该答案,因为我认为我在某个地方有更好的版本。您是对的,示例的方式并不多,但是一旦您知道要查找的内容,文档就相当不错了。我使用 Dynamo 已经有一段时间了,通过反复试验,我学到了很多重要的经验。也就是说,它现在是我的首选数据库,除非有充分的理由使用其他东西。他们确实将很多责任留给了开发人员,这很难成为第一次,但这也意味着您很快就可以掌握整个 API 并将所有逻辑保持在一种语言中。
    • 啊,太好了,我正要自己写出来,感谢您节省的时间!
    【解决方案2】:

    使用Javascript SDK V3:

    从正确的包导入:

    import { DynamoDBClient PutItemCommandInput, UpdateItemCommandInput, UpdateItemCommand } from '@aws-sdk/client-dynamodb';
    

    动态对项目进行部分更新的功能: (下面的代码是 typescript 可以很容易地转换成 Javascript,只需删除类型!)

    function updateItem(id: string, item: any) {
      const dbClient = new DynamoDBClient({region: 'your-region-here });
    
      let exp = 'set ';
      let attNames: any = { };
      let attVal: any = { };
      for(const attribute in item) {
        const valKey = `:${attribute}`;
        attNames[`#${attribute}`] = attribute;
        exp += `#${attribute} = ${valKey}, `;     
        const val = item[attribute]; 
        attVal[valKey] = { [getDynamoType(val)]: val };
      }
    
      exp = exp.substring(0, exp.length - 2);
    
      const params: UpdateItemCommandInput = {
        TableName: 'your-table-name-here',
        Key: { id: { S: id } },
        UpdateExpression: exp,
        ExpressionAttributeValues: attVal,
        ExpressionAttributeNames: attNames,
        ReturnValues: 'ALL_NEW',
      };
    
      try {
        console.debug('writing to db: ', params);
        const command = new UpdateItemCommand(params);
        const res = await dbClient.send(command);
        console.debug('db res: ', res);
        return true;
      } catch (err) {
        console.error('error writing to dynamoDB: ', err);
        return false;
      }
    }
    

    并使用它(我们也可以进行部分更新):

    updateItem('some-unique-id', { name: 'some-attributes' });
    

    【讨论】:

      【解决方案3】:

      我所做的是创建一个helper class

      • 这是一个简单的函数:添加所有进入的属性和值,如果值为 null 或未定义,则不会出现在表达式中。

      • 我建议用typescript 创建一个helper class,并添加更多功能和其他东西,例如expressionAttributeValuesexpressionAttributeNames ...的生成器,希望对您有所帮助。

      function updateExpression(attributes, values) {
        const expression = attributes.reduce((res, attribute, index) => {
      
          if (values[index]) {
            res += ` #${attribute}=:${attribute},`;
          }
      
          return res;
        }, "SET ");
      
        return expression.slice(0, expression.length - 1)
      }
      
      console.log(
        updateExpression(["id", "age", "power"], ["e8a8da9a-fab0-55ba-bae3-6392e1ebf624", 28, undefined])
      );

      【讨论】:

        【解决方案4】:

        您可以使用代码并根据您提供的对象生成 params 对象。它只是一个 JavaScript 对象,您遍历这些项目,以便更新表达式仅包含您提供的字段。 这不是一个真正的 DynamoDB 问题,因为这更像是一个通用的 JS 编码问题。

        【讨论】:

        • 从 JS 方面显然可以做到这一点,但我在想如果 dynamodb 提供某种更新表达式来做同样的事情。
        • 好的,那么简短的回答是否定的。 :) 但公平地说,在 SQL 中也是如此,只提供您要使用的参数。一般来说,虽然 DynamoDB 中的舒适功能要少得多,但我已经使用 SQL Server 和 Oracle 多年,尤其是后者有很多不错的额外功能。
        • 我尝试这样做,我在上面发布了我的代码。你能检查一下并告诉我我在这里做错了什么吗?
        • 我猜问题是缺少#。您需要在所有字段名称前添加#。 exp最后应该是这个样子#k1 = :v1, #k2 = :v2
        • 顶部的代码 sn-p 来自工作版本,因为我只在第一个变量之前使用了“#”符号。
        【解决方案5】:

        您可以使用 UpdateItem;要熟悉 DynamoDb 查询,我建议您使用 DynamoDb NoSQL 工作台: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html

        它可以根据您的查询为您生成 sn-ps。

        DynamoDb NoSQL workbench screenshot query

        【讨论】:

        • 我认为这与理查德的答案相同?
        • 否,因为它没有告诉您使用可以为您完成繁重工作的 DynamoDb 工作台?
        猜你喜欢
        • 1970-01-01
        • 2018-09-24
        • 2020-05-21
        • 2018-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-08
        • 2022-01-22
        相关资源
        最近更新 更多