Unable to Upsert data in DynamoDB using WriteRequest JAVA - amazon-dynamodb

I have a java application which is building a DynamoDB client write request as
WriteRequest.builder().putRequest(PutRequest.builder().item(attributeValueMap).build()).build();
The above request is replacing the items with same PartitionKey and SortKey instead of upserting the data into the table. Any idea what am I doing wrong or do I need a to pass any additional parameter in PutRequest ?

As the commenter mentions, you want to use UpdateItem if you want to "patch" an item. PutItem will replace the entire item. You can read more about the differences here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.WritingData
This is a simplified code sample for how that works in Javav2 SDK.
HashMap<String,AttributeValue> itemKey = new HashMap<>();
itemKey.put(key, AttributeValue.builder()
.s(keyVal)
.build());
HashMap<String,AttributeValueUpdate> updatedValues = new HashMap<>();
// Put your attributes/values you wish to update here.
// Attributes you don't include won't be effected by the update
updatedValues.put(name, AttributeValueUpdate.builder()
.value(AttributeValue.builder().s(updateVal).build())
.action(AttributeAction.PUT)
.build());
UpdateItemRequest request = UpdateItemRequest.builder()
.tableName(tableName)
.key(itemKey)
.attributeUpdates(updatedValues)
.build();
ddb.updateItem(request);

Related

AWS Java SDK DynamoDB, how to get attribute values from ExecuteStatementRequest response?

I'm using Java AWS SDK to query a DynamoDB table using ExecuteStatementRequest, but I'm don't know how to fetch the returned attribute values from the response.
Given I have the following query:
var response2 = client.executeStatement(ExecuteStatementRequest.builder()
.statement("""
UPDATE "my-table"
SET thresholdValue= thresholdValue + 12.5
WHERE assignmentId='item1#123#item2#456#item3#789'
RETURNING ALL NEW *
""")
.build());
System.out.println(response2.toString());
System.out.println(response2.getValueForField("Items", Collections.class)); // Doesn't cast to
This query executes fine and returns as part of the response attributes, however I can't find a way to get these values out of the response object using Java.
How can I do that?
I have found how to do that, however I'm not sure it this is the indicated way as the documentation doesn't provide any examples.
List items = response2.getValueForField("Items", List.class).get();
for (Object item : items) {
var values = (Map<String, AttributeValue>) item;
System.out.println(values.get("assignmentId").s());
System.out.println(values.get("thresholdValue").n());
}

DynamoDb - .NET Object Persistence Model - LoadAsync does not apply ScanCondition

