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.
Related
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")
I have an option like so
#CommandLine.Option(names = "-D", description = "Define a symbol.")
/* A list of defines provided by the user. */
Map<String, String> defines = new LinkedHashMap<String, String>();
This does work when I do the following:
-Dkey=value
however when I do this
-Dkey
it does not work. Is there a way to add in a default value for keys which do not have a value associated with them?
Update: from picocli 4.6, this can be accomplished by specifying a mapFallbackValue in the option or positional parameter.
#Option(names = {"-P", "--properties"}, mapFallbackValue = Option.NULL_VALUE)
Map<String, Optional<Integer>> properties;
#Parameters(mapFallbackValue= "INFO", description= "... ${MAP-FALLBACK-VALUE} ...")
Map<Class<?>, LogLevel> logLevels;
The value type may be wrapped in a java.util.Optional. (If it isn't, and the fallback value is Option.NULL_VALUE, picocli will put the value null in the map for the specified key.)
(Original answer follows below):
This can be accomplished with a custom parameterConsumer. For example:
/* A list of defines provided by the user. */
#Option(names = "-D", parameterConsumer = MyMapParameterConsumer.class,
description = "Define a symbol.")
Map<String, String> defines = new LinkedHashMap<String, String>();
... where MyMapParameterConsumer can look something like this:
class MyMapParameterConsumer implements IParameterConsumer {
#Override
public void consumeParameters(
Stack<String> args,
ArgSpec argSpec,
CommandSpec commandSpec) {
if (args.isEmpty()) {
throw new ParameterException(commandSpec.commandLine(),
"Missing required parameter");
}
String parameter = args.pop();
String[] keyValue = parameter.split("=", 1);
String key = keyValue[0];
String value = keyValue.length > 1
? keyValue[1]
: "MY_DEFAULT";
Map<String, String> map = argSpec.getValue();
map.put(key, value);
}
}
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 have a JSON document stored in an attribute called doc that looks something like this:
{
doc:
{
"foo":
{
"bar": "baz"
}
}
}
I'd like to be able to do a table scan and filter/search on data.foo.bar == "baz". I'm using the Java SDK and have tried the following code but it doesn't seem to work for a sub-map of a document:
String filterExpression = "#d.#f.#b = :val";
Map<String, String> nameMap = new HashMap();
nameMap.put("#d", "doc");
nameMap.put("#f", "foo");
nameMap.put("#b", "bar");
Map valueMap = new HashMap();
valueMap.put(":val", "baz");
ItemCollection<ScanOutcome> items = table.scan(
new ScanSpec()
.withFilterExpression(filterExpression)
.withNameMap(nameMap)
.withValueMap(valueMap));
EDIT - I have found that this works:
String filterExpression = "#d.foo.bar = :val";
Where I only have a single ExpressionAttributeNames for the first attribute it works. Any thoughts why it doesn't work with 3 ExpressionAttributeNames? What if by some chance I needed 3, i.e. they were reserved words?
Any help or suggestions greatly appreciated. Thanks.