基于部分嵌套对象的DynamoDB条件更新

时间:2018-11-16 17:31:28

标签: java amazon-dynamodb

我试图根据嵌套对象的属性值来部分更新项目,但是由于条件检查的是整个嵌套对象,而不仅仅是我感兴趣的属性,所以我无法使其工作。

我的模式如下:

{ 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)));
    }
}

0 个答案:

没有答案