Deb
Deb

Reputation: 389

How to dynamically update an attribute in a dynamodb item?

I created an item in dynamodb using Node js, the item has multiple attributes such as brand, category, discount, validity, etc. I am using uuid to generate ids for each item. Now let's say I want to update the validity attribute of the item, in which case I am currently sending the entire json object with the value of validity modified to the new value.

This is definitely not optimal, please help me find an optimal solution.

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

I want to send just the attribute I want to update with the new value, if I want to change the validity from 6 months to 8 months, I should just send something like: { "validity": "8 months" } And it should update the validity attribute of the item. Same should apply to any other attribute of the item.

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

Upvotes: 20

Views: 28553

Answers (4)

Richard Dunn
Richard Dunn

Reputation: 6780

Contrary to others comments, this is very possible, use the UpdateItem action.

Language agnostic API docs

JavaScript specific API docs

If you want to dynamically create the query, try something like this:

function GenerateUpdateExpression(object) {
    let exp = {
        UpdateExpression: 'set',
        ExpressionAttributeNames: {},
        ExpressionAttributeValues: {}
    };
    for (const [key, value] of Object.entries(object)) {
        exp.UpdateExpression += ` #${key} = :${key},`;
        exp.ExpressionAttributeNames[`#${key}`] = key;
        exp.ExpressionAttributeValues[`:${key}`] = value;
    };
    // remove trailing comma
    exp.UpdateExpression = exp.UpdateExpression.slice(0, -1);
    return exp
}

let data = {
    'field' : { 'subfield': 123 },
    'other': '456'
}

let expression = GenerateUpdateExpression(data)

let params = {
    // Key, Table, etc..
    ...expression
}

console.log(params)

Output:

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

Upvotes: 39

A-Sharabiani
A-Sharabiani

Reputation: 19377

Using Javascript SDK V3:

Import from the right package:

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

Function to dynamically do partial updates to the item: (the code below is typescript can be easily converted to Javascript, just remove the types!)

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

And to use it (we can do partial updates as well):

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

Upvotes: 0

michael
michael

Reputation: 1

You can use UpdateItem; to familiarize yourself with DynamoDb queries I would suggest you DynamoDb NoSQL workbench: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html

It can generate snippets for you based on your queries.

DynamoDb NoSQL workbench screenshot query

Upvotes: -2

SDM
SDM

Reputation: 562

You can use code and generate the params object based on the object you provide. It's just a JavaScript object, you walk through the items so that the update expression only contains the fields you have provided. This is not really a DynamoDB question in that this is more a general JS coding question.

Upvotes: -1

Related Questions