DynamoDB:UpdateItem,忽略ExpressionAttributeValues

时间:2017-08-23 14:20:08

标签: amazon-web-services lambda amazon-dynamodb aws-lambda

我正在使用DynamoDB UpdateItem更新数据库中的记录。像这样的基本功能对我有用。

var user = {
    userID: '123213',
    name: 'John Doe',
    age: 12,
    type: 'creator'
};
var params = {
    TableName:table,
    Key:{
        "UserID": user.userID
    },
    UpdateExpression: "set Name = :r, Age=:p, Type=:a",
    ExpressionAttributeValues:{
        ":r":user.name,
        ":p":user.age,
        ":a":user.type
    },
    ReturnValues:"UPDATED_NEW"
};

docClient.update(params, function(err, data) {
    if (err) {
        console.error("Unable to update item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
    }
});

可是...

如果我只想更新一个属性,名称就像这样:

 var user = {
        userID: '123213',
        name: 'John Smith'
    };
var params = {
    TableName:table,
    Key:{
        "UserID": user.userID
    },
    UpdateExpression: "set Name = :r, Age=:p, Type=:a",
    ExpressionAttributeValues:{
        ":r":user.name,
        ":p":user.age,
        ":a":user.type
    },
    ReturnValues:"UPDATED_NEW"
};

它给了我错误

  

ExpressionAttributeValues不能为NULL

我知道我可以通过检查用户的值来动态生成UpdateExpression字符串,如下所示:

for (var key in user) {
  if (user.hasOwnProperty(key)) {
    ...add to DynamicUpdateExpression..
  }
}

但有没有办法告诉updateItem忽略空值并只更新name

4 个答案:

答案 0 :(得分:2)

我问了同样的问题......在Java中有SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES,但我在aws-sdk中找不到类似于nodejs的内容。

您可以使用 AttributeUpdates 代替 UpdateExpression 来制作更清晰的解决方法:

const AWS      = require(aws-sdk);
const bluebird = require('bluebird');
const _        = require('lodash');

AWS.config.setPromisesDependency(bluebird);

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

var skipNullAttributes = (attributes) => {
  return _.omitBy(attributes, (attr) => { 
    return _.isNil(attr.Value); 
  }); 
}

var update = (id, attributes) => {
  var params = {
    TableName       : 'MyTableName',
    Key             : { id: id },
    AttributeUpdates: skipNullAttributes(attributes)
  };

  return dynamodb.update(params).promise();
}

exports.handler = (event, context, callback) => {
  var body   = JSON.parse(event.body);
  var userId = event.pathParameters.id;

  var attributes = {
    firstName: { Action: 'PUT', Value: body.firstName },
    lastName : { Action: 'PUT', Value: body.lastName  }
  };

  update(userId, attributes)
    .then((result) => console.log(result) )
    .catch((error) => console.error(error) );

  callback(null, {statusCode: 200, body: JSON.stringify({message: 'done!'})});
}

答案 1 :(得分:1)

这是一个简单得多的答案。

当您将ExpressionAttributeValues视为一个对象时,它将起作用。

代码如下:

params.TableName = ddbTable;
params.UpdateExpression =  "set LastPostedDateTime = :l" ;
if (req.body.AttachmentDescription)  { params.UpdateExpression  += ", AttachmentDescription = :d"; }
if (req.body.AttachmentURL)          { params.UpdateExpression  += ", AttachmentURL = :a"; }

因此,首先,我们使用一种简单的串联技术来构建是否可以传递值的表达式。

然后我们提供值:

params.ExpressionAttributeValues = {};
params.ExpressionAttributeValues[':l'] =  formattedDate ;
if (req.body.AttachmentDescription)  { params.ExpressionAttributeValues[':d']= req.body.AttachmentDescription ; }
if (req.body.AttachmentURL)          { params.ExpressionAttributeValues[':a']= req.body.AttachmentURL ; }

难点在于ExpressionAttributeValues,在这里,我们将其视为对象,并且如果我们首先将其定义为对象,则可以将其添加到对象,因此将{}。

然后,如果对象尚不具有属性名称,则将其添加,然后添加值。

最终结果是,您的记录可以很宽,因为可以使用可变字段名称扩展记录。即该应用程序列出了URL和描述符。使用可变字段名称,我可以在同一记录中添加更多URL和描述符。最终会有一个内存限制,但是对于一些可变字段而言,这种类型的应用程序对于我的应用程序而言已足够。

答案 2 :(得分:0)

通过向我的documentClient构造函数传递附加选项,我设法获得了预期的结果。

const documentClient = new AWS.DynamoDB.DocumentClient({convertEmptyValues: true});

这会将空字符串转换为true(在dynamodb世界中也称为NULL),并且我的http请求json对象中不存在的字段不会影响数据库或导致请求失败。

答案 3 :(得分:0)

我发现了另一种受@David White的回答启发的方法。此外,OP非常接近动态编写UpdateExpression和ExpressionAttributeValues的方法。这是关于如何制作动态更新表达式的代码,无论您具有什么属性。

const create = (req, context, callback) => {
  const data = JSON.parse(req.body)
  const params = {
    TableName: process.env.DYNAMODB_TABLE,
    Key: { title: data.title },
  };
  params.UpdateExpression = "SET latestUpdate = :updateTime"
  params.ExpressionAttributeValues = {}
  for (let k in data) {
    if (k !== 'title') {
        // you don't want to update whatever you've set as your primary key for the table, so you have to ignore this
      params.UpdateExpression += `, ${k} = :${k}`
      params.ExpressionAttributeValues[`:${k}`] = data[k]
    }
  }
  params.ExpressionAttributeValues[':updateTime'] = Date.now()
  
  dynamodb.update(params)