DynamoDB: UpdateItem, Ignore Null values in ExpressionAttributeValues

独自空忆成欢 提交于 2020-01-12 10:33:54

问题


I'm using DynamoDB UpdateItem to update records in my DB. A basic function like this is working for me.

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

But...

If I'd like to only update one attribute, the name, like this:

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

It gives me the error that

ExpressionAttributeValues cannot be NULL.

I know that I could dynamically produce the UpdateExpression String by checking for values in user, like this:

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

but is there a way that I can tell updateItem to ignore the null values and only update the name?


回答1:


I was asking the same question...In Java there's the SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES but I couldn't find anything like that in aws-sdk for nodejs.

You could use AttributeUpdates instead of UpdateExpression to make a cleaner workaround:

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!'})});
}



回答2:


This is a much simpler answer.

It works when you consider ExpressionAttributeValues an object.

Here's the code:

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

so first we build up the expression if values are available to be passed, using a simple concatenation technique.

Then we supply the values:

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

The difficulty is with ExpressionAttributeValues which here, we treat as an object and we can add to the object if we first define it as an object, hence the {}.

Then if the object does not already have the property name it adds it and then adds the value.

The net result is you can have very wide flat records as your records can be extended with variable field names. I.e. this application lists a URL and descriptor. With variable field names, I could add more URLs and descriptors to the same record. There is ultimately a memory limit, yet this type of application, for a few variable fields, would be sufficient for my application.




回答3:


I managed to get the desired result by passing an additional option to my documentClient constructor.

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

This converts empty strings to true (aka NULL in dynamodb world) and fields absent in my http request json object do not affect the database or cause the request to fail.



来源:https://stackoverflow.com/questions/45842363/dynamodb-updateitem-ignore-null-values-in-expressionattributevalues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!