Условное обновление DynamoDB на основе частично вложенного объекта - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь частично обновить элемент на основе значения атрибута вложенного объекта, но не могу заставить его работать, потому что условие проверяет весь вложенный объект, а не только интересующий меня атрибут.

Моя схема выглядит так:

{ hash: hash_1, sort: sort_1, timestamp: 2018-11-16T00:00:00Z A: { value: 1, audit: U1 } } Мне нужно обновить timestamp и весь объект A тогда и только тогда, когда новая отметка времени больше, а новое значение A.value отличается от текущего значения A.value; но без заботы об А.аудите.

Мой текущий подход выглядит так:

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

Я пытался использовать выражения и значения атрибутов, но получил исключение, что они не могут быть объединены (я хотел бы избегать использования выражений, так как считаю, что их трудно читать).

Я также нашел этот вопрос: Обновление вложенной карты 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)));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...