3

I want increment value if exist else add element.

+-----------------------+
| id | iteration | data |
+-----------------------+
| 10 |         1 | foo1 |
| 11 |         1 | foo2 |
| 12 |         2 | foo3 |
+-----------------------+

my code:

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({'region': 'eu-west-1'}); 
exports.handler = function(event, context, callback) {

var params = {
  Item: {
    id: uuid,
    iteration: 1,
    data: body.data
  },
  TableName: "my-table"
};

documentClient.put(params, function(err, data) {
    if (err) {
      console.log("Error", err);
      const errResponse = {
        statusCode: 500,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify({ Error: 500, device : "DynamoDB", detail : err })
      };
      callback(null, errResponse);
    } else {
      console.log("Success", params.Items);
      const response = {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify("thanks")
      };
      callback(null, response);
    }
  });

}

My insert is OK.

I try with:

var params = {
    TableName: "my-table",
    Key:{
        "id": uuid
    },
    UpdateExpression: "set iteration = iteration + :val",
    ExpressionAttributeValues:{
        ":val": 1
    },
    ReturnValues:"UPDATED_NEW"
  };
  documentClient.update(params, function(err, data) {
    if (err) {
        console.error("Unable to update item. Error JSON:", JSON.stringify(err, null, 2));
        const errResponse = {
          statusCode: 500,
          headers: {
            "Access-Control-Allow-Origin": "*"
          },
          body: JSON.stringify({ Error: 500, device : "DynamoDB", detail : err })
        };
        callback(null, errResponse);
    } else {
        console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));
        const response = {
          statusCode: 200,
          headers: {
            "Access-Control-Allow-Origin": "*"
          },
          body: JSON.stringify("thanks")
        };
        callback(null, response);
    }
  });

Update is OK (increment -> 2)

But I want increment value ONLY if exist else ONLY add element. Both methods are asynchronous, how should I do?

Stéphane GRILLON
  • 11,140
  • 10
  • 85
  • 154
  • Can you have one "upsert" function which inside a transaction will check if the record exists and if not create it otherwise update the iteration? I'm not familiar with DynamoDB but basically you need to check for the record/create/update in a transaction. – maschaub Sep 25 '19 at 21:27
  • @maschaub, I understand what to do, I ask how to do? – Stéphane GRILLON Sep 26 '19 at 08:59

1 Answers1

7

Your 2nd answer is close. In your UpdateExpression you need both and ADD and a SET on the same line. like the following:

    UpdateExpression: "ADD iteration :iteration SET itemdata = :itemdata",

This is formally documented in the AWS DOCS for UpdateItem under DynamoDB scrolling down it says

ADD - Adds the specified value to the item, if the attribute does not already exist. If the attribute does exist, then the behavior of ADD depends on the data type of the attribute: If the existing attribute is a number, and if Value is also a number, then Value is mathematically added to the existing attribute. If Value is a negative number, then it is subtracted from the existing attribute.

I was able to find a AWS forum post that had a successful upsert, as well as another SO answer with the ADD and SET syntax in the same line. Below is some code that will run if placed in a new lambda (change your region to match where your table is)

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({'region': 'eu-central-1'});

exports.handler = function(item, context, callback) {

    var params = {
        TableName: "my-table",
        Key:{
            "id": item.id
        },
        UpdateExpression: "ADD iteration :iteration SET itemdata = :itemdata",
        ExpressionAttributeValues:{
            ':iteration': 1,
            ':itemdata' : item.data
        },
        ReturnValues:"NONE"
};

documentClient.update(params, function(err, data) {
    if (err) {
      console.log("Error", err);
      const errResponse = {
        statusCode: 500,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify({ Error: 500, device : "DynamoDB", detail : err })
      };
      callback(null, errResponse);
    } else {
      console.log("Success", params.Items);
      const response = {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Origin": "*"
        },
        body: JSON.stringify("upsert complete.")
      };
      callback(null, response);
    }
  });
};

note: data is a reserved keyword, and I got the following error:

Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: data

consider changing it to something else. I used itemdata.

Running it once will insert a new record. The following images are before and after images of the table after I run the lambda the second time.
enter image description here enter image description here

Taterhead
  • 5,763
  • 4
  • 31
  • 40