I am trying to get the data from DynamoDB based on a few filters. (e.g.: get me a record where the productNumber and colorwayNumber is 'A', 'B'.) Following is the code snippet:
AmazonDynamoDB amazonDynamoDB = DynamoDBClient.getInstance().getConnection();
Map<String, HashMap<String, String>> attrVal = new HashMap<String, HashMap<String, String>>();
Map<String,String> expressionAttributesNames = new HashMap<>();
expressionAttributesNames.put("#colorwayNumber","colorwayNumber");
expressionAttributesNames.put("#productNumber","productNumber");
Map<String,AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":colorwayNumber",new AttributeValue().withS(colorwayID));
expressionAttributeValues.put(":productNumber",new AttributeValue().withS(productNumber));
QueryRequest queryRequest = new QueryRequest()
.withTableName(ConverseConstants.PLM_PRODUCT_TABLE)
.withKeyConditionExpression("#colorwayNumber = :colorwayNumber")
.withKeyConditionExpression("#productNumber = :productNumber")
.withExpressionAttributeNames(expressionAttributesNames)
.withExpressionAttributeValues(expressionAttributeValues);
QueryResult queryResult1 = amazonDynamoDB.query(queryRequest);
List<Map<String, AttributeValue>> results1 = queryResult1.getItems();
Following is the exception:
Caused by: com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException: Value provided in ExpressionAttributeNames unused in expressions: keys: {#colorwayNumber} (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException;
Hope I provided enough information. Thanks!
The problem seems to be here:
.withKeyConditionExpression("#colorwayNumber = :colorwayNumber")
.withKeyConditionExpression("#productNumber = :productNumber")
Here you overwrite the first expression (#colorwayNumber = :colorwayNumber) with the second one (#productNumber = :productNumber). This way, colorwayNumber is actually unused in the expressions.
I have tried the below way and its working for me.
.withKeyConditionExpression("#colorwayNumber = :colorwayNumber and #productNumber = :productNumber")
or you can try below way which should work with FilterExpression and you can build it with DynamoDBScanExpression:
.withFilterExpression(("#colorwayNumber = :colorwayNumber and #productNumber = :productNumber")
Related
I'm trying to figure out how to make a conditional delete of an item. Assume a table with the attributes (account, system, secret). Let's say that I have the following code:
String mySecret = ...
final Map<String, AttributeValue> key = new HashMap<String, AttributeValue>();
key.put("account", AttributeValue.builder()
.s(account).build());
key.put("system", AttributeValue.builder()
.s(system).build());
DeleteItemRequest request = DeleteItemRequest.builder()
.tableName(DYNAMODB_TABLE_NAME)
.key(key)
.build();
I'd like the DeleteItemRequest to succeed only if the value of mySecret matches the value of the attribute secret of the row to be deleted. How do I specify this in the DeleteItemRequest?
Your request arguments should look something like this (in pseudocode):
{
"TableName": "YOUR_TABLE",
"Key": {
"PK": "PARTITION_KEY",
"SK": "SORT_KEY"
},
"ConditionExpression": "#secretKey = :secretValue",
"ExpressionAttributeNames": {"#secretKey":"secret"},
"ExpressionAttributeValues": {":secretValue": {"S":"mySecret"}}
}
I believe you're missing a ConditionExpression from the DeleteItemRequest. It should look something like this. I've not used Java to work with DDB, so there's probably a better way of doing this, but...
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#secretKey", "mySecret");
Map<String, AttributeValue> expressionAttributeValues = new HashMap<String, AttributeValue>();
expressionAttributeValues.put(":secretValue", new AttributeValue("secret"));
String conditionExpression = "#secretKey = :secretValue";
deleteItemRequest.setConditionExpression(conditionExpression);
deleteItemRequest.setExpressionAttributeNames(getExpressionAttributeNames());
deleteItemRequest.setExpressionAttributeValues(getExpressionAttributeValues());
Oh, and keep in mind that system is a reserved word in DDB.
I have a dynamodb Table and I want to do a conditional update operation on that using aws sdk java library. I have the table with hashkey named as "Id" and sortkey named as "Sk" . I have 5 other fields in the table and i have to update 2 fields based on the condition on the remaining fields. The condition is "total_record_count = record_passed + records_failed" , where total_record_count, record_passed and records_failed are the fields in the table.
Below is the code
DynamoDB dynamoDB = new DynamoDB(amazonDynamoDB);
Table table = dynamoDB.getTable("ciw_ocrResponse_upload");
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#updatedAt", "updatedAt");
expressionAttributeNames.put("#statusOfTracker","statusOfTracker");
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
Map<String, Object> eav = new HashMap<>();
eav.put(":val1", value);
eav.put(":val2", timestamp.getTime());
UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", requestId, "Sk", sortKey)
.withUpdateExpression("set #updatedAt = :val2, #statusOfTracker = :val1")
.withValueMap(eav)
.withNameMap(expressionAttributeNames)
.withConditionExpression(" total_record_count = record_passed + records_failed")
.withReturnValues(ReturnValue.UPDATED_NEW);
table.updateItem(updateItemSpec);
I am getting the error as Invalid ConditionExpression: Syntax error; token: "+", near: "record_passed + records_failed"
According to the docs this arithmetic operation is not supported.
Workaround is to create a total_record_count field which you need to calculate and add on every new item created. Also to add it to all existing items.
I have dynamodb table structure as follows:
{
"id": "1",
"skills": {
"skill1": "html",
"skill2": "css"
}
}
I have task to filter by skills value, In order to complete my task wrote java logic as follows:
AmazonDynamoDB client = dynamoDBService.getClient();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("dummy");
Map<String, String> attributeNames = new HashMap<String, String >();
attributeNames.put("#columnValue", "skills.skill1");
Map<String, AttributeValue> attributeValues = new HashMap<String, AttributeValue>();
attributeValues.put(":val1", new AttributeValue().withS("html"));
ScanSpec scanSpec = new ScanSpec().withProjectionExpression("skills.skill1")
.withFilterExpression("#columnValue = :val1 ").withNameMap(new NameMap().with("#columnValue", "skills.skill1"))
.withValueMap(new ValueMap().withString(":val1", "html"));
ItemCollection<ScanOutcome> items = table.scan(scanSpec);
Iterator<Item> iter = items.iterator();
while (iter.hasNext()) {
Item item = iter.next();
System.out.println("--------"+item.toString());
}
The mentioned code does not help me out. Any solution ?
You can use a ProjectionExpression to retrieve only specific attributes or elements, rather than an entire item. A ProjectionExpression can specify top-level or nested attributes, using document paths.
for example from AWS:
GetItemSpec spec = new GetItemSpec()
.withPrimaryKey("Id", 206)
.withProjectionExpression("Id, Title, RelatedItems[0], Reviews.FiveStar")
.withConsistentRead(true);
Item item = table.getItem(spec);
System.out.println(item.toJSONPretty());
Simple solution to this problem is:
First fetch all the records from the table.
Then iterate over the list of that object.
Extract the skills from each object.
Wrote your logic to do filtering.
Repeat the loop till the last record.
I found solution,scanSpec should be as follows:
ScanSpec scanSpec = new ScanSpec()
.withFilterExpression("#category.#uid = :categoryuid").withNameMap(new NameMap().with("#category","skills").with("#uid",queryString))
.withValueMap(new ValueMap().withString(":categoryuid", queryString));
i read the following link that explains to use placeholders by means of expression attribute name
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html#ExpressionAttributeValues
My json document is stored as follows:
{"user_json": {"profile.title":"abc"} }"
my java code is as follows
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#u1", "user_json.profile.title");
String projectionExpression = "user_id, #u1";
QuerySpec spec = new QuerySpec()
.withProjectionExpression(projectionExpression)
.withKeyConditionExpression("user_id = :v_id")
.withNameMap(expressionAttributeNames)
.withValueMap(new ValueMap()
.withString(":v_id", userId))
.withConsistentRead(true);
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> iterator = items.iterator();
String jsonPretty="";
while (iterator.hasNext()) {
jsonPretty = iterator.next().toJSON();
System.out.println(jsonPretty);
}
Problem: not able to retrieve Document path which has a dot in it.
can someone please point out the problem? thanks
Try doing like this:
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#u1_1", "user_json");
expressionAttributeNames.put("#u1_2", "profile.title");
String projectionExpression = "user_id, #u1_1.#u1_2";
I am trying to map a meterId to a list of MeterBlinks that have that Id. I'm mainly confused on how to build the list up for the HashMap.put() call. Code below:
Map<String, List<MeterBlink>> IdToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
List<MeterBlink> meterBlinkList = new ArrayList<>();
meterBlinkList.add(meterBlink);
String meterId = meterBlink.getMeterId();
idToMetersMap.put(meterId, meterBlinkList)
}
I think the issue is that I am creating a new list each time I iterate through but I am not sure how to resolve this.
Use the computeIfAbsent method added in jre 8:
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
idToMetersMap.computeIfAbsent(meterId, k -> new ArrayList<>()).add(meterBlinks);
}
Another option in java 8:
Map<String, List<MeterBlink>> idToMetersMap = createData().stream()
.collect(Collectors.groupingBy(MeterBlink::getMeterId));
I like the java8 answer, but here without java8 (without lambda expressions):
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
List<MeterBlink> meterBlinkList = idToMetersMap.get(meterId);
//if List doesn't exist create it and put in Map
if (meterBlinkList == null) {
meterBlinkList = new ArrayList<>();
idToMetersMap.put(meterId, meterBlinksList)
}
meterBlinkList.add(meterBlink);
}