Cant update Dynamo Db table , getting ValidationException - node.js

I need to update my dynamo db table by using only partition key. But i got validation exeption.
I have created a table with 3 fields.
id (Partition Key)
name (Sort Key)
age
Then i have triyed to update age field using only id.(tryied to modify age 30 to 40) this is my code
var AWS = require("aws-sdk");
AWS.config.update({
region: "us-east-1",
});
var params = {
TableName: 'test',
Key: { id: '100' },
UpdateExpression: 'set #age = :age ',
ConditionExpression: '#age = :testAge',
ExpressionAttributeNames: { '#age': 'age' },
ExpressionAttributeValues: { ':age': '40', ':testAge': '30' }
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.update(params, function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
}
});
But i got error like this.
{ [ValidationException: The provided key element does not match the schema]
message: 'The provided key element does not match the schema',
code: 'ValidationException',
time: Thu Nov 17 2016 22:38:01 GMT+0530 (IST),
requestId: '34PNMFM6CEACQIRHTSV77OI0JRVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 0 }
After getting error, i modified my params variable like this
var params = {
TableName: 'test',
Key: { id: '100',name: 'manaf' },
UpdateExpression: 'set #age = :age ',
ConditionExpression: '#age = :testAge',
ExpressionAttributeNames: { '#age': 'age' },
ExpressionAttributeValues: { ':age': '40', ':testAge': '30' }
};
Using this, updation is successfully completed. How to update table using without sort key?

Currently, the DynamoDB update API doesn't have an option to update the item by partition key only. There is no batchUpdateItem API similar to batchWriteItem as well.
So, if the sort key is not available, get all the sort keys of partition key and update each item for the partition and sort key combination.
For the primary key, you must provide all of the attributes. For
example, with a simple primary key, you only need to provide a value
for the partition key. For a composite primary key, you must provide
values for both the partition key and the sort key.
Sample code:-
You may need to change it for your table. The below code uses "Movies" table which has "yearkey" as partition key and "title" as sort key.
The below code updates the "createdate" attribute for the given hash key "2012".
The variable paramsUpdate is formed based on the query operation. Please update it accordingly for your requirement (i.e. table structure). Logic remains same, you just need to change the table name and key values accordingly.
var AWS = require("aws-sdk");
var creds = new AWS.Credentials('akid', 'secret', 'session');
AWS.config.update({
region : "us-west-2",
endpoint : "http://localhost:8000",
credentials : creds
});
var docClient = new AWS.DynamoDB.DocumentClient();
var hashKey = 2012;
var paramsQuery = {
TableName : "Movies",
KeyConditionExpression : 'yearkey = :hkey',
ExpressionAttributeValues : {
':hkey' : hashKey
}
};
function updateItem(paramsUpdate) {
console.log("Updating the item...");
docClient.update(paramsUpdate, function(err, data) {
if (err) {
console.error("Unable to update item. Error JSON:", JSON.stringify(
err, null, 2));
} else {
console.log("UpdateItem succeeded:", JSON.stringify(data));
}
});
}
docClient.query(paramsQuery, function(err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err,
null, 2));
} else {
console.log(data.Count);
var itemIndex = 0;
while (itemIndex < data.Count) {
console.log('Hashkey to be updated ======>',
data.Items[itemIndex].yearkey,
';Title to be updated ========>',
data.Items[itemIndex].title);
var paramsUpdate = {
TableName : "Movies",
Key : {
"yearkey" : data.Items[itemIndex].yearkey,
"title" : data.Items[itemIndex].title
},
UpdateExpression : "set #createdate = :createdate",
ExpressionAttributeNames : {
'#createdate' : 'createdate'
},
ExpressionAttributeValues : {
':createdate' : '2016-11-17'
},
ReturnValues : 'UPDATED_NEW'
};
updateItem(paramsUpdate);
itemIndex++;
}
}
});

In DynamoDB, partition key + sort key is treated as a "composite primary key", which uniquely identifies an item (on the contrary, Dynamo also supports simple primary key, which only contains partition key). So you need both to update an item. This is the reason that you can have two items with the same partition key but different sort key. So if you only provide the partition keys, Dynamo will get confused with which item to update.
For your current table configuration, the only way to update an item given a partition key is to make a query with only partition key to get all the items, and filter out the one with the intended sort key. Then use the combination of partition key and sort key to update this item.

Related

How to delete multiple rows in DynamoDB?

