我试图根据嵌套对象的属性值来部分更新项目,但是由于条件检查的是整个嵌套对象,而不仅仅是我感兴趣的属性,所以我无法使其工作。
我的模式如下:
{
hash: hash_1,
sort: sort_1,
timestamp: 2018-11-16T00:00:00Z
A: {
value: 1,
audit: U1
}
}
当且仅当新时间戳更大且新A.value与当前A.value不同时,我才需要更新timestamp
和整个A
对象。但不关心A.audit。
我当前的方法如下:
private void updateData(final String dataName, final String value, final String audit, final Date timestamp) {
UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName(TABLE_NAME)
.addKeyEntry(HASH_KEY, new AttributeValue().withS(HASH_KEY_1))
.addKeyEntry(SORT_KEY, new AttributeValue().withS(SORT_KEY_1))
.addAttributeUpdatesEntry(dataName, new AttributeValueUpdate()
.withValue(new AttributeValue()
.addMEntry(VALUE, new AttributeValue().withN(value))
.addMEntry(AUDIT, new AttributeValue().withS(audit)))
.withAction(AttributeAction.PUT))
.addAttributeUpdatesEntry(TIMESTAMP, new AttributeValueUpdate()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withAction(AttributeAction.PUT))
.addExpectedEntry(dataName, new ExpectedAttributeValue()
.withValue(new AttributeValue().addMEntry(VALUE, new AttributeValue().withN(value)))
.withComparisonOperator(ComparisonOperator.NE))
.addExpectedEntry(TIMESTAMP, new ExpectedAttributeValue()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withComparisonOperator(ComparisonOperator.LT));
ddb.updateItem(updateItemRequest);
我曾尝试使用表达式和属性值,但遇到一个例外,即它们不能被合并(我想避免仅使用表达式,因为我认为它们难以阅读)。
我还发现了一个问题:Update nested map dynamodb,这似乎表明使用表达式是可行的(我自己尚未对其进行测试)。
那么不使用表达式就可以有这种行为吗?
我的测试用例类(测试失败为shouldNotUpdateDataIfSame
):
package sandbox;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
import com.amazonaws.services.dynamodbv2.model.AttributeAction;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
import org.junit.Before;
import org.junit.Test;
import static com.amazonaws.util.DateUtils.formatISO8601Date;
import static com.amazonaws.util.DateUtils.parseISO8601Date;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
public class Sandbox {
private static final String TABLE_NAME = "Tabla";
private static final String HASH_KEY = "hash";
private static final String SORT_KEY = "sort";
private static final String TIMESTAMP = "timestamp";
private static final String VALUE = "value";
private static final String AUDIT = "audit";
private static final String HASH_KEY_1 = "hash_1";
private static final String SORT_KEY_1 = "sort_1";
private static final String DATA_A = "A";
private static final String DATA_B = "B";
private static final String VALUE_1 = "1";
private static final String VALUE_2 = "2";
private static final String USER_1 = "U1";
private static final String USER_2 = "U2";
private static AmazonDynamoDB create() {
AmazonDynamoDB dynamoDB = DynamoDBEmbedded.create().amazonDynamoDB();
CreateTableRequest createTableRequest = new CreateTableRequest()
.withTableName(TABLE_NAME)
.withKeySchema(
new KeySchemaElement(HASH_KEY, KeyType.HASH),
new KeySchemaElement(SORT_KEY, KeyType.RANGE))
.withAttributeDefinitions(
new AttributeDefinition(HASH_KEY, ScalarAttributeType.S),
new AttributeDefinition(SORT_KEY, ScalarAttributeType.S))
.withProvisionedThroughput(new ProvisionedThroughput(5L, 5L));
dynamoDB.createTable(createTableRequest);
return dynamoDB;
}
private void save(final String dataName, final String value, final String audit, final Date timestamp) {
try {
insertNewRecord(dataName, value, audit, timestamp);
} catch (ConditionalCheckFailedException e) {
try {
insertNewData(dataName, value, audit, timestamp);
} catch (ConditionalCheckFailedException e2) {
try {
updateData(dataName, value, audit, timestamp);
} catch (ConditionalCheckFailedException e3) {
// Ignore: Obsolete data or same as before
}
}
}
}
private void insertNewRecord(final String dataName, final String value, final String audit, final Date timestamp) {
PutItemRequest putItemRequest = new PutItemRequest()
.withTableName(TABLE_NAME)
.addItemEntry(HASH_KEY, new AttributeValue().withS(HASH_KEY_1))
.addItemEntry(SORT_KEY, new AttributeValue().withS(SORT_KEY_1))
.addItemEntry(dataName, new AttributeValue()
.addMEntry(VALUE, new AttributeValue().withN(value))
.addMEntry(AUDIT, new AttributeValue().withS(audit)))
.addItemEntry(TIMESTAMP, new AttributeValue()
.withS(formatISO8601Date(timestamp)))
.addExpectedEntry(TIMESTAMP, new ExpectedAttributeValue().withExists(false));
ddb.putItem(putItemRequest);
}
private void insertNewData(final String dataName, final String value, final String audit, final Date timestamp) {
UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName(TABLE_NAME)
.addKeyEntry(HASH_KEY, new AttributeValue().withS(HASH_KEY_1))
.addKeyEntry(SORT_KEY, new AttributeValue().withS(SORT_KEY_1))
.addAttributeUpdatesEntry(dataName, new AttributeValueUpdate()
.withValue(new AttributeValue()
.addMEntry(VALUE, new AttributeValue().withN(value))
.addMEntry(AUDIT, new AttributeValue().withS(audit)))
.withAction(AttributeAction.PUT))
.addAttributeUpdatesEntry(TIMESTAMP, new AttributeValueUpdate()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withAction(AttributeAction.PUT))
.addExpectedEntry(dataName, new ExpectedAttributeValue().withExists(false))
.addExpectedEntry(TIMESTAMP, new ExpectedAttributeValue()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withComparisonOperator(ComparisonOperator.LT));
ddb.updateItem(updateItemRequest);
}
private void updateData(final String dataName, final String value, final String audit, final Date timestamp) {
UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName(TABLE_NAME)
.addKeyEntry(HASH_KEY, new AttributeValue().withS(HASH_KEY_1))
.addKeyEntry(SORT_KEY, new AttributeValue().withS(SORT_KEY_1))
.addAttributeUpdatesEntry(dataName, new AttributeValueUpdate()
.withValue(new AttributeValue()
.addMEntry(VALUE, new AttributeValue().withN(value))
.addMEntry(AUDIT, new AttributeValue().withS(audit)))
.withAction(AttributeAction.PUT))
.addAttributeUpdatesEntry(TIMESTAMP, new AttributeValueUpdate()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withAction(AttributeAction.PUT))
.addExpectedEntry(dataName, new ExpectedAttributeValue()
.withValue(new AttributeValue().addMEntry(VALUE, new AttributeValue().withN(value)))
.withComparisonOperator(ComparisonOperator.NE))
.addExpectedEntry(TIMESTAMP, new ExpectedAttributeValue()
.withValue(new AttributeValue().withS(formatISO8601Date(timestamp)))
.withComparisonOperator(ComparisonOperator.LT));
ddb.updateItem(updateItemRequest);
}
private Map<String, AttributeValue> get() {
GetItemRequest getItemRequest = new GetItemRequest()
.withTableName(TABLE_NAME)
.addKeyEntry(HASH_KEY, new AttributeValue().withS(HASH_KEY_1))
.addKeyEntry(SORT_KEY, new AttributeValue().withS(SORT_KEY_1));
return ddb.getItem(getItemRequest).getItem();
}
private AmazonDynamoDB ddb;
@Before
public void before() {
ddb = create();
}
@Test
public void shouldInsertNewRecord() {
Date now = new Date();
save(DATA_A, VALUE_1, USER_1, now);
Map<String, AttributeValue> item = get();
assertThat(parseISO8601Date(item.get(TIMESTAMP).getS()), is(equalTo(now)));
assertThat(item.get(DATA_A).getM().get(VALUE).getN(), is(equalTo(VALUE_1)));
assertThat(item.get(DATA_A).getM().get(AUDIT).getS(), is(equalTo(USER_1)));
assertThat(item.get(DATA_B), is(nullValue()));
}
@Test
public void shouldInsertNewData() {
Date yesterday = Date.from(Instant.now().minus(1L, ChronoUnit.DAYS));
save(DATA_A, VALUE_1, USER_1, yesterday);
Date now = new Date();
save(DATA_B, VALUE_2, USER_1, now);
Map<String, AttributeValue> item = get();
assertThat(parseISO8601Date(item.get(TIMESTAMP).getS()), is(equalTo(now)));
assertThat(item.get(DATA_A).getM().get(VALUE).getN(), is(equalTo(VALUE_1)));
assertThat(item.get(DATA_B).getM().get(VALUE).getN(), is(equalTo(VALUE_2)));
}
@Test
public void shouldNotUpdateIfNewerExists() {
Date now = new Date();
save(DATA_A, VALUE_1, USER_1, now);
save(DATA_B, VALUE_2, USER_1, now);
Map<String, AttributeValue> item = get();
assertThat(parseISO8601Date(item.get(TIMESTAMP).getS()), is(equalTo(now)));
assertThat(item.get(DATA_A).getM().get(VALUE).getN(), is(equalTo(VALUE_1)));
}
@Test
public void shouldUpdateDataIfDifferent() {
Date yesterday = Date.from(Instant.now().minus(1L, ChronoUnit.DAYS));
save(DATA_A, VALUE_1, USER_1, yesterday);
Date now = new Date();
save(DATA_A, VALUE_2, USER_1, now);
Map<String, AttributeValue> item = get();
assertThat(parseISO8601Date(item.get(TIMESTAMP).getS()), is(equalTo(now)));
assertThat(item.get(DATA_A).getM().get(VALUE).getN(), is(equalTo(VALUE_2)));
}
@Test
public void shouldNotUpdateDataIfSame() {
Date yesterday = Date.from(Instant.now().minus(1L, ChronoUnit.DAYS));
save(DATA_A, VALUE_1, USER_1, yesterday);
Date now = new Date();
save(DATA_A, VALUE_1, USER_2, now);
Map<String, AttributeValue> item = get();
assertThat(parseISO8601Date(item.get(TIMESTAMP).getS()), is(equalTo(yesterday)));
}
}