How to write the parameter to update a particular property's value in an object from an array?

1.1k Views Asked by At

How to update quantity value based on title in the movies array and Item id (123)

I only manage to update value at the first layer like name (David), but don't know how to update the second layer with additional filter for the array (movies).

From:

Item:
{
   id: 123,
   name: 'David',
   movies: [
      {
         id: 1,
         title: 'The lord of the ring',
         quantity: 1
      },
      {
         id: 2,
         title: 'Star Wars',
         quantity: 1
      }
   ]
}

To:

Item:
{
   id: 123,
   Name: 'David',
   movies: [
      {
         id: 1,
         title: 'The lord of the ring',
         quantity: 2
      },
      {
         id: 2,
         title: 'Star Wars',
         quantity: 1
      }
   ]
}

By the way, I'm using aws DynamoDB document client in node.js, it will be nice if you can share me how you do it in your update parameter.

2

There are 2 best solutions below

0
On

There is no way to update an object inside of a list without replacing it.

You probably want to restructure your table to emulate a relational data model. AWS has some documentation on this.

As an example, create your table like this:

aws dynamodb create-table \
  --table-name movie-table \
  --attribute-definitions AttributeName=rId,AttributeType=N AttributeName=rKey,AttributeType=S \
  --key-schema AttributeName=rId,KeyType=HASH AttributeName=rKey,KeyType=RANGE

The table will have generically named hash and range keys. This script demonstrates how to structure the data and add to the "count":

const { DynamoDB } = require('aws-sdk');

const client = new DynamoDB.DocumentClient({ region: 'us-east-1' });

const addItem = (rId, rKey, attributes) => {
  const item = { rId, rKey };
  Object.assign(item, attributes);
  return client.put({ TableName: 'movie-table', Item: item }).promise();
};

// NOTE: this is where the count attribute gets iterated
const addToCount = (rId, rKey) => client.update({
  TableName: 'movie-table',
  Key: { rId, rKey },
  UpdateExpression: 'ADD #count :n',
  ExpressionAttributeNames: { '#count': 'count' },
  ExpressionAttributeValues: { ':n': 1 },
}).promise();

const run = async () => {
  await addItem(123, 'USER|123', { name: 'David' });
  await addItem(1, 'MOVIE|1', { title: 'The lord of the ring' });
  await addItem(2, 'MOVIE|2', { title: 'Star Wars' });
  await addItem(123, 'COUNT|1', { count: 1 });
  await addItem(123, 'COUNT|2', { count: 1 });
  await addToCount(123, 'COUNT|1');
};

run();

This is what the table looks like after the script runs:

enter image description here

0
On

I know this is a bit old but there is a way. Using the document client SDK, you can reference object properties and array elements in the UpdateExpression. However, you can't run any logic so you have to know/assume/expect that the element indexes are enough.

For example, you can do something like this:

    let params = {
         TableName: 'your-table-name',
         Key: { id: 123 },
         UpdateExpression: 'set movies[0].quantity = :x',
         ExpressionAttributeValues: { ':x': 5 }
     };

     const client = AWS.DynamoDB.DocumentClient();

     client.update(params);

NOTE: You cannot make the index an Expression Attribute Value. You would have to dynamically build that update expression based on the index you know has to be updated. It's not a perfect solution but it could get the job done.

For reference, I derived this from the base (non-DocumentClient) example from here: Adding Nested Map Attributes