How to update item by primary key and other conditions? - amazon-dynamodb

I am trying to update item by email (HASH PK), id and verifyToken. My query looks like this:
params =
TableName: 'users'
Key:
email:
S: 'example#email.com'
AttributeUpdates:
verified:
Action: 'PUT'
Value:
BOOL: true
verifyToken:
Action: 'DELETE'
ExpressionAttributeValues:
':id': { S: '123' }
':verifyToken': { S: 'XXX' }
ConditionExpression: 'id = :id and verifyToken = :verifyToken'
dynamodb.updateItem(params)
In other words I want to update Item where email = 'example#email.com' AND id = '123' AND verifyToken = 'XXX', but I am getting following error:
Can not use both expression and non-expression parameters in the same request:
Non-expression parameters: {AttributeUpdates}
Expression parameters: {ConditionExpression}

You are combining legacy parameters (AttributeUpdates), which are only there for backwards compatibility, with expression parameters (ConditionExpression). As the error states, you cannot do that.
You need to use an UpdateExpression in conjunction with your ConditionExpression.
It would be something like this. You may need to use expression attribute names/values in the UpdateExpression:
ConditionExpression: 'id = :id and verifyToken = :verifyToken'
UpdateExpression: 'SET verified = true, REMOVE verifyToken'
See this documentation for more information on update expressions

Related

How to force the DynamoDB query's ExclusiveStartKey to use exact match?