I am fairly new in this realm and any help is appreciated
I have a table in Dynamodb database named Tenant as below:
"TenantId" is the hash primary key and I have no other keys. And I have a field named "IsDeleted" which is boolean
Table Structure
I am trying to run a query to get the record with specified "TenantId" while it is not deleted ("IsDeleted == 0")
I can get a correct result by running the following code: (returns 0 item)
var filter = new QueryFilter("TenantId", QueryOperator.Equal, "2235ed82-41ec-42b2-bd1c-d94fba2cf9cc");
filter.AddCondition("IsDeleted", QueryOperator.Equal, 0);
var dbTenant = await
_genericRepository.FromQueryAsync(new QueryOperationConfig
{
Filter = filter
}).GetRemainingAsync();
But no luck when I try to get it with following code snippet (It returns the item which is also deleted) (returns 1 item)
var queryFilter = new List<ScanCondition>();
var scanCondition = new ScanCondition("IsDeleted", ScanOperator.Equal, new object[]{0});
queryFilter.Add(scanCondition);
var dbTenant2 = await
_genericRepository.LoadAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig
{
QueryFilter = queryFilter,
ConditionalOperator = ConditionalOperatorValues.And
});
Any Idea why ScanCondition has no effect?
Later I also tried this: (throw exception)
var dbTenant2 = await
_genericRepository.QueryAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig()
{
QueryFilter = new List<ScanCondition>()
{
new ScanCondition("IsDeleted", ScanOperator.Equal, 0)
}
}).GetRemainingAsync();
It throws with: "Message": "Must have one range key or a GSI index defined for the table Tenants"
Why does it complain about Range key or Index? I'm calling
public AsyncSearch<T> QueryAsync<T>(object hashKeyValue, DynamoDBOperationConfig operationConfig = null);
You simply cant query a table only giving a single primary key (only hash key). Because there is one and only one item for that primary key. The result of the Query would be that still that single item, which is actually Load operation not Query. You can only query if you have composite primary key in this case (Hash (TenantID) and Range Key) or GSI (which doesn't impose key uniqueness therefore accepts duplicate keys on index).
The second code attempts to filter the Load. DynamoDBOperationConfig's QueryFilter has a description ...
// Summary:
// Query filter for the Query operation operation. Evaluates the query results and
// returns only the matching values. If you specify more than one condition, then
// by default all of the conditions must evaluate to true. To match only some conditions,
// set ConditionalOperator to Or. Note: Conditions must be against non-key properties.
So works only with Query operations
Edit: So after reading your comments on this...
I dont think there conditional expressions are for read operations. AWS documents indicates they are for put or update operations. However, not being entirely sure on this since I never needed to do a conditional Load. There is no such thing like CheckIfExists functionality as well in general. You have to read the item and see if it exists. Conditional load will still consume read throughput so your only advantage would be only NOT retrieving it in other words saving the bandwith (which is very negligible for single item).
My suggestion is read it and filter it in your application layer. Dont query for it. However what you can also do is if you very need it you can use TenantId as hashkey and isDeleted for range key. If you do so, you always have to query when you wanna get a tenant. With the query you can set rangeKey(isDeleted) to 0 or 1. This isnt how I would do it. As I said, would just read it and filter it at my application.
Another suggestion thing could be setting a GSI on isDeleted field and writing null when it is 0. This way you can only see that attribute in your table when its only 1. GSI on such attribute is called sparse index. Later if you need to get all the tenants that are deleted (isDeleted=1) you can simply scan that entire index without conditions. When you are writing null when its 0 dynamoDB wont put it in the index at the first place.

Updating multiple records in DynamoDB

How can i update multiple records in DynamoDB in single query?
I have a csv file as an input based on the csv file I have to update multiple records(only one attribute) in the DB.
Is there any API available for this?
or This can be done using batch processing(Spring-batch)?
DynamoDB doesn't have batchUpdate API directly. It does have batch get item and batch write item API.
However, you can use batchWriteItem API to update the item.
1) Use the below BatchWriteItemSpec class to construct the request
BatchWriteItemSpec
2) Use the below TableWriteItems class to construct the item that need to be updated
TableWriteItems
3) Use the below addItemToPut method (or withItemsToPut) to add the new attribute
addItemToPut
The batchWriteItem API creates a new item if the item (i.e. partition key) is not available. If the partition key is available, it will update the existing item. Your use case falls under this category.
Code Sample:-
files - is the the table name
fileName - is the partition key
transcriptionnew - Adding the new attribute (or can update the existing attribute value as well)
DynamoDB dynamoDB = new DynamoDB(dynamoDBClient);
Item itemUpdate1 = new Item();
itemUpdate1.withKeyComponent("fileName", "file1")
.withString("transcriptionnew", "new value");
Item itemUpdate2 = new Item();
itemUpdate2.withKeyComponent("fileName", "file2")
.withString("transcriptionnew", "new value");
TableWriteItems tableWriteItems = new TableWriteItems("files").withItemsToPut(itemUpdate1, itemUpdate2);
BatchWriteItemSpec batchWriteItemSpec = new BatchWriteItemSpec().withTableWriteItems(tableWriteItems);
BatchWriteItemOutcome batchWriteItemOutCome = dynamoDB.batchWriteItem(batchWriteItemSpec);
if (batchWriteItemOutCome.getUnprocessedItems().isEmpty()) {
//All items are processed
return true;
}

Updating Cassandra Map Value through querybuilder

