I'm trying to keep items in my table unique based on a combination of two different columns.
I have an instanceId and imageId column (along with others) and based on a couple of posts on Stackoverflow and AWS Forums the below should work?
public void saveUnique(Server server) {
DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
Map<String, ExpectedAttributeValue> expectedAttributes =
ImmutableMap.<String, ExpectedAttributeValue>builder()
.put("instanceId", new ExpectedAttributeValue(false))
.put("imageId", new ExpectedAttributeValue(false))
.build();
saveExpression.setExpected(expectedAttributes);
saveExpression.setConditionalOperator(ConditionalOperator.AND);
try {
mapper.save(server, saveExpression);
} catch (ConditionalCheckFailedException e) {
//Handle conditional check
}
}
However every time I try and save a duplicate item (same instanceId and imageId) it's successfully being saved into the database.
Am I missing anything else here?
EDIT R.E notionquest answer
Updating to the answer below.
I have a job that runs once a minute polling an API. The response from the API is represented as a Server
POJO. The Server
has an attribute named instanceId
.
I want to make sure that if a Server
with that instanceId
is already in the database, don't save it.
The Server
object has another attribute of id
which is set as the table primary key.
public void saveUnique(Server server) {
DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
Map<String, ExpectedAttributeValue> expected = new HashMap<>();
expected.put("instanceId", new ExpectedAttributeValue(new AttributeValue(server.getInstanceId())).withComparisonOperator(ComparisonOperator.NE));
saveExpression.setExpected(expected);
try {
mapper.save(server, saveExpression);
} catch (ConditionalCheckFailedException e) {
LOGGER.info("Skipped saving as not unique...");
}
}
This code will save the Server object over and over again with the exception never being thrown.
The Server POJO
@DynamoDBTable(tableName = "Servers")
public class Server {
@Id
private String id = UUID.randomUUID().toString();
@DynamoDBTypeConvertedJson
private Task task;
@DynamoDBAttribute(attributeName = "instanceId")
private String instanceId;
public Server() {
}
@DynamoDBHashKey
public String getId() {
return id;
}
// other standard getters and setters
}
In 2019, looks like nothing has changed here since the time when the question was asked. Providing uniqueness for a non-primary-key field is still tricky in DynamoDB. Here is an article posted on Amazon recently: https://aws.amazon.com/blogs/database/simulating-amazon-dynamodb-unique-constraints-using-transactions/
Basically, instead of using an additional table as described below, the author proposes creating auxiliary records in the same table.
For example, on the below picture, for ensuring that instance_id=2c5d0cc8d900 is a unique value, you have to add a record with an artificial primary key value "instance_id#2c5d0cc8d900". If the insert succeeds, you can insert the main record itself.
+--------------------------+-------------------------+--------+----------------
| id | instance_id | attr1 | other fields...
| | | |
| (primary key) | (a non-key field, | |
| | must be unique) | |
+--------------------------+-------------------------+--------+----------------
| instance_id#2c5d0cc8d900 | | |
| a9fd702a | 2c5d0cc8d900 | qwerty | ...
While this approach may work fine, I personally still prefer to use a separate table like described below in my original answer. Because, when reading data from a table that contains such auxiliary records, you have to care about filtering them from actual ones.
If I correctly understood the question, you'd like to ensure uniqueness for a field which is not the hash key.
(I'm not sure why you do not use instanceId
as the hash key for Servers table, I guess you have a reason for that).
My answer: looks like you can't do that without using an auxiliary table.
Here is your existing Servers table:
+----------------------------------------------+
| Servers |
+----------------------------------------------+
| * id the hash key |
| * instanceId non-key field, must be unique|
| |
| * ... |
| * other fields |
| * ... |
+----------------------------------------------+
I would create an additional table with instanceId as the hash key:
+----------------------------------------------+
| Instance |
+----------------------------------------------+
| * instanceId the hash key |
+----------------------------------------------+
Having such a table, before saving a record into Servers, you have to
ensure first that instanceId value is unique by adding a record (putItem) into Instance, providing a ConditionExpression like attribute_not_exists(instanceId)
.
And only if the put operation completes without an ConditionalCheckFailedException
error, you can proceed with adding the record into Servers.
If you'd like to ensure uniqueness of records in Servers based on combination of two fields, instanceId and imageId, instead of just instanceId use concatenated values of these fields as a single field in your aux table:
+----------------------------------------------+
| Instance_Image |
+----------------------------------------------+
| * instanceId_imageId the hash key |
+----------------------------------------------+
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With