I'm using DynamoDB for my new Serverless Restful API with nodejs.
The Restful API supports query for resources with the limit and lastKey query parameters for key pagination.
Assume there's a table like below:
PK
SK
School
firstSchool
School
secondSchool
School
thirdSchool
PK is partition key, and SK is sort key.
I use SK for key pagination.
If I call the api with http://somewhere/api/school?limit=1&lastKey=secondSchool, ExclusiveStartKey in query will be {"PK" : "School", "SK" : "secondSchool"}, and the returned item will be {"PK" : "School", "SK" : "thirdSchool"}.
It works well in that case, but the problem is the same result is created with the url like http://somewhere/api/school?limit=1&lastKey=seco.
In this case, ExclusiveStartKey in query will be {"PK" : "School", "SK" : "seco"}
It seems DynamoDB doesn't use exact match for a sk value in ExclusiveStartKey.
Is there any way to force DynamoDB to use exact match for ExclusiveStartKey?
I attach my test code below:
const { DynamoDBClient } = require("#aws-sdk/client-dynamodb");
const { DynamoDBDocument } = require("#aws-sdk/lib-dynamodb");
const ddbClient = new DynamoDBClient({
region: AWS_REGION,
endpoint: AWS_DYNAMODB_END_POINT,
credentials: {
accessKeyId: AWS_ACCESSKEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
},
});
const ddbDocClient = DynamoDBDocument.from(ddbClient);
(async () => {
try {
const data = await ddbDocClient.query({
TableName: "Table Name",
KeyConditionExpression: "#pk = :pk",
ExpressionAttributeNames: {
"#pk": "PK",
},
ExpressionAttributeValues: {
":pk": "Test",
},
Limit: 1,
ExclusiveStartKey: { PK: "Test", SK: "Seco" },
});
console.log(data);
} catch (err) {
console.log("Error", err);
}
})();
The ExclusiveKeyStart is used mainly for paging large Scan or Query requests - i.e., retrieving the next page of results after the previous page ended with a LastEvaluatedKey, and you are supposed to give exactly that key (not some subset of it...) as the ExclusiveKeyStart of the next request.
You are trying to do something different, and to achieve you can't use ExclusiveKeyStart, but you can use something else:
The Query request has a KeyConditionExpression. You can specify sk > :value as a key condition expression (don't pass ExclusiveKeyStart), and you'll get this all the sort keys higher than that :value like your string "seco". Please note, however, that because your sort key is truncated, this result may actually include one or more extra results before the first key you want (e.g., the keys "seco" and "secoaaaa" come before "secondSchool") so you may need to drop them yourself from the results.
The KeyConditionExpression is implemented efficiently - DynamoDB knows how to skip directly to that sort key in the partition, and doesn't charge you for reading the entire partition, so in this respect it is just as good as ExclusiveKeyStart.

DynamoDB if attribute doesn't exist create it, in node.js

I'm trying to update a user objects attribute which may not exist.
The attribute is called claimed which itself will have a property for each currency type #c1 (USD, euro, ect). A user can have multiple currencies but starts with none so claimed may or may not exist on a user object.
My initial draft was:
let params = {
TableName: 'myproject-user',
Key: {"id":req.user.sub},
UpdateExpression: 'set claimed.#c1 = :o',
ExpressionAttributeValues:{
":o": req.body.currency
},
ExpressionAttributeNames:{ "#c1": req.body.currency.type },
ReturnValues:"UPDATED_NEW"
};
This returns the error:
"Error: ValidationException: The document path provided in the update expression is invalid for update"
I've tried some variations using if_not_exists but I can't seem to get it working. How can I modify the params to have the desired effect?
you can use ConditionExpression: "attribute_not_exists(<YOUR_ATTRIBUTE>)"
for example:
let params = {
TableName: 'myproject-user',
Key: {"id":req.user.sub},
UpdateExpression: 'set claimed.#c1 = :o',
ExpressionAttributeValues:{
":o": req.body.currency
},
ExpressionAttributeNames:{ "#c1": req.body.currency.type },
ConditionExpression: "attribute_not_exists(claimed.#c1)",
ReturnValues:"UPDATED_NEW"
};

Unable to use IN operator in DynamoDb Query

The problem is I am getting error (Filter Expression can only contain non-primary key attributes: Primary key attribute: name).
tenant is my Primary Partition Key and name is my Primary Sort Key.
I need to write something equivalent to this in dynamo db:
Select * from projects where tenant = 'testProject' and name in ('John','Dave').
query = {
TableName: 'projects',
ExpressionAttributeNames: {
'#tenant': 'tenant',
'#name' : 'name'
},
ExpressionAttributeValues: {
":tenant": 'testProject',
":user1" : "John",
":user2" : "Dave"
},
KeyConditionExpression: '#tenant = :tenant',
FilterExpression: '#name IN (:user1,:user2)'
};
As of the day that I am writing this, you cannot use IN in your key condition expression, nor can you include a key attribute in your filter expression.
The way to do what you’re wanting to do is with a BatchGetItem request, which allows you to specify the full primary key for multiple items.

dynamo db FilterExpression, find in json object using value as key

It is possible to somehow filter results by key name that stored in the same object?
I have JSON object "keys", in property "default" stored key of the object that I need. Is it somehow possible to filter like that keys[keys.default].type = some_type?
var params = {
TableName: 'TABLE_NAME',
IndexName: 'TABLE_INDEX', // optional (if querying an index)
KeyConditionExpression: 'myId = :value',
FilterExpression: '#kmap[#kmap.#def].#tp = :keyval',
ExpressionAttributeNames: {names with special characters
'#kmap': 'keys',
'#tp': 'type',
'#def': 'default'
},
ExpressionAttributeValues: { // a map of substitutions for all attribute values
':value': '1',
':keyval': 'some_type'
},
Limit: 10, // optional (limit the number of items to evaluate)
ProjectionExpression: "displayName, #kmap",
ReturnConsumedCapacity: 'TOTLAL', // optional (NONE | TOTAL | INDEXES)
};
docClient.query(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
I'm pretty sure the answer is no.
This keys[keys.default] is not even valid json, as far as I can tell.
Of course, you can do this in two steps:
First, query to get the default key
Then query to get the value
Don't forget, filters are obly applied to the result set - it still requires a libear traversal as specified by your Query or Scan operation.
So you can probably more easily run your query on the client.
And lastly, if this is a typical query ypu need to perform, as an optimization, you can lift the default key and value to be top level attributes on the item. Thrn you can actually create a GSI on that attribure and can actually do efficient lookups.

DynamoDB query with Lambda (node.js): Query key condition not supported

I try to query my dynamoDB from a Lambda function. My table uses "id" as the hash key. I tried both versions below and received the corresponding error messages. What am I doing wrong?
var params = {
TableName : "addresses",
KeyConditionExpression: "id = :id AND city = :city",
ExpressionAttributeValues: {
":id": "Austria",
":city": "Salzburg"
}
};
Unable to query. Error: {
"message": "Query key condition not supported",...}
var params = {
TableName : "addresses",
KeyConditionExpression: "city = :city",
ExpressionAttributeValues: {
":city": "Salzburg"
}
};
Unable to query. Error: {
"message": "Query condition missed key schema element: id",...}
EDIT:
I now added secondary indices, but still get the same errors:
if your hash key is 'id' then you cant query by:
KeyConditionExpression: "id = :id AND city = :city"
or by:
KeyConditionExpression: "city = :city"
you can query dynamodb only by hash and range key.
so your query should contain always hash key (id). if you want to query by 'city' also, you should add 'city' as range key to dynamodb table (or local secondary index)
then you can query for a record with 'id' and 'city'.
update:
if you want to query for 'city'
KeyConditionExpression: "city = :city"
then you can just add global secondary index to the table.

Resources