Unexpected key '0' found in params.ExpressionAttributeValues - node.js

I'm trying to replace an array in an dynamodb item, but I'm getting the error
* UnexpectedParameter: Unexpected key '0' found in params.ExpressionAttributeValues[':myArray']
* UnexpectedParameter: Unexpected key '1' found in params.ExpressionAttributeValues[':myArray']
* UnexpectedParameter: Unexpected key '2' found in params.ExpressionAttributeValues[':myArray']
* UnexpectedParameter: Unexpected key '3' found in params.ExpressionAttributeValues[':myArray']
* UnexpectedParameter: Unexpected key '4' found in params.ExpressionAttributeValues[':myArray']
I thought I'm setting the attributes correctly, but I guess not.
dynamo record
{
"PK": "myItem",
"myArray": [
{
"timeStamp": 1672678545
},
...more objects
]
}
let params = {
UpdateExpression: 'set #myArray = :myArray',
ExpressionAttributeNames: {
'#myArray': 'myArray'
},
ExpressionAttributeValues: {
':myArray': filteredOutObjects
},
Key: {
PK: {
S: 'myItem'
}
},
TableName: 'configs',
ReturnValues: 'ALL_NEW'
};
try {
const result = await dynamo.updateItem(params).promise();
console.log('RESULT', result);
return result;
} catch (e) {
console.log('ERROR', e);
}

filteredOutObjects should be a suitable data type for DynamoDB and look like the following:
filteredOutObjects = { 'L' : [1,2,3...] }
Or rather:
ExpressionAttributeValues: {
':myArray': {
'L': filteredOutObjects
}
},
If you want to just pass an array as filteredOutObjects then you should use the Document Client.
The DynamoDB Document client simplifies working with items by abstracting the notion of attribute values. This abstraction annotates native JavaScript types supplied as input parameters, and converts annotated response data to native JavaScript types.

Related

How to execute multiple operations while updating an item in Amazon DynamoDB?

I'm trying to update an item in Amazon DynamoDB. I want to execute an operation that consists of one sum and one subtraction, but I'm not able to accomplish my goal.
Here is the function, ddb is an instance of the class AWS.DynamoDB.DocumentClient:
function updateLecture(event){
const params = {
TableName: 'lecture',
Key: {
'lecture_id': Number.parseInt(event.lecture_id)
},
UpdateExpression: 'SET #free = :total - (#total + #free)',
ExpressionAttributeValues: {
':total': event.total
},
ExpressionAttributeNames: {
"#total" : "total",
"#free" : "free"
},
ReturnValues : 'UPDATED_NEW'
}
return ddb.update(params).promise();
}
When I try to run the function, I get the following error:
"errorType": "ValidationException",
"errorMessage": "Invalid UpdateExpression: Syntax error; token: \"+\", near: \"#total + #free\""
I've tried to work with parenthesis, but I always receive the same error.
I'm afraid I can't use more than one operation, but I couldn't find any trace of that in the docs. Anyway, is there a way to use multiple operations?
Just tried the following PartiQL statement in the DynamoDB console, and it worked fine:
// Works just fine
UPDATE "ddb-playground"
SET myNumA = 10 - (myNumB + myNumA)
WHERE PK = 'foo' AND SK = 'bar'
It runs just fine, so at least there's no fundamental limitation here.
However, just like you, I'm not able to get the JS SDK to do this with an UpdateExpression, I get syntax error no matter what I try. So it seems like the JS SDK is limited to a single arithmetic expression on numeric values.
// Fails with:
//
// ValidationException: Invalid UpdateExpression: Syntax error; token: "+", near: "myNumB + myNumA"
import AWS from "aws-sdk"
const ddb = new AWS.DynamoDB()
await ddb.putItem({
TableName: "ddb-playground",
Item: {
PK: {S: "foo"},
SK: {S: "bar"},
myNumA: {N: "10"},
myNumB: {N: "20"}
}
}).promise()
await ddb.updateItem({
TableName: "ddb-playground",
Key: {
PK: {S: "foo"},
SK: {S: "bar"}
},
UpdateExpression: "SET myNumA = :newVal - (myNumB + myNumA)",
ExpressionAttributeValues: {
":newVal": {N: "30"}
}
}).promise()
So perhaps your only option here is to actually use PariQL for this one?
await ddb.executeStatement({
Statement: 'UPDATE "ddb-playground" SET myNumA = ? - (myNumB + myNumA) WHERE PK = ? AND SK = ?',
Parameters: [{N: "30"}, {S: "foo"}, {S: "bar"}]
}).promise()