I am attempting to delete data from a DynamoDB table.
If I delete data using a partition key, it works.
But when I delete multiple rows using any other fields, it fails.
var params = {
TableName: "test",
Key: {
dmac: dmac,
},
ConditionExpression: "dmac= :dmac"
};
docClient.delete( params, (error) => {
if (error) {
console.log( "Delete data fail" );
} else {
console.log( "Delete data Success" );
}
});
Items (or rows) in DynamoDB are uniquely identified by their primary key. A table can have a simple primary key (a partition key) or a composite primary key (a partition key plus a sort key).
To delete an item, you must provide the full primary key (whether it's a simple partition key or composite partition key plus sort key).
So, if you want to delete items that meet a specific condition, for example cars with maxspeed < 120, then issue a query or scan to identify those items, retrieve the primary keys, and then delete the items in a second operation.
To delete a single item, use DeleteItem. To delete multiple items, use BatchWriteItem. Despite the naming of BatchWriteItem, it can be used to put multiple items or to delete multiple items, and you can target one or more DynamoDB tables in the same API call.
Here is an AWS SDK v2 example of deleting multiple items:
const aws = require("aws-sdk");
const ddb = new aws.DynamoDB({ region: "us-east-1" });
(async () => {
const params = {
RequestItems: {
albums: []
}
};
params.RequestItems.albums.push({
DeleteRequest: {
Key: {
pk: { S: "The Who" },
sk: { S: "Tommy" }
}
}
});
params.RequestItems.albums.push({
DeleteRequest: {
Key: {
pk: { S: "The Beatles" },
sk: { S: "Abbey Road" }
}
}
});
await ddb.batchWriteItem(params).promise();
})();
Here is an AWS SDK v3 example of deleting multiple items:
const {
BatchWriteItemCommand,
DynamoDBClient
} = require("#aws-sdk/client-dynamodb");
(async () => {
const client = new DynamoDBClient({ region: "us-east-1" });
const params = {
RequestItems: {
albums: []
}
};
params.RequestItems.albums.push({
DeleteRequest: {
Key: {
pk: { S: "The Who" },
sk: { S: "Tommy" }
}
}
});
params.RequestItems.albums.push({
DeleteRequest: {
Key: {
pk: { S: "The Beatles" },
sk: { S: "Abbey Road" }
}
}
});
await client.send(new BatchWriteItemCommand(params));
})();
in DynamoDB you can only delete an item using its key (that is: the partition key and the sort key, if it is defined on the table). This is, for example, underlined by the fact that the Key attribute is a required attribute in the canonical specification of the delete operation. See: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html#DDB-DeleteItem-request-Key
This means that if you want to delete an item using other attributes you must first lookup the item by the attributes you do have, extract the key from the returned item, and then delete the item using that key.
The standard solution for "looking up an item by attributes that are not the item's key" is to define a global secondary index (GSI) on the table with those attribute(s) defined as the GSI's key.

How are primary keys specified with DynamoDB.DocumentClient for NodeJS?

I've been reading a lot of documentation, specifically for AWS.DynamoDB.DocumentClient, and I'm trying to figure out how I create a Table / DocumentClient with a specific primary key, like id. My understanding is that primary keys are required, and that "secondary indexes" are nested attributes, but I don't see any constructor params for actually specifying that id will be the primary key.
How can I specify that id should be a Table / DocumentClient's primary key?
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.NodeJs.03.html
Note
The primary key is required. This code adds an item that has a primary key (year, title) and info attributes. The info attribute stores sample JSON that provides more information about the movie.
Ok, I didn't realize the schema for creating the table was so intricate. Through trial and error with local DynamoDB, it told me that AttributeDefinitions must exist for the KeySchema. This key schema is what designates the "primary" key names, and the types are in a completely separate field.
The constructor object DOES NOT populate the table, it only sets up the initial partition (primary?) key and optional sort key, which would form a composite key.
var AWS = require('aws-sdk');
AWS.config.update({
region: 'us-west-2',
endpoint: 'http://localhost:8000'
});
var db = new AWS.DynamoDB();
var awaitTable = db.describeTable({ TableName: 'app-content' }).promise();
awaitTable.catch(e => {
if (e.statusCode === 400) {
return db.createTable({
TableName: 'app-content',
KeySchema: [
{ 'AttributeName' : 'fooPartitionKeyName', 'KeyType' : 'HASH' },
{ 'AttributeName' : 'barSortKeyName', 'KeyType' : 'RANGE' }
],
AttributeDefinitions: [
{ 'AttributeName' : 'fooPartitionKeyName', 'AttributeType' : 'S' },
{ 'AttributeName' : 'barSortKeyName', 'AttributeType' : 'S' }
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
}).promise();
} else { return Promise.reject('Unknown error fetching table.') }
}).then(table => {
console.log('table!', table);
});
KeySchema: [
{
AttributeName: 'ID',
KeyType: 'HASH'
},
This should work right?

DynamoDB scanning using filter

Im new to DynamoDB and have a table which is "feeds" and partition key is "id" and i have 3 other attributes which are "category", "description", "pubDate".
I want to query the "category" attribute. But it doesn't work, because i can only query the partition key (hashkey), if im right.
Now my query is that which doesnt work;
let category = event.category;
const params = {
Key: {
"category": {
S: category
}
},
TableName: "feeds"
};
dynamodb.getItem(params, function (err, data) {
if (err) {
console.log(err);
callback(err);
}
else {
console.log(data);
callback(null, data);
}
});
How can i make it work? I tried to write a scan query but i couldn't understand the documentation of AWS good.
Edit: I did make it work with the help of Dunedan. Here is the working code,
var params = {
TableName: 'feeds',
IndexName: 'category-index',
KeyConditionExpression: 'category = :category',
ExpressionAttributeValues: {
':category': 'backup',
}
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.query(params, function(err, data) {
if (err) callback(err);
else callback(null, data);
});
If your application will regularly query for the category, you should check out Global Secondary Indexes (GSI), which allow you to generate a projection of your data with another key than the original hash key as the key you can use to query.
Scanning and filtering as you suggested doesn't scale very well, as it fetches all data in the table and just filters the results.

How to fetch data from dynamodb with Keycondition expression with sort key timestamp?

I am facing some issue with querying data from DynamoDB can any one of you help me on this? I am coding in NodeJS.
My table looks like below with,
Primary key: RequestId
Secondary index: Userid with sortkey Timestamp
When I am pulling the data using UserId, I am getting lot's for records so, planning to pull the data with Timestamp condition.
RequestId Request Timestamp UserId
var doc = require("dynamodb-doc");
var dynamo = new doc.DynamoDB();
var userid_col = "amzn1.ask.account.AGCAPY7JBHQWHTNGAHJJ";
var databaserec = { TableName: "dna_cdknow_prod_historylog",
IndexName: "UserId-TimeStamp-index",
KeyConditionExpression: "UserId = :input",
FilterExpression : 'created between :val1 and :val2',
ExpressionAttributeValues:{ ":input": userid_col, ":val1" : "2016-05-23T00:00:00Z", ":val2" : "2017-05-23T16:20:49Z" } };
The below code should work if the GSI definition is as follows:-
UserId - Partition key of GSI
created - Sort key of GSI
Corrected code:-
var databaserec = {
TableName: "dna_cdknow_prod_historylog",
IndexName: "UserId-TimeStamp-index",
KeyConditionExpression: "UserId = :input AND created between :val1 and :val2'",
ExpressionAttributeValues:{ ":input": userid_col, ":val1" : "2016-05-23T00:00:00Z", ":val2" : "2017-05-23T16:20:49Z" }
};
docClient.query(databaserec, function(err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
} else {
console.log(params);
console.log("Query succeeded.");
data.Items.forEach(function(item) {
console.log("Item :" + JSON.stringify(item));
});
}
});

DynamoDB ConditionalExpression not recognizing Attribute Names or Values

I'm using NodeJS's aws-sdk and am trying to do an update such that if the item doesn't exist it will throw an error. I'm using the Expression API instead of the legacy one. Here is my contrived example that isn't working for me.
client.update({
TableName: 'User',
Key: {'_id': '10'},
UpdateExpression: 'SET username = :user, password = :pword',
ConditionalExpression: 'attribute_exists(#idKey) AND #idKey = :idVal',
ExpressionAttributeNames: {
'#idKey': '_id'
},
ExpressionAttributeValues: {
':idVal': '10',
':user': 'user10',
':pword': 'password10'
}}, function(err, data){
if(err) console.log(err);
else console.log(data);
});
ValidationException: Value provided in ExpressionAttributeNames unused in expressions: keys: {#idKey}
I've tried various other ConditionalExpressions both using attribute names and inserting the actual value into the expression. I'm beginning to think this is a bug. Using the legacy Expected->Exists with the legacy AttributeUpdate works but I am unable to demonstrate this feature with Expressions.
You are already narrowing down to the specific item where _id=10 with the Key parameter of your UpdateItemRequest. If an item does not exist, you cannot condition the UpdateItem call on a specific value of a key. Therefore, only the attribute_exists(#idKey) in the ConditionExpression is necessary.
The following code elicits the behavior you desire (I had to change table name to Images and primary key to Id to match the contents of the DynamoDB Local Shell tutorial.
var params = {
TableName: 'Image',
Key: { // The primary key of the item (a map of attribute name to AttributeValue)
'_id': 'dynamodb.png'
},
UpdateExpression: 'SET username = :user, password = :pword',
ConditionExpression: 'attribute_exists(#id)',
ExpressionAttributeValues: {
':user': 'user10',
':pword': 'password10'
},
ExpressionAttributeNames: {
'#id': '_id'
},
ReturnValues: 'ALL_NEW'
};
docClient.update(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
As a reminder, please do not post any real password data here :)

Resources