In a DynamoDB table where CustomerStatus is a column in which item may contain 'Active','Inactive' or 'Deleted' as CustomerStatus. I want to fetch all customers whose status is 'Active' as well 'Inactive'.
Here is the code sample I am using, but I am not sure how to get it done.
private Object data(MuleEventContext eventContext) {
List<Object> finalJson = new ArrayList<Object>();
String tableName = "Customers";
NameMap nameMap = new NameMap();
nameMap.put("#v_status", "CustomerStatus");
ValueMap valueMap = new ValueMap();
valueMap.put(":v_statusval", "Deleted");
BasicAWSCredentials cre = new BasicAWSCredentials(accesKey,secretKey);
AmazonDynamoDB dynamoDB1 = AmazonDynamoDBClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(cre)).withRegion(Regions.EU_WEST_1).build();
DynamoDB dynamoDB = new DynamoDB(dynamoDB1);
Table table = dynamoDB.getTable(tableName);
QuerySpec querySpec = new QuerySpec().withKeyConditionExpression("#v_status != :v_statusval")
.withNameMap(nameMap)
.withValueMap(valueMap);
ItemCollection<QueryOutcome> items = null;
Iterator<Item> iterator = null;
Item item = null;
try {
items = table.query(querySpec);
iterator = items.iterator();
while (iterator.hasNext()) {
item = iterator.next();
finalJson.add(item.asMap());
}
} catch (Exception e) {
logger.info(e.getMessage());
}
return finalJson;
}
You can't filter on a HASH key.
I would need to know which attribute is the HASH key and which is the RANGE key.
If the CustomerStatus is not the HASH key, the best way is to create a secondary index for this attribute and do 2 different queries:
One for the Active;
Another one for the Inactive.
This is the most efficient way to do this kind of query.
Another way is doing a scan and filtering the attribute, using the IN operand, as stated in Syntax for Condition Expressions. But this will read all data from your table, can be slow, and consume a lot capacity.
Related
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 created a table in Amazon dynamodb with primary key Issue(String) which has data stored in it.I want to read the values from my table. I'm using the following code..
#DynamoDBTable(tableName="Incident")
AmazonDynamoDBClient dynamoDBClient = new AmazonDynamoDBClient();
String tableName = "Incident";
Table table = dynamoDBClient.getTable("Incident");
Item getItem=dynamoDBClient.getItem();
I'm getting an error when calling the getTable method.... is it a predefined method just like createTable() or do we need to write our own..if so how?
And also what method should be used to read all items in the table..?
I used this link to write some of the code... http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/JavaDocumentAPIItemCRUD.html#JavaDocumentAPIGetItem
I'm new to Java please help..
Scan API can be used to get all the items from the table.
The scan should be done until LastEvaluatedKey is not null which is very important to get all the items. Otherwise, you will not get all the items if the table has many items i.e. the API will return 1 MB of data per scan.
A Scan operation performs eventually consistent reads by default, and
it can return up to 1 MB (one page) of data.
Scan API
Map<String, AttributeValue> lastKeyEvaluated = null;
do {
ScanRequest scanRequest = new ScanRequest()
.withTableName("ProductCatalog")
.withLimit(10)
.withExclusiveStartKey(lastKeyEvaluated);
ScanResult result = client.scan(scanRequest);
for (Map<String, AttributeValue> item : result.getItems()){
printItem(item);
}
lastKeyEvaluated = result.getLastEvaluatedKey();
} while (lastKeyEvaluated != null);
Here is example how to read data using Scan API :
#Override
protected ArrayList<String> doInBackground(String... params) {
String tableName = params[0];
ArrayList<String> tempList = new ArrayList<String>();
AmazonDynamoDBClient dynamoDBClient = new AmazonDynamoDBClient (
new BasicAWSCredentials(Constants.ACCESS_KEY_ID,
Constants.SECRET_KEY));
ScanRequest scanRequest = new ScanRequest()
.withTableName(tableName);
//.withAttributesToGet("name");
com.amazonaws.services.dynamodb.model.ScanResult result = dynamoDBClient.scan(scanRequest);
for (Map<String, AttributeValue> item : result.getItems()) {
tempList.add(item.toString());
//analizeItem(tempList, item);
}
return tempList;
}
Reference from programcreeks
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.AP_SOUTH_1).build();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("Student");
Item item = table.getItem("PK", "portion Key","SK","Sort Key");
System.out.println(item.toJSONPretty());
What is the best way to do complex queries using DynamoDBQueryExpression.
By default the fetch will executed an AND on all the Conditions provided.
How do i execute something like (C1 && C2 && (C3|| C4|| C5))? Is it even possible with DynamoDB java api?
DynamoDB supports the complex conditions using "com.amazonaws.services.dynamodbv2.document.spec.QuerySpec". You can write the AND and OR conditions using FilterExpression.
Please see the example below:-
1) withKeyConditionExpression - For hash key and range key expressions
2) withFilterExpression - For all other attributes (i.e. other than key attributes)
ItemCollection<QueryOutcome> items = null;
QuerySpec querySpec = new QuerySpec();
ValueMap valueMap = new ValueMap();
valueMap.withString(":autoIdVal", autoID);
valueMap.withString(":docTypeVal", docType);
valueMap.withString(":username", username);
valueMap.withString(":comment", comment);
Map<String, String> nameMap = new LinkedHashMap<>();
nameMap.put("#comment", "comment");
querySpec.withKeyConditionExpression("autoID = :autoIdVal").withFilterExpression("(docType = :docTypeVal AND username = :username) OR (#comment = :comment)")
.withValueMap(valueMap)
.withNameMap(nameMap);
items = table.query(querySpec);
Iterator<Item> iterator = items.iterator();
Item itemData = null;
while (iterator.hasNext()) {
itemData = iterator.next();
System.out.println("Json data ====================>" + itemData.toJSONPretty());
}
I have a DynamoDB attribute whose value is a map from a Number to a String. I am trying to put in a new key-value pair. From what I've read, this seems to be possible, but I don't know how.
I assume the solution is similar to the one in the link below:
How to update a Map or a List on AWS DynamoDB document API?
But I do not believe the example is on putting in a new item to a map. Could someone show me how to put in an item to a map?
Thanks.
EDIT:
I do not want to get the item, locally make the changes, and put it back. I am working with multiple clients who might interact concurrently (and I assume the update by dynamo ensures there will be no race conditions).
With the following arguments to UpdateItem, you can condition adding a map entry at #number when map.#number does not exist in the map already:
UpdateExpression = "SET map.#number = :string"
ExpressionAttributeNames = { "#number" : "1" }
ExpressionAttributeValues = { ":string" : "the string to store in the map at key value 1" }
ConditionExpression = "attribute_not_exists(map.#number)"
Visit http://www.tryyourskill.com/aws/insert-or-append-key-values-map-in-dynamodb-using-java may help you with the code snippet to add or append values in map column
public boolean insertKeyValue(String tableName, String primaryKey, String
primaryKeyValue, String updateColumn, String newKey, String newValue) {
//Configuration to connect to DynamoDB
Table table = dynamoDB.getTable(tableName);
boolean insertAppendStatus = false;
try {
//Updates when map is already exist in the table
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey(primaryKey, primaryKeyValue)
.withReturnValues(ReturnValue.ALL_NEW)
.withUpdateExpression("set #columnName." + newKey + " = :columnValue")
.withNameMap(new NameMap().with("#columnName", updateColumn))
.withValueMap(new ValueMap().with(":columnValue", newValue))
.withConditionExpression("attribute_exists("+ updateColumn +")");
table.updateItem(updateItemSpec);
insertAppendStatus = true;
//Add map column when it's not exist in the table
} catch (ConditionalCheckFailedException e) {
HashMap<String, String> map = new HashMap<>();
map.put(newKey, newValue);
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey(primaryKey,primaryKeyValue)
.withReturnValues(ReturnValue.ALL_NEW)
.withUpdateExpression("set #columnName = :m")
.withNameMap(new NameMap().with("#columnName", updateColumn))
.withValueMap(new ValueMap().withMap(":m", map));
table.updateItem(updateItemSpec);
insertAppendStatus = true;
} catch(Exception e) {
e.printStackTrace();
}
return insertAppendStatus;
}
The new AWS DynamoDB document API allows 2 new data types that correspond directly to the underlying JSON representation: Map (aka JSON object) and List (aka JSON array).
However, I can't find a way to update attributes of these data types without completely overwriting them. In contrast, a Number attribute can be updated by ADDing another number, so in Java you can do something like:
new AttributeUpdate("Some numeric attribute").addNumeric(17);
Similarly you can addElements to an attribute of a Set data type. (In the old API you would use AttributeAction.ADD for both purposes.)
But for a Map or a List, it seems you must update the previous value locally, then PUT it instead of that value, for example in Java:
List<String> list = item.getList("Some list attribute");
list.add("new element");
new AttributeUpdate("Some list attribute").put(list);
This is much less readable, and under some circumstances much less efficient.
So my questions are:
Is there a way to update an attribute of a Map or a List data type without overwriting the previous value? For example, to add an element to a List, or to put an element in a Map?
How would you implement it using the Java API?
Do you know of plans to support this in the future?
Please take a look at UpdateExpression in the UpdateItem API
For example given an item with a list:
{
"hashkey": {"S" : "my_key"},
"my_list" : {"L":
[{"N":"3"},{"N":"7"} ]
}
You can update the list with code like the following:
UpdateItemRequest request = new UpdateItemRequest();
request.setTableName("myTableName");
request.setKey(Collections.singletonMap("hashkey",
new AttributeValue().withS("my_key")));
request.setUpdateExpression("list_append(:prepend_value, my_list)");
request.setExpressionAttributeValues(
Collections.singletonMap(":prepend_value",
new AttributeValue().withN("1"))
);
dynamodb.updateItem(request);`
You can also append to the list by reversing the order of the arguments in the list_append expression.
An expression like: SET user.address.zipcode = :zip would address a JSON map element combined with expression attribute values {":zip" : {"N":"12345"}}
Base on DynamoDB examples, this also work (scala)
val updateItemSpec:UpdateItemSpec = new UpdateItemSpec()
.withPrimaryKey("hashkey", my_key)
.withUpdateExpression("set my_list = list_append(:prepend_value, my_list)")
.withValueMap(new ValueMap()
.withList(":prepend_value", "1"))
.withReturnValues(ReturnValue.UPDATED_NEW)
println("Updating the item...")
val outcome: UpdateItemOutcome = table.updateItem(updateItemSpec)
println("UpdateItem succeeded:\n" + outcome.getItem.toJSONPretty)
A generic function to add or update a key/value pairs. attribute updateColumn should be of type map.
Update tableName attribute name should be passed as attributeName under key:value pairs where primaryKey = primaryKeyValue
public boolean insertKeyValue(String tableName, String primaryKey, String
primaryKeyValue, String attributeName, String newKey, String newValue) {
//Configuration to connect to DynamoDB
Table table = dynamoDB.getTable(tableName);
boolean insertAppendStatus = false;
try {
//Updates when map is already exist in the table
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey(primaryKey, primaryKeyValue)
.withReturnValues(ReturnValue.ALL_NEW)
.withUpdateExpression("set #columnName." + newKey + " = :columnValue")
.withNameMap(new NameMap().with("#columnName", attributeName))
.withValueMap(new ValueMap().with(":columnValue", newValue))
.withConditionExpression("attribute_exists("+ attributeName +")");
table.updateItem(updateItemSpec);
insertAppendStatus = true;
//Add map column when it's not exist in the table
} catch (ConditionalCheckFailedException e) {
HashMap<String, String> map = new HashMap<>();
map.put(newKey, newValue);
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey(primaryKey,primaryKeyValue)
.withReturnValues(ReturnValue.ALL_NEW)
.withUpdateExpression("set #columnName = :m")
.withNameMap(new NameMap().with("#columnName", attributeName))
.withValueMap(new ValueMap().withMap(":m", map));
table.updateItem(updateItemSpec);
insertAppendStatus = true;
} catch(Exception e) {
e.printStackTrace();
}
return insertAppendStatus;
}