I have implemented a single table architecture in my project. I got some problem about using DynamoDB Scan and Query Methods because In my db i got multiple Items about the different modules ex: login,register..etc. So the Key not same in each item. I'm only set the KeyType for identify the Item in yml. I don't know how to use the Scan and Query method in single table arch.
If i use scan method i got the entire item from database. Without any expression or condition ex: FilterExpression, KeyConditionExpression
my yaml file ex:
Resources:
TABLENAMEDynamoDBTable:
Type: 'AWS::DynamoDB::Table'
Properties:
AttributeDefinitions:
-
AttributeName: id
AttributeType: S
KeySchema:
-
AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: TABLENAME
var params = {
TableName: 'TABLENAME'
};
dynamodb.scan(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
Output: Item:[{...},...]
but if i use the expression or condition the error will be through. not only scan. same in query also
var params = {
TableName: 'TABLENAME',
Limit: 10,
FilterExpression:'#user =:val',
ExpressionAttributeNames: {
"#user": "status",
},
ExpressionAttributeValues: {
":val": 'active'
}
};
dynamodb.scan(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
I expected output
Item:[{status:'active',..},{status:'active',..},...]
but I got this error.
"message":"There were 7 validation
errors: InvalidParameterType: Expected params.ExpressionAttributeValues[':val'] to be a structure
UnexpectedParameter: Unexpected key '0' found in params.ExpressionAttributeValues[':val']
UnexpectedParameter: Unexpected key '1' found in params.ExpressionAttributeValues[':val']
UnexpectedParameter: Unexpected key '2' found in params.ExpressionAttributeValues[':val']
UnexpectedParameter: Unexpected key '3' found in params.ExpressionAttributeValues[':val']
UnexpectedParameter: Unexpected key '4' found in params.ExpressionAttributeValues[':val']
UnexpectedParameter: Unexpected key '5' found in params.ExpressionAttributeValues[':val']... etc
So anyone can resolve this problem and explain about the single table architecture design, scan and query to use.
Related
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.
I'm querying a dynamodb table with a partion key and a sort key using serverless framework and nodejs. I'm not sure to be using a correct syntax because if y add a condition over the sort key I'm always getting a null result.
I'm trying this:
//Table definition serverless.yml
resources:
Resources:
ConsentDynamoDBTable:
Type: 'AWS::DynamoDB::Table'
Properties:
AttributeDefinitions:
-
AttributeName: domain
AttributeType: S
-
AttributeName: ts
AttributeType: N
KeySchema:
-
AttributeName: domain
KeyType: HASH
-
AttributeName: ts
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
TableName: ${self:custom.tableName}
//THE QUERY
app.get("/consents/:domain/:startDate/:endDate", function (req, res) {
const params = {
TableName: DEV_TABLE,
KeyConditionExpression: "#dom = :doma AND #tstamp BETWEEN :startDate AND :endDate",
ExpressionAttributeNames: {
"#dom": "domain",
"#tstamp": "ts"
},
ExpressionAttributeValues: {
":doma": req.params.domain,
":startDate": req.params.startDate,
":endDate": req.params.endDate
}
};
console.log("PARAMS: ", params);
dynamoDb.query(params, function(err, data){
if (err) {
res.status(400).json({ error: "Could not get user" });
}
if (data.Items) { // HERE COMES THE ERROR --> TypeError: Cannot read property 'Items' of null
res.json(data.Items);
} else {
res.status(404).json({ error: "User not found" });
}
});
});
I should be seeing some output or even a [] (there are registers that match the query) but getting a null and:
console.log --> PARAMS: { TableName: 'table-dev',
KeyConditionExpression: '#dom = :doma AND #tstamp BETWEEN :startDate AND :endDate',
ExpressionAttributeNames: { '#dom': 'domain', '#tstamp': 'ts' },
ExpressionAttributeValues:
{ ':doma': 'url.com',
':startDate': '1555857529129',
':endDate': '1555857536819' } }
2019-04-21 17:36:21.411 (+02:00) c7f9720f-ea15-4e15-b508-9ffea47935dd TypeError: Cannot read property 'Items' of null
at Response.<anonymous> (/var/task/index.js:95:11)ยดยดยด
ok, I found the answer, I had to parse the string to integer:
const params = {
TableName: DEV_TABLE,
KeyConditionExpression: "#dom = :doma AND #tstamp BETWEEN :startDate AND :endDate",
ExpressionAttributeNames: {
"#dom": "domain",
"#tstamp": "ts"
},
ExpressionAttributeValues: {
":doma": req.params.domain,
":startDate": parseInt(req.params.startDate),
":endDate": parseInt(req.params.endDate)
}
};
To avoid such issues I like to use dynamodb-sql open source which enable working with Dynamo using SQL syntax
Select Example
/**
*
* #param dynamoConfig
* #param sql
* #param tableName
* #param workFlowParams
* #returns {Promise<*>}
*/
static async getFromCache(dynamoConfig, sql, tableName, workFlowParams) {
//Option 1 - Try to get results from Dynamo
try {
let db = require('#awspilot/dynamodb-sql')(dynamoConfig)
return await db.queryp(`select * from ${tableName} where ${tableName}Key = '${SHA256(JSON.stringify(sql)).words.join('')}'`)
} catch (err) {
console.log(`Failed to get from cache: ${JSON.stringify(err)}`)
return [err, null]
}
}
Insert Example
static async updatePolicyLog (clientJobId, orgId, userId, policy, reason, dynamoConfig) {
let db = require('#awspilot/dynamodb-sql')(dynamoConfig)
let insert = `insert into ${tableId} set id = '${clientJobId ? clientJobId : shortId.generate()}',
orgId = ${orgId},
userId = ${userId},
createTime = '${moment(new Date()).format("YYYY-MM-DD HH:mm:ss.SSS")}'`
let [err, data] = await db.queryp(insert)
}
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?
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.
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 :)