DynamoDB - Delete Item based on ConditionExpression on Sort Key

Is it possible to delete records from dynamodb based on PrimaryKey and ConditionExpression on SortKey?
Following code sample is throwing an exception for me
DeleteVideoCall = async function (pk, sk) {
let params = {
TableName: this._tableName,
Key: {
pk: { S: pk.toString() },
sk: { S: sk.toString() }
},
ConditionExpression: "begins_with(sk,:sk)",
ExpressionAttributeValues: {
":sk" : { S: sk.toString() + "_" }
}
};
return this._ddb
.deleteItem(params)
.promise()
.then((data) => {
console.log(`Video Call '${pk}/${sk}' deleted`);
return null;
})
.catch((error) => {
console.error(
`Error deleting video room '${pk}/${sk}' (${error})`
);
throw error;
});
};
I want to delete all records that begins_with sk and . For example if sk is 560622 then delete all records where sk begins_with 560622
with the code above I get this error:
Error deleting video room '10900/560622'
(ConditionalCheckFailedException: The conditional request failed)
You can't do that. You need whole key to perform a delete. What you can do:
query items (limit retrieved properties to your PK and SK)
use batch-write to remove multiple items, it also accepts delete requests

Get criteria not match the schema

Im trying to get an Item from DynamoDB based on Primary Key but it throws me an exception:
ValidationException: The provided key element does not match the schema
Here is how my table looks:
I'm following a tutorial and here is how I wrote my get:
let params = {
TableName: process.env.CALL_NAVEGATION_HISTORY_TABLE,
Key: {
"Id": requestBody.CallSid
}
}
dynamoDb.get(params, function(err, data) {
if(err){
console.log('Error on dynamodb', err);
callback(null, Helpers.xmlTwimlResponse(twiml));
}
console.log(data);
callback(null, Helpers.xmlTwimlResponse(twiml));
});
What is wrong on my code?
Sometimes the most obvious thing is what we miss right in front of our eyes.
let params = {
TableName: process.env.CALL_NAVEGATION_HISTORY_TABLE,
Key: {
"Id": requestBody.CallSid
}
}
The Key name is case-sensitive. If you change it to 'id' it should work fine.

Response from db is offset, not all results returned using map function

Why is offset: 1?
I'm expecting to see 2 rows in the response from Cloudant, as total_rows indicates, but its offset for some reason so only 1 row is present in response.
Console output:
{ total_rows: 2,
offset: 1,
rows:
[ { id: '1e0a2d30d18d95b9bcc05d92c883d496',
key: '1e0a2d30d18d95b9bcc05d92c883d496',
value: [Object] } ] }
Calling the function, handle response:
viewImpl('test', 'something').then(function(result) {
result.rows.forEach(function(doc) {
console.log(doc.value);
});
res.status(200).send('done');
}
}).catch(someFunc());
Function def:
App.prototype.viewImpl = function(designName, viewName) {
return new Promise(function(fulfill, reject) {
_this.db.view(designName, viewName, { keys: ["1e0a2d30d18d95b9bcc05d92c883d496","1e0ad30d18d95b9bcc05d92c883e272"] }, function(err, result) {
if (err) {
reject(err);
} else {
fulfill(result);
}
});
});
};
View defined in Cloudant:
function(doc) {
emit(doc._id, { _id: doc._id, _rev: doc._rev, some_field: doc.some_field });
}
Take a look at the keys provided to the view() function:
1e0a2d30d18d95b9bcc05d92c883d496
1e0ad30d18d95b9bcc05d92c883e272
The second key is one character shorter - it's just 31 characters instead of 32. I guess you just missed the last character when copy pasting it from document ID.
Remove the keys parameters to see all rows, then check what is the correct key for the second row and update your code.

How to use 'BatchGetItem' for the NodeJS AWS-SDK for DynamoDB

I am trying to get items out of a DynamoDB table using the Node JS AWS-SDK. The function getItem is working fine but BatchGetItem is harder to use.
I use the official documentation:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/Client.html#batchGetItem-property
I am looking for examples on how to use this function correctly, but I can't find any. The code I wrote is:
var params = {
"RequestItems" : {
"Keys" : [
{"HashKeyElement" : { "N" : "1000" } },
{"HashKeyElement" : { "N" : "1001" } }
]
}
}
db.client.batchGetItem(params, function(err, data) {
console.log('error: '+ err);
console.log(jsDump.parse(data));
});
I get a SerializationException: Start of list found where not expected error but as far as my NodeJS and JSON expertise goes, my syntax is correct. But it's confusing:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API_BatchGetItems.html
In that syntax example, you have to provide the table name.
I used the dynamo db client version... after an hour of research I managed to make it work...
var params = {
RequestItems: { // map of TableName to list of Key to get from each table
Music: {
Keys: [ // a list of primary key value maps
{
Artist: 'No One You Know',
SongTitle:'Call Me Today'
// ... more key attributes, if the primary key is hash/range
},
// ... more keys to get from this table ...
],
AttributesToGet: [ // option (attributes to retrieve from this table)
'AlbumTitle',
// ... more attribute names ...
],
ConsistentRead: false, // optional (true | false)
},
// ... more tables and keys ...
},
ReturnConsumedCapacity: 'NONE', // optional (NONE | TOTAL | INDEXES)
};
docClient.batchGet(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
I feel your pain... AWS documentation is confusing at best. I think it's caused by aging infrastructure and bad technical writing. The nodejs and JSON syntax used by the SDK reminds me of XML structure.
Anyway, I managed to get the BatchGetItem to work after a whole hour. The params should look like below:
{
"RequestItems": {
"<TableName>": {
"Keys": [
{"<HashKeyName>": {"<type>":"<hash key value>"}},
{"<HashKeyName>": {"<type>":"<hash key value>"}},
{"<HashKeyName>": {"<type>":"<hash key value>"}}
]
}
}
}
I believe that you're missing table name. You want this:
var params = {
"RequestItems" : {
"TableName": {
"Keys" : [
{"HashKeyElement" : { "N" : "1000" } },
{"HashKeyElement" : { "N" : "1001" } }
]
}
}
}
I tried all of the solutions here and none of them worked for me, which likely means the NodeJS library has gotten an update. Referencing their better written docs, you should be able to make a request like so:
var params = {
RequestItems: {
'Table-1': {
Keys: [
{
HashKey: 'haskey',
NumberRangeKey: 1
}
]
},
'Table-2': {
Keys: [
{ foo: 'bar' },
]
}
}
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.batchGet(params, function(err, data) {
if (err) console.log(err);
else console.log(data);
});
Specifically, providing the type is no longer necessary.
Try this:
db.client.batchGetItem(
{"RequestItems":{
"TableName":{
"Keys":[
{"HashKeyElement" : {"N":"1000"}},
{"HashKeyElement" : {"N":"1001"}}
]
}
}
}, function(err, result){ //handle error and result here });
In your case, the correct answer should be:
var params = {
"RequestItems": {
"<table_name>": {
"Keys": [
{"HashKeyElement" : { "N" : "1000" } },
{"HashKeyElement" : { "N" : "1001" } }
]
}
}
}
Try this, it's untested though:
var params = {
TableName: "tableName",
RequestItems : {
Keys : [
{
HashKeyElement : { N : "1000" }
},
{
HashKeyElement : { N : "1001" }
}
]
}
}

Resources