Update array item in dynamoDB - node.js

In my dynamoDB, I have a table that structure as like as given below:
var params = {
"TableName" : "pdfs",
"Item" : {
pdfid: // Partition key
html: [] // array attribute
}
};
I can insert new array data, just like as given code:
Insert array data
var params = {
"TableName" : "pdfs",
"Item" : {
pdfid: pdfid,
html: [event.html] // here "html" is an array, I just insert first array data
}
};
dc.put(params, function(err, data) {
............................
});
How can I update array in dynamoDB ?
var params = {
TableName: "pdfs",
Key: {
pdfid: event.pdfid
},
UpdateExpression: "SET html = :html",
ExpressionAttributeValues: {
":html": ??????
},
ReturnValues: "ALL_NEW"
};

Use the following UpdateExpression to append values to a list: SET html = list_append(html, :html). The ExpressionAttributeValues would just be a mapping from :html to the string that you want to add.

Use the String Set attribute to update the list.
ExpressionAttributeValues: {
":html": {
SS: aws.StringSlice(your_html_array)
}

Related

Is there an easier way to convert javascript object to dynamodb update expression?

I am using nodejs aws-sdk/clients/dynamodb library with dynamodb. I need to update an item in the table. Below is the sample code to update an item:
params = {
TableName:table,
Key:{
"year": year,
"title": title
},
UpdateExpression: "set info.rating = info.rating + :val",
ExpressionAttributeValues:{
":val": 1
},
ReturnValues:"UPDATED_NEW"
};
I will have to specify each attribute in info in UpdateExpression. My info object is very big and I am looking for an easier way to do that. Is there a build-in method to support update an object to dynamodb item? something like:
params = {
TableName:table,
Key:{
"year": year,
"title": title
},
Item: info
};
The answer given by E.J. Brennan is great for cases where it's ok to replace the entire item. DocumentClient eases the hassle of dealing with DynamoDB attribute types, but the example given uses the put method. According to the docs put passes through to putItem which
Creates a new item, or replaces an old item with a new item
That means that it's not going to help with partial updates to existing items where you don't already have the full record (and can get away with a full replacement). For partial updates you have to use updateItem, or it's DocumentClient counterpart, update.
The AWS labs has published a utility to help with constructing update expressions to use with updateItem. Since I generally prefer to use DocumentClient, I unmarshall values with the utility function provided by DynamoDB's Converter (yes, I know it's a bit a back and forth, but it makes testing easier).
const AWS = require('aws-sdk');
const db = new AWS.DynamoDB.DocumentClient();
const { UpdateExpression, ExpressionAttributes } = require('#aws/dynamodb-expressions');
const { unmarshall } = AWS.DynamoDB.Converter;
const updateExpressionProps = ({ category, classification }) => {
attributes = new ExpressionAttributes();
expression = new UpdateExpression();
expression.set('category', category);
expression.set('classification', classification);
return {
UpdateExpression: expression.serialize(attributes),
ExpressionAttributeNames: attributes.names,
ExpressionAttributeValues: unmarshall(attributes.values),
};
};
const updateRequest = async ({ id, subject, category, classification }) =>
await db
.update({
TableName: 'table-name',
Key: {
id,
subject,
},
...updateExpressionProps({ category, classification }),
})
.promise();
This bit of code only updates the category and classification attributes on the record identified with id and subject without the hassle of manually building a correct UpdateExpression string. This example could easily be generalized into something reusable throughout your project.
I wrote the following utility method to convert a given object to UpdateExpression (SET only), ExpressionAttributeNames, and ExpressionAttributeValues.
const convertToCompositePathObj = (obj: Record<any, any>) => {
const res: Record<string, string | number | []> = {};
const getPropertyPath = (obj: Record<any, any>, current = "") => {
for (let key in obj) {
const value = obj[key];
const newKey = current ? [current, key].join(".") : key;
if (value && typeof value === "object" && !Array.isArray(value)) {
getPropertyPath(value, newKey);
} else res[newKey] = value;
}
};
getPropertyPath(sampleObject);
return res;
};
const generateDynamoDbUpdateExpression = (obj: Record<any, any>) => {
const compositePathObj = convertToCompositePathObj(sampleObject);
let counter = 0;
let updateExpression = "SET ";
const expressionAttNamesMap: any = {};
const expressionAttValuesMap: any = {};
for (let k in compositePathObj) {
const newUpdateExpression = k
.split(".")
.map((item) => {
const attName = `#${item}`;
if (!expressionAttNamesMap[attName]) {
expressionAttNamesMap[attName] = item;
}
return attName;
})
.join(".")
.concat(`= :${counter} AND `);
expressionAttValuesMap[`:${counter}`] = compositePathObj[k];
counter += 1;
updateExpression += newUpdateExpression;
}
updateExpression = updateExpression.substring(0, updateExpression.length - 5);
return {
UpdateExpression: updateExpression,
ExpressionAttributeNames: expressionAttNamesMap,
ExpressionAttributeValues: expressionAttValuesMap
};
};
// example usage:
const sampleObject = {
name: {
first: "John",
last: "Doe"
},
address: {
line1: "123 test st.",
line2: "Apt 123",
city: "Los Angeless",
state: "CA",
zip: 92763
},
phone: 8675768475
};
console.log(generateDynamoDbUpdateExpression(sampleObject));
/**
*****OUTPUT*****
{
UpdateExpression: "SET #name.#first= :0 AND #name.#last= :1 AND #address.#line1= :2 AND #address.#line2= :3 AND #address.#city= :4 AND #address.#state= :5 AND #address.#zip= :6 AND #phone= :7",
ExpressionAttributeNames:{
#name: "name"
#first: "first"
#last: "last"
#address: "address"
#line1: "line1"
#line2: "line2"
#city: "city"
#state: "state"
#zip: "zip"
#phone: "phone"
},
ExpressionAttributeValues:{
:0: "John"
:1: "Doe"
:2: "123 test st."
:3: "Apt 123"
:4: "Los Angeless"
:5: "CA"
:6: 92763
:7: 8675768475
}
}
**/
PS: I wrote this in a hurry so please excuse the formatting and any types.
https://codesandbox.io/s/aws-dynamic-update-expression-set-33tye7?file=/src/index.ts
You could use the Document Client:
Version 2.2.0 of the AWS SDK for JavaScript introduces support for the
document client abstraction in the AWS.DynamoDB namespace. The
document client abstraction makes it easier to read and write data to
Amazon DynamoDB with the AWS SDK for JavaScript. Now you can use
native JavaScript objects without annotating them as AttributeValue
types.
For example:
var docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-2'});
var params = {
Item: {
hashkey: 'key',
boolAttr: true,
listAttr: [1, 'baz', true]
mapAttr: {
foo: 'bar'
}
},
TableName: 'table'
};
docClient.put(params, function(err, data){
if (err) console.log(err);
else console.log(data);
});
https://aws.amazon.com/blogs/developer/announcing-the-amazon-dynamodb-document-client-in-the-aws-sdk-for-javascript/

AWS DynamoDB Transact Write using IN operator on Array

I am working on one query where I need to use TrasactWrite of dynamoDB and perform update query on one of my table.
Scenario is I am having array of unique IDs and I need to perform same update operation on each ID in array and change one flag in table without using Loop or map.
But I am having difficulty in finding example or docs related to how do I use IN operator on IDs which are primary keys in a Key parameter of transactWrite.
Here is my sample code:
let IDs = await find('table', { type: 'some_type' }, 'type-index}
let params = {
TransactItems: [{
Update: {
TableName: 'table',
Key: '#_id IN :IDs,
UpdateExpression: 'set #flag = :flag',
ExpressionAttributeNames: {
'#flag': 'flag',
'#_id': '_id'
},
ExpressionAttributeValues: {
':flag': false,
":IDs": IDs
}
}
}]
}
Already tried: Answer Its not same question as mine
I will answer if anyone has any question. Any help will be really helpful. Thank you
You can't use IN operator in this case - Update. With update operator you have to put key value to Update object. A key includes partitionKey and sortKey, I think in your case, you just set partitionKey.
To update more than one item by ID, you can put many Update object to TransactItems.
let IDs = await find('table', { type: 'some_type' }, 'type-index');
let params = {
TransactItems: [], // init empty array
}
IDs.forEach((ID) => {// loop though IDs array
params.TransactItems.push({
Update: {
TableName: 'table',
Key: {
'_id': ID, // ID value
},
UpdateExpression: 'set #flag = :flag',
ExpressionAttributeNames: {
'#flag': 'flag',
// '#_id': '_id' // remove this line
},
ExpressionAttributeValues: {
':flag': false,
// ":IDs": IDs // remove this line
}
}
})
});
// await client.transactWrite(params).promise()

dynamodb get all items by an array of ids

I have a table with an attribute with name id and of type HASH. I want to get all items from a array of id's.
{
TableName: `MyTable`,
FilterExpression: 'id IN (:id)',
ExpressionAttributeValues: { ':id': ids },
};
What should I do to get all items by my ids?
You can also use DocumentClient and batchGet.
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();
let queryParams = {RequestItems: {}};
queryParams.RequestItems['tableName'] = {
Keys: [{'id': 'Value1'}, {'id': 'value2'}],
ProjectionExpression: 'id' //define other fileds that you have Ex: 'id,name'
};
documentClient.batchGet(queryParams, function (err, data) {
if (err) {
console.log('failure:getItemByBatch data from Dynamo error', err);
} else {
console.log('success:getItemByBatch data from Dynamo data');
console.log(data)
}
});
Please use BatchGetItem API to get multiple values from DynamoDB table.
BatchGetItem
Example:-
var dynamodb = new AWS.DynamoDB({maxRetries: 5, retryDelayOptions: {base: 300} });
var table = "Movies";
var year_val = 2015;
var title = "The Big New Movie";
var params = {
"RequestItems" : {
"Movies" : {
"Keys" : [ {
"yearkey" : {N : "2016"},
"title" : {S : "The Big New Movie 1"}
} ]
}
},
"ReturnConsumedCapacity" : "TOTAL"
};
dynamodb.batchGetItem(params, function(err, data) {
if (err) {
console.error("Unable to get item. Error JSON:", JSON.stringify(err,
null, 2));
} else {
console.log("Movie data:", JSON.stringify(data, null, 2));
}
});
Its in C#, below code is to get all items by an array of ids from a dynamodb table having different guid's using BatchGet or CreateBatchGet
string tablename = "AnyTableName"; //table whose data you want to fetch
var BatchRead = ABCContext.Context.CreateBatchGet<ABCTable>(
new DynamoDBOperationConfig
{
OverrideTableName = tablename;
});
foreach(string Id in IdList) // in case you are taking string from input
{
Guid objGuid = Guid.Parse(Id); //parsing string to guid
BatchRead.AddKey(objGuid);
}
await BatchRead.ExecuteAsync();
var result = BatchRead.Results;
// ABCTable is the table modal which is used to create in dynamodb & data you want to fetch

How to overwrite an item with a different schema but same key in DynamoDB using nodejs on lambda

I am trying to overwrite an item in DynamoDB (that uses a primary key called username) by using put as shown below:
console.log('writing commands',existingCommands,message.username);
var t2 = performance();
var writeParams = {
Item: {
username: message.username,
commands: existingCommands // Sorry for the confusing name, due to deepExtend existingCommands are the new commands
},
TableName: TableName
};
docClient.put(writeParams, function(err, data){
if(err){
console.error('error',err);
} else {
console.log('write result',data);
var t3 = performance();
console.info('delete & write performance',(t3-t2).toFixed(3));
}
// End function
context.done();
});
That works for:
Inserting a new item where the username doesn't exist.
Updating an item that matches the schema of the Item i'm trying to insert, for example, I'm trying to insert that item:
{
"username":"ausin441062133",
"commands": {
"command1":"command",
"command2":"command"
}
}
and if there's an item that matches the schema and the username it'll get overwritten, i.e.
{
"username":"ausin441062133",
"commands": {
"command1":"I will be overwritten",
"command2":"I will be overwritten"
}
}
But when there's an item with the exact username but different schema, it doesn't work, i.e.
{
"username":"ausin441062133",
"commands": {
"command1":"I will NOT be overwritten"
}
}
What command do I need to use to overwrite an existing item if it matches the username?
Eventually as Dmitry suggested update works but it needs some different params as opposed to put here's my code:
// Step 3 write command back
console.log('writing commands',existingCommands,message.username);
var t2 = performance();
var updateParams = {
Key: {
username: message.username
},
UpdateExpression: "set commands = :c",
ExpressionAttributeValues: {
":c":existingCommands
},
ReturnValues: "UPDATED_NEW",
TableName: TableName
};
docClient.update(updateParams, function(err, data){
if(err){
console.error('error',err);
} else {
console.log('write result',data);
var t3 = performance();
console.info('delete & write performance',(t3-t2).toFixed(3));
}
// End function
context.done();
});

How to query a table by using IN conditions in DynamoDB

Struggling to find an example of how to query a table to return rows with ids from a given list.
The query below throws on the inclusion of IN
var params = {
id: '7deb3df9-552b-47a4-aef3-ad601f141d50'
};
var p = {
TableName: 'players',
KeyConditionExpression: 'id IN (:id)',
ExpressionAttributeValues: buildQuery(params)
};
You can't use "IN" operator with KeyConditionExpression, please see details in this SO question
You may want to use batchGetItem instead of query, which is not so efficient though.
Here is how your params could look like:
var params = {
RequestItems: {
'players': {
Keys: [{
id: "7deb3df9-552b-47a4-aef3-ad601f141d50",
rangeKey: "<range key 1>" // <--- if your table has a range key, you must specify its value here
}, {
id: "<ANOTHER ID 2>",
rangeKey: "<range key 2>"
}, {
id: "<ANOTHER ID 3>",
rangeKey: "<range key 3>"
}]
}
}
};
dynamodbDoc.batchGet(params, function(err, data) {
});

Resources