DynamoDB Java AWS SDK - UpdateItem simplification possible? - amazon-web-services

I am new to using AWS and am writing something that will add an attribute to the provided item given some keyAttributes. I got it working, but the code I ended up writing just looks unintuitive and silly to me. (I just based it off of documentation I've been finding online.)
I was able to do a successful basic update of an item in a dynamoDB table with this code:
final AttributeValue fulfilled = new AttributeValue().withBOOL(true);
final UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName(tableName)
.withKey(keyAttributes)
.withUpdateExpression("SET fulfilled = :fulfilled")
.withExpressionAttributeValues(ImmutableMap.of(":fulfilled", fulfilled));
final UpdateItemResult result = dynamoClient.updateItem(updateItemRequest);
Is there anything I can do to cut it down to just the barebones thing I'm trying to do here: just adding a new attribute "fulfilled" to an item, set to true?

An UpdateItemRequest must always contain the table name and the key attributes. (Otherwise how would DynamoDB know which item to update?)
You can simplify it, though, by getting rid of the ExpressionAttributeValues, like this:
final UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName(tableName)
.withKey(keyAttributes)
.withUpdateExpression("SET fulfilled = TRUE");
final UpdateItemResult result = dynamoClient.updateItem(updateItemRequest);

Related

APEX: Assigning a Map without a Key to a List

Sorry for the newbie question. Learning Apex here. Been working on an issue for several hours and can't seem to needle it out.
I have a JSON that I need converted to a List... the JSON is retrieved via an API. The code is pretty simple, it is only a couple of lines.
The JSON looks like this:
{"id":1,"abbreviation":"ATL","city":"Atlanta","conference":"East","division":"Southeast","full_name":"Atlanta Hawks","name":"Hawks"}
And the code I am told to use to retrieve it looks like this:
Map<String, Object> resultsMap = (Map<String, Object>) JSON.deserializeUntyped(results.getBody());
Based on the JSON provided, there does not appear to be a MAP key being assigned, so I have no idea how to get it so that I may assign it to a List...
I've already tried assigning it directly to a List, but I didn't get much success there either.
I've tried this already:
List<Object> other = (List<Object>) results.get('');
I've also tried this:
List<Object> other = (List<Object>) results.keySet()[0];
I'm sure it is something simple. Any help would be appreciated.
Thanks
You cant convert the map to a list without an id. Alternatively why to convert it to List. Use the map itself. Below is a code example of how you can use it using your JSON response. For example, I want to insert this JSON response into the contact record, so I can map it using the MAP itself.:
#RestResource(urlMapping='/URIId/*')
global class OwneriCRM {
#HttpPut
global static String UpdateOwnerinfo(){
RestRequest req= RestContext.request;
RestResponse res= RestContext.response;
res.addHeader('Content-type','application/JSON');
Map<String,Object> responseresult=new Map<String,Object>();
Map<String,Object> results= (Map<String,Object>)JSON.deserializeUntyped(req.requestBody.toString());
List<contact> insertList = new List<contact>();
for (String key : results.keySet()){
System.debug(results.get(key));
contact c= new contact();
c.ContactField__C = results.get(id);
c.ContactField1__C = results.get(city);
c.ContactField2__C = results.get(conference);
c.ContactField3__C = results.get(division);
c.ContactField3__C = results.get(full_name);
c.ContactField3__C = results.get(name);
insertList.add(c);
}
if(!insertList.Isempty()){
update insertList;
}
Question can be disregarded. Not sure if there is a way to withdraw the question.
Question was based on the fact that previous APIs had been sent in the following format:
{"data": {"more stuff": "stuff"} }
And the map key was 'data' with a list. In this scenario, the whole API was a list and the keys to the map were set in place with the actual values instead.

Update specific attribute using UpdateItemEnhancedRequest DynamoDb java sdk2

We have a DynamoDB table which has an attribute counter, which will be decremented asynchronously by multiple lambda based on an event. I am trying to update the counter using UpdateItemEnhancedRequest (using the Dynamodb Enhanced Client. - JAVA SDK 2). I am able to build the condition for updating the counter but it updates the entire item and not just the counter. Can somebody please guide on how to update a single attribute using DynamoDb Enhanced Client?
Code Sample
public void update(String counter, T item) {
AttributeValue value = AttributeValue.builder().n(counter).build();
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":value", value);
Expression myExpression = Expression.builder()
.expression("nqctr = :value")
.expressionValues(expressionValues)
.build();
UpdateItemEnhancedRequest<T> updateItemEnhancedRequest =
UpdateItemEnhancedRequest.builder(collectionClassName)
.item(item)
.conditionExpression(myExpression)
.build();
getTable().updateItem(updateItemEnhancedRequest);
}
When you update a specific column, you need to specify which column to update. Assume we have this table:
Now assume we want to update the archive column. You need to specify the column in your code. Here we change the archive column of the item that corresponds to the key to Closed (a single column update). Notice we specify the column name by using the HashMap object named updatedValues.
// Archives an item based on the key
public String archiveItem(String id){
DynamoDbClient ddb = getClient();
HashMap<String,AttributeValue> itemKey = new HashMap<String,AttributeValue>();
itemKey.put("id", AttributeValue.builder()
.s(id)
.build());
HashMap<String, AttributeValueUpdate> updatedValues =
new HashMap<String,AttributeValueUpdate>();
// Update the column specified by name with updatedVal
updatedValues.put("archive", AttributeValueUpdate.builder()
.value(AttributeValue.builder()
.s("Closed").build())
.action(AttributeAction.PUT)
.build());
UpdateItemRequest request = UpdateItemRequest.builder()
.tableName("Work")
.key(itemKey)
.attributeUpdates(updatedValues)
.build();
try {
ddb.updateItem(request);
return"The item was successfully archived";
NOTE: This is not the Enhanced Client.
This code is from the AWS Tutorial that show how to build a Java web app by using Spring Boot. Full tutorial here:
Creating the DynamoDB web application item tracker
TO update a single column using the Enhanced Client, call the Table method. This returns a DynamoDbTable instance. Now you can call the updateItem method.
Here is the logic to update the the archive column using the Enhanced Client. Notice you get a Work object, call its setArchive then pass the Work object. workTable.updateItem(r->r.item(work));
Java code:
// Update the archive column by using the Enhanced Client.
public String archiveItemEC(String id) {
DynamoDbClient ddb = getClient();
try {
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(getClient())
.build();
DynamoDbTable<Work> workTable = enhancedClient.table("Work", TableSchema.fromBean(Work.class));
//Get the Key object.
Key key = Key.builder()
.partitionValue(id)
.build();
// Get the item by using the key.
Work work = workTable.getItem(r->r.key(key));
work.setArchive("Closed");
workTable.updateItem(r->r.item(work));
return"The item was successfully archived";
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
return "";
}
This answer shows both ways to update a single column in a DynamoDB table. The above tutorial now shows this way.
In your original solution, you're misinterpreting the meaning of the conditionExpression attribute. This is used to validate conditions that must be true on the item that matches the key in order to perform the update, not the expression to perform the update itself.
There is a way to perform this operation with the enhanced client without needing to fetch the object before making an update. The UpdateItemEnhancedRequest class has an ignoreNulls attribute that will exclude all null attributes from the update. This is false by default, which is what causes a full overwrite of the object.
Let's assume this is the structure of your item (without all the enhanced client annotations and boilerplate, you can add those):
class T {
public String partitionKey;
public Int counter;
public String someOtherAttribute
public T(String partitionKey) {
this.partitionKey = partitionKey;
this.counter = null;
this.someOtherAttribute = null
}
}
You can issue an update of just the counter, and only if the item exists, like this:
public void update(Int counter, String partitionKey) {
T updateItem = new T(partitionKey)
updateItem.counter = counter
Expression itemExistsExpression = Expression.builder()
.expression("attribute_exists(partitionKey)")
.build();
UpdateItemEnhancedRequest<T> updateItemEnhancedRequest =
UpdateItemEnhancedRequest.builder(collectionClassName)
.item(item)
.conditionExpression(itemExistsExpression)
.ignoreNulls(true)
.build();
getTable().updateItem(updateItemEnhancedRequest);
}

Retrieve item from db using ObjectMapper

I'm totally new to coding in general, so this is really my first attempt, so don't shoot me if I ask stupid questions ;) Right now I'm having trouble even understanding the the vast world of backend.
So I'm having some problems in my service, and even deciding which way is the best to go, scanning, querying... what?
So I -think- the way to go for me is scanning... I'm having trouble to retrieve an item from the database, based on the id of that item. Retrieving all items works like a charm, and I need something similar for getting one item. I'm getting confused when searching the web, and not really even understanding the difference for example scanfilter, scanexpression? That's why I haven't even come up with a good attempt... but what I need is scan the table and retrieve the item with the matching id. I tried looking at my method for retrieving all searchCases, and implement it for retrieving one it, as it should look quite the same, but no success...
EDITED method a bit: Method I need help with:
public SearchCase getSearchCase(String id){
//this is obviously for a list, but how do I do it for ONE item?
HashMap<String, AttributeValue> sc = new HashMap<String, AttributeValue>();
sc.put("scId", new AttributeValue().withS(id));
ScanRequest scanRequest = new ScanRequest()
.withTableName(searchCaseTableName)
.withFilterExpression("id = scId");
ScanResult scanResult = client.scan(scanRequest);
?????
return searchCase;
}
As a reference here is the method for retrieving all items, that does work:
public List<SearchCase> getSearchCases() {
final List<SearchCase> cases = new ArrayList<SearchCase>();
ScanRequest scanRequest = new ScanRequest()
.withTableName(searchCaseTableName);
ScanResult result = client.scan(scanRequest);
try {
for (Map<String, AttributeValue> item : result.getItems()) {
SearchCase searchCase = mapper.readValue(item.get("payload").getS(), SearchCase.class);
cases.add(searchCase);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return cases;
}
It has been forever, but thought I'd post the correct answer I fought with for a long time back in June. So this was the solution that worked for me for retrieving a single item:
public SearchCase getSearchCase(String id) throws Exception {
Table t = db.getTable(searchCaseTableName);
GetItemSpec gis = new GetItemSpec()
.withPrimaryKey("id", id);
Item item = t.getItem(gis);
SearchCase searchCase = mapper.readValue(StringEscapeUtils.unescapeJson(item.getJSON("payload").substring(1)), SearchCase.class);
return searchCase;
}
This method actually took a whole another approach then the way I originally thought I would solve this. So no Scanrequest, but using GetItemSpec and Item instead. This thus caused some funky backslashes in the JSON, so my frontend wouldn't accept before I ran it through StringEscapeUtils.unescapeJson, otherwise worked like a charm.
I'm having trouble to retrieve an item from the database, based on the id of that item
If you want to retrieve an item from DynamoDB based on some unique ID, then use load, which "Loads an object with the hash key given".

Increment Number Property in AWS DynamoDB

How do I increment a number in AWS Dynamodb?
The guide says when saving an item to simply resave it:
http://docs.aws.amazon.com/mobile/sdkforios/developerguide/dynamodb_om.html
However I am trying to use a counter where many users may be updating at the same time.
Other documentation has told me to use and UpdateItem operation but I cannot find a good example to do so.
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html
However, I cannot find a method to implement the expression. In the future I will be adding values to arrays and maps. Will this be the same? My code is in Obj C
Currently my code looks like:
AWSDynamoDBUpdateItemInput *updateItemInput = [AWSDynamoDBUpdateItemInput new];
updateItemInput.tableName = #"TableName";
updateItemInput.key = #{
UniqueItemKey:#"KeyValue"
};
updateItemInput.updateExpression = #"SET counter = counter + :val";
updateItemInput.expressionAttributeValues =#{
#":val":#1
};
It looks like you're missing the last bit of code that actually makes the update item request:
AWSDynamoDB *dynamoDB = [AWSDynamoDB defaultDynamoDB];
[[dynamoDB updateItem:updateItemInput]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
//Do something with result.
}
return nil;
}];
In DynamoDB if you want to increment the value of the any propertie/field you can use the UpdateItemRequest with action option ADD. I used in android this method would update the existing value of the field. Let me share the code snippet. You can use any actions such like add,delete,put etc.
.....
AttributeValue viewcount = new AttributeValue().withS("100");
AttributeValueUpdate attributeValueUpdate = new AttributeValueUpdate().withAction(AttributeAction.ADD).withValue(viewcount);
updateItems.put(UploadVideoData.FIELD_VIEW_COUNT, attributeValueUpdate);
UpdateItemRequest updateItemRequest = new UpdateItemRequest().withTableName(UploadVideoData.TABLE_NAME)
.withKey(primaryKey).withAttributeUpdates(updateItems);
UpdateItemResult updateItemResult = amazonDynamoDBClient.updateItem(updateItemRequest);
....
You can see the above code will add 100 count into the existing value of that field.
This code is for android but the technique would remain the same.
Thank you.

acknowledge order report amazon mws

I am using "GetReportList" api with report list type as "_GET_ORDERS_DATA" to pull order reports from amazon. But I want to pull only new orders. How can I use the "acknowledged" field to make sure that I pull only new orders(which were not previously pulled).I observed that the "acknowledged" field is true by default. Please let me know if there is a way to pull new orders only(I am trying to avoid using timestamp here)
Thanks
You need to acknowledge the order report when you picked it up, then you will only get order reports set to acknowledged false on the next call.
So you need to run this operation:
$request1 = new MarketplaceWebService_Model_UpdateReportAcknowledgementsRequest();
$request1->setMerchant(MERCHANT_ID);
$idList1 = new MarketplaceWebService_Model_IdList();
$request1->setReportIdList($idList1->withId(/* SET THE REPORT ID YOU HAVE TAKEN */));
$request1->setAcknowledged(true);
invokeUpdateReportAcknowledgements($service, $request1);
function invokeUpdateReportAcknowledgements(MarketplaceWebService_Interface $service, $request1)
{
try {
$response = $service->updateReportAcknowledgements($request1);
} catch (MarketplaceWebService_Exception $ex) {
var_dump($ex);
After you have picked up the order report, and then, you can simply request the next order report with this line:
$request->setAcknowledged(false);
Like this, only reports you haven't set to acknowledged with the first call will be shown in the list.
The first call is described in the php API, I think it's called something like SetAcknowledgmentSample, and the second call needs to be called in the getReportListSample file
I think you can set them as below which I found from sample code found in Amazon Report's API sample
$parameters = array (
'Merchant' => MERCHANT_ID,
'AvailableToDate' => new DateTime('now', new DateTimeZone('UTC')),
'AvailableFromDate' => new DateTime('-6 months', new DateTimeZone('UTC')),
'Acknowledged' => false,
);
$request = new MarketplaceWebService_Model_GetReportListRequest($parameters);
$request = new MarketplaceWebService_Model_GetReportListRequest();
$request->setMerchant(MERCHANT_ID);
$request->setAvailableToDate(new DateTime('now', new DateTimeZone('UTC')));
$request->setAvailableFromDate(new DateTime('-3 months', new DateTimeZone('UTC')));
$request->setAcknowledged(false);