Cassandra support updating specific value in Collection by syntax
ALTER TABLE users ADD todo map<timestamp, text>
UPDATE users SET todo['2012-10-2 12:00'] = 'throw my precious into mount doom'
WHERE user_id = 'frodo';
http://www.datastax.com/documentation/cql/3.0/cql/cql_using/use_map_t.html
Did not see any example of using QueryBuilder to update specific row in Map. How it can be done?
I think you have several options.
1/ Build your own query based on the CQL one.
Example: Consider that you have a table named Interactions and in your schema a column of type named 'attributes'.
String update ="UPDATE demo.Interactions SET attributes=attributes + {'i':'j'} where id='ff';
SimpleStatement statement = new SimpleStatement(update);
session.execute(statement);
2/ Use Java API.
Java API is not that documented indeed.
Let's take an example
a- Create an update object using queryBuilder
Update update = QueryBuilder.update(keyspace, tableName);
b- Then populate with 'put' or 'putAll' functions. put/putAll will add/update content
update.with(QueryBuilder.putAll(key, map));
To remove a key, set the content of a key to null, like:
for (Object item : map.keySet()) {
update.with(
QueryBuilder.put(columName, item, null)
);
}
Then execute the query.
Following methods are available for different types:
LIST:
QueryBuilder.appendAll(...)
QueryBuilder.discardAll(...)
SET:
QueryBuilder.addAll(...)
QueryBuilder.removeAll(...)
MAP:
QueryBuilder.putAll(...)
QueryBuilder.put(...)
The list is not exhautive.
Have a look in QueryBuilder class if you do not find what you are looking for.
Best regards and best luck with Cassandra.

Can we use Firebase update function for updating priority as well

I am updating some properties of my object in firebase using the update method. As part of the update call, can i also update the priority of my object?
I have a collection of objects stored in firebase. I am using the Javascript sdk to interact with my data. Whenever i update any property of an object, i set the priority of the object to Firebase.ServerValue.TIMESTAMP constant.
Now, i want to start storing the priority of each object in a property called UpdatedOnTimestampTs defined on the object. Whenever i update any property i will also update UpdatedOnTimestampTs with TIMESTAMP constant.
Now the problem is, I am unable to find a way to call the update function along with priority (no updateWithPriority like setWithPriority). So if I update the UpdatedOnTimestampTs and priority in two different operations using the TIMESTAMP constant i may (or will) end up with a different values.
How can i address this?
I know I'm way late on an answer here, but I'm just coming across the need for something similar now and updateWithPriority doesn't exist (nor a way to easily view/edit priorities in the Forge).
You can include the specially-named key, .priority in your update and it ought to achieve the desired effect.
The .priority key is documented in the Firebase REST API, but can be used in the other libraries.
So, in Javascript, the following would work if you only wanted to update the priority (which, there is setPriority for that):
var fb = new Firebase("http://a_firebase.firebaseio.com");
var ref = fb.child("/path/to/child");
var data = { ".priority": (new Date()).getTime() } //timestamp, as you mentioned
ref.update(data);
If you wanted to change the priority while you're updating other properties, which is what you had asked, just include it with the data you want to update:
var fb = new Firebase("http://a_firebase.firebaseio.com");
var ref = fb.child("/path/to/child");
var data = {
"property1": "updated_val",
"property2": "updated_val2",
".priority": (new Date()).getTime(), //timestamp, as you mentioned
}
ref.update(data);
EDIT: To be clear, this will update the priority at the location of the child reference (i.e. /path/to/child) not the priorities of the individual children that are being updated (i.e. priorities of property1 and property2). If you wanted to include a priority for the children that are being updated, and the children are leaf nodes (i.e. string, number, boolean), then you can use the specially-named .value key in conjunction with the .priority key to do so.
The following example shows how to update the priority for the root location that the update is occurring from (i.e. path/to/child) as well as for both the children that are being update (i.e. property1 and property2). Obviously, you could selectively pick which priorities to update if you didn't want to update them all.
var fb = new Firebase("http://a_firebase.firebaseio.com");
var ref = fb.child("/path/to/child");
var newPriority = (new Date()).getTime(); //timestamp, as you mentioned
var data = {
"property1": {".value": "updated_val", ".priority": newPriority},
"property2": {".value": "updated_val2", ".priority": newPriority},
".priority": newPriority
}
ref.update(data);

Resources