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?
Related
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()
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.
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.
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 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.