Removing the "reserved keyword" from dynamodb update item node js - amazon-web-services

I am able to SET values for reserved keywords but not able to remove certain keywords in dynamodb.
My expression is below. Not able to remove data if I give like this. But able to remove size though.
Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword:data
{
ConditionExpression: "#id = :id",
ExpressionAttributeNames: {
"#id": "id",
“#name": “name",
},
ExpressionAttributeValues: {
":id": “1234566",
“:name": “John",
},
Key: {
id: "1234566",
},
ReturnValues: "ALL_NEW",
TableName: “table_name",
UpdateExpression: "SET #name = :name REMOVE size, data",
}
Modified to include data in expression attribute names as below. Still throws error.
{
ConditionExpression: "#id = :id",
ExpressionAttributeNames: {
"#id": "id",
“#name": “name",
“#size": “size",
“#data": “data",
},
ExpressionAttributeValues: {
":id": “1234566",
“:name": “John",
“:size": undefined,
“:data": undefined,
},
Key: {
id: "1234566",
},
ReturnValues: "ALL_NEW",
TableName: "table_name",
UpdateExpression: "SET #name = :name, #size = :size, #data =
:data REMOVE size, data”,
}

Remove size and data from ExpressionAttributeValues and from SET UpdateExpression. Add #size and #data to REMOVE on UpdateExpression instead of size and data.
{
ConditionExpression: "#id = :id",
ExpressionAttributeNames: {
"#id": "id",
“#name": “name",
“#size": “size",
“#data": “data",
},
ExpressionAttributeValues: {
":id": “1234566",
“:name": “John"
},
Key: {
id: "1234566",
},
ReturnValues: "ALL_NEW",
TableName: "table_name",
UpdateExpression: "SET #name = :name, REMOVE #size, #data”,
}

Reserved words in DynamoDB
to do that dynamically you need only to provide a json object with this function
it's create dynamically
-ExpressionAttributeNames
-ExpressionAttributeValues
-UpdateExpression
also skip reserved keyword for dynamodb
def generate_update_att(body):
"""
#############input
body={
'newval':newData['newval'],
'lastname':newData['lastname'],
'name':newData['name']
}
#################output
-ExpressionAttributeNames
-ExpressionAttributeValues
-UpdateExpression
"""
UpdateExpression = ["set "]
ExpressionAttributeValues = dict()
ExpressionAttributeNames=dict()
for key, val in body.items():
UpdateExpression.append(f" #{key} = :{key},")
ExpressionAttributeValues[f":{key}"] = val
ExpressionAttributeNames[f"#{key}"] = key
return "".join(UpdateExpression)[:-1], ExpressionAttributeValues,ExpressionAttributeNames
function Call
my_data={
'newval':newData['newval'],
'lastname':newData['lastname'],
'name':newData['name']
}
a, v,z = generate_update_att(my_data)
my_table.update_item(
Key={'id':my_id},
UpdateExpression=my_UpdateExpression,
ExpressionAttributeValues=dict(my_ExpressionAttributeValues),
ExpressionAttributeNames=dict(my_ExpressionAttributeNames)
)

Related

Having problem in understanding to update data in Dynamodb. I can't understand what is happening in this updateTodo

I have a lambda function which should update the field on dynamodb using AppSync. But i am having difficulty to understand the code. As I cant understand what is the purpose of the for loop and creating the variable "attributes" and the purpose of all those params and the prefix variable. Where as id the the primary key of my dynamo table
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
type Params = {
TableName: string | undefined,
Key: string | {},
ExpressionAttributeValues: any,
ExpressionAttributeNames: any,
UpdateExpression: string,
ReturnValues: string
}
async function updateTodo(todo: any) {
let params: Params = {
TableName: process.env.TODOS_TABLE,
Key: {
id: todo.id
},
ExpressionAttributeValues: {},
ExpressionAttributeNames: {},
UpdateExpression: "",
ReturnValues: "UPDATED_NEW"
};
let prefix = "set ";
let attributes = Object.keys(todo);
for (let i = 0; i < attributes.length; i++) {
let attribute = attributes[i];
if (attribute !== "id") {
params["UpdateExpression"] += prefix + "#" + attribute + " = :" + attribute;
params["ExpressionAttributeValues"][":" + attribute] = todo[attribute];
params["ExpressionAttributeNames"]["#" + attribute] = attribute;
prefix = ", ";
}
}
try {
await docClient.update(params).promise()
return todo
} catch (err) {
console.log('DynamoDB error: ', err)
return null
}
}
export default updateTodo;
This method is building the required attributes for the updateItem method.
Here's an example of what the parameters being passed to the upateItem method should look like:
const params = {
TableName: "YOUR_TABLE_NAME",
Key: {
"id": "1"
},
UpdateExpression: "set #attribute1 = :a1, #attribute2 = :a2",
ExpressionAttributeNames: {
"#attribute1": "attribute1_name"
"#attribute1": "attribute2_name"
},
ExpressionAttributeValues: {
":a1": "attribute 1 value",
":a2": "attribute 2 value"
}
};
The updateTodo method is building this parameter hash dynamically in several steps:
This block of code is creating a map called params. It's specifying the table name by looking up the TODOS_TABLE environment variable. The Key refers to the partition key, which in this example is named id.
let params: Params = {
TableName: process.env.TODOS_TABLE,
Key: {
id: todo.id
},
ExpressionAttributeValues: {},
ExpressionAttributeNames: {},
UpdateExpression: "",
ReturnValues: "UPDATED_NEW"
};
Notice how the ExpressionAttributeValues,ExpressionAttributeNames and UpdateExpression keys all have empty values.
The next step is to set the params[ExpressionAttributeValues] and params[ExpressionAttributeNames] as required by the updateItem API.
let prefix = "set ";
let attributes = Object.keys(todo);
for (let i = 0; i < attributes.length; i++) {
let attribute = attributes[i];
if (attribute !== "id") {
params["UpdateExpression"] += prefix + "#" + attribute + " = :" + attribute;
params["ExpressionAttributeValues"][":" + attribute] = todo[attribute];
params["ExpressionAttributeNames"]["#" + attribute] = attribute;
prefix = ", ";
}
}
The end result is a params map that would look something like this:
const params = {
TableName: "YOUR_TABLE_NAME",
Key: {
"id": "1"
},
UpdateExpression: "set #attribute1 = :a1, #attribute2 = :a2",
ExpressionAttributeNames: {
"#attribute1": "attribute1_name"
"#attribute1": "attribute2_name"
},
ExpressionAttributeValues: {
":a1": "attribute 1 value",
":a2": "attribute 2 value"
}
};

AWS, dynamodb startwith condiftion gsi

I need to execute query like this:
select * from table where sampling_date like "2020-05-%"
To do this, I'm calling for
db.query({
TableName: "Tubes",
Select: "ALL_ATTRIBUTES",
IndexName: "sampling_date_idx",
KeyConditionExpression: " sampling_date > :sampling_date ",
ExpressionAttributeValues:{ ':sampling_date': {'S': '2020-05-'}}
}, function(error: AWSError, data: QueryOutput){
console.log(error, data);
})
And I get this error message:
{"errorType":"Error","errorMessage":"{\"message\":\"Query key condition not supported\",\"code\":\"ValidationException\",
My table:
this.tubes = new dynamodb.Table(this, "tubes", {
tableName: "Tubes",
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
partitionKey: {
name: "id",
type: dynamodb.AttributeType.STRING
},
pointInTimeRecovery: true,
removalPolicy: cdk.RemovalPolicy.RETAIN
});
this.tubes.addGlobalSecondaryIndex({
indexName: "sampling_date_idx",
sortKey: {
name: 'sampling_date_srt',
type: AttributeType.STRING
},
partitionKey: {
name: "sampling_date",
type: AttributeType.STRING,
},
})
I think there are two issues in your current code -
In KeyConditionExpression, there must be an equality condition on a single partition key value. In your case, it must include "sampling_date = :sampling_date".
Please read "KeyConditionExpression" section in -
https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html
In short, you only can perform equality test against partition key.
I am not sure which language you use. I suspect your syntax for ExpressionAttributeValues is not correct.
The syntax given in AWS doc is -
"ExpressionAttributeValues": {
"string" : {
"B": blob,
"BOOL": boolean,
"BS": [ blob ],
"L": [
"AttributeValue"
],
"M": {
"string" : "AttributeValue"
},
"N": "string",
"NS": [ "string" ],
"NULL": boolean,
"S": "string",
"SS": [ "string" ]
}
}
In your case, it may be something like -
"ExpressionAttributeValues": {
":sampling_date": {"S": "2020-05-01"}
}
My experience is in C#, it may be something like -
ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
{
{ ":sampling_date", new AttributeValue{S = "2005-05-01"} }
}
To solve your problem, you may need to use another attribute as the index's partition key. sampling_date only can be used as sort key.
sampling_date is the partition key for your GSI sampling_date_idx.
DynamoDB documentation says that in key condition expressions:
You must specify the partition key name and value as an equality condition.
Source: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions
So sampling_date can only be used with the "equal to" comparison operator. None of the other operators like less than, greater than, between, contains, begins with, etc. can be used with sampling_date.
However, these operators can be used with a sort key!
So if you can redesign your table and/or indexes such that sampling_date becomes a sort key of some index, you can use begins_with on it.
Here's a suggestion:
Create a GSI with partition key = sampling_year & sort key = sampling_date.
Then if your table has the following items:
{
"id": "id1",
"sampling_year": 2020,
"sampling_date": "2020-04-01"
}
{
"id": "id2",
"sampling_year": 2020,
"sampling_date": "2020-05-01"
}
{
"id": "id3",
"sampling_year": 2020,
"sampling_date": "2020-06-01"
}
And you use the following Node.js code:
let AWS = require("aws-sdk")
let dc = new AWS.DynamoDB.DocumentClient()
dc.query({
TableName: "Tubes",
IndexName: "sampling_year-sampling_date-index",
KeyConditions: {
"sampling_year": {
ComparisonOperator: "EQ",
AttributeValueList: [2020]
},
"sampling_date": {
ComparisonOperator: "BEGINS_WITH",
AttributeValueList: ["2020-05-"]
}
}
}
You'll get your desired output:
{
"id": "id2",
"sampling_year": 2020,
"sampling_date": "2020-05-01"
}
Try
KeyConditionExpression: `begins_with(sampling_date, :sampling_date)`
See available condition expressions here...
https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html

Amazon Lex and DynamodDB - can't update existing item

I'm trying to get a specific item from a table.
My DynamoDB table name is table and I have:
Name PK | Number<br/>
S: Juan | S: #####
When I try to run in Lambda I don't get any Item when it really exist one with that name... any idea why it's like that?
AWS = require("aws-sdk"),
DDB = new AWS.DynamoDB({
region: "REGION",
}),
lookup_name_str = name //From Intent variable,
params = {
TableName: "table",
KeyConditionExpression: "name = :v1",
ExpressionAttributeValues: {
":v1":{
"S": lookup_name_str
}
},
FilterExpression: 'contains(nomColaborador,:v1)',
ProjectionExpression: "Number"
};
console.log(params);
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.scan(params, function(err, data){
if(err){
throw err;
}
if(data.Items && data.Items[0] && data.Items[0].Number){
console.log("There is a Name with that number");
console.log(data.Items[0]);
my_response.statusCode = 200;
my_response.body = {
"sessionAttributes": {
"extension_str": data.Items[0].Number.S,
"nomColaborador": event.currentIntent.slots.Name
},
"dialogAction":{
"type": "Close",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": data.Items[0].Number.S
}
}
};
The main problem here is that you are doing a scan. KeyConditionExpression isn't a parameter of a scan request. If you are requesting a single item by key you want to use getItem. If you need to query data by partition key and an optional sort key you should use query.
With that all said, when you do a scan, or put a filter on a query, you really need to be sure to page through the data. You will often find that you'll get a response with no data, but a paging key to make another call.

DynamoDB conditional put

Say I want to add a record like below:
{
id:876876,
username:example,
email:xxxxxx#xxx.com,
phone:xxxxxxx,
created_at:current_date,
updated_at:current_date
}
What I want is to check with the id if record exist created_date should not be modified only updated_date should have current_date.
Can we do this with put method without having to be making a get item call?
To create a new item or update an existing item with conditional expression. Prefer to use updateItem rather than putItem.
If the Hash key (Primary key) doesn't exist both putItem and updateItem create a new record. If the record already exist, putItem will completely override the existing row but updateItem only updates the attributes passed in UpdateExpression not a whole record.
In your case, use if_not_exists() function to check whether the created_at field already exists or not. If exists created_at will not be overridden.
Update expression: "SET #email = :email, #created_at = if_not_exists(#created_at, :created_at), #updated_at = :updated_at"
Sample snippet
var params = {
ExpressionAttributeNames: {
"#email": "email",
"#created_at": "created_at",
"#updated_at": "updated_at"
},
ExpressionAttributeValues: {
":email": {
S: "test2#grr.la"
},
":created_at": {
S: date.toISOString()
},
":updated_at": {
S: date.toISOString()
}
},
Key: {
"id": {
S: "T1"
}
},
ReturnValues: "ALL_NEW",
TableName: "stack",
UpdateExpression: "SET #email = :email, #created_at = if_not_exists(#created_at, :created_at), #updated_at = :updated_at"
};
ddb.updateItem(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data);
})

DynamoDB Update - ExpressionAttributeNames can only be specified when using expressions

I need another set of eyes on this. For the life of me I see no issues with this parameter set, used for Dynamo DocumentClient, update method - (here: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property ).
{
TableName: "mygame-dev",
Key: { pk: "09d017aa-cbf7-42ce-be6a-a94ecb58f9a7", sk: "GAME" },
ExpressionAttributeNames: { "#GAMELASTUPDATED": "gameLastUpdated", "#GAMETITLE": "gameTitle" },
ExpressionAttributeValues: { ":gamelastupdated": 1556376010704, ":gametitle": "test title 1" },
UpdateExpression: "SET #GAMELASTUPDATED = :gamelastupdated, #GAMETITLE = :gametitle",
ReturnValues: "ALL_NEW"
};
Error:
ValidationException: ExpressionAttributeNames can only be specified
when using expressions
Any thoughts?
Disregard, this was a copy and paste issue... I was using "query" not "update"
Was:
const updateGameResult = await ddbCall("query", params);
Should have been:
const updateGameResult = await ddbCall("update", params);