Skip to content

DocumentTableSchema Implementation #3758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
@ThreadSafe
@Immutable
public final class DefaultAttributeConverterProvider implements AttributeConverterProvider {
private static DefaultAttributeConverterProvider INSTANCE = getDefaultBuilder().build();

private static final Logger log = Logger.loggerFor(DefaultAttributeConverterProvider.class);

private final ConcurrentHashMap<EnhancedType<?>, AttributeConverter<?>> converterCache =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this class, not sure why we use ConcurrentHashMap, seems unnecessary...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree since this is created only at the time of creation the ConcurrentHashMap looks unecessary.
Will not modify this Class as part of DocumentAPI since this class is used by EnhancedClient , we can raise a separate PR for this.

Expand Down Expand Up @@ -117,10 +119,9 @@ public DefaultAttributeConverterProvider() {
* Returns an attribute converter provider with all default converters set.
*/
public static DefaultAttributeConverterProvider create() {
return getDefaultBuilder().build();
return INSTANCE;
}


/**
* Equivalent to {@code builder(EnhancedType.of(Object.class))}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.internal.document.DefaultEnhancedDocument;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

/**
* Interface representing Document API for DynamoDB. Document API operations are used to carry open content i.e. data with no
Expand All @@ -39,9 +40,6 @@
@SdkPublicApi
public interface EnhancedDocument {

DefaultAttributeConverterProvider defaultProvider = DefaultAttributeConverterProvider.create();


/**
* Convenience factory method - instantiates an <code>EnhancedDocument</code> from the given JSON String.
*
Expand All @@ -54,7 +52,22 @@ static EnhancedDocument fromJson(String json) {
}
return DefaultEnhancedDocument.builder()
.json(json)
.addAttributeConverterProvider(defaultProvider)
.attributeConverterProviders(DefaultAttributeConverterProvider.create())
.build();
}

/**
* Convenience factory method - instantiates an <code>EnhancedDocument</code> from the given AttributeValueMap.
* @param attributeValueMap - Map with Attributes as String keys and AttributeValue as Value.
* @return A new instance of EnhancedDocument.
*/
static EnhancedDocument fromAttributeValueMap(Map<String, AttributeValue> attributeValueMap) {
if (attributeValueMap == null) {
return null;
}
return DefaultEnhancedDocument.builder()
.attributeValueMap(attributeValueMap)
.attributeConverterProviders(DefaultAttributeConverterProvider.create())
.build();
}

Expand All @@ -70,7 +83,7 @@ static EnhancedDocument fromMap(Map<String, Object> attributes) {
}
DefaultEnhancedDocument.DefaultBuilder defaultBuilder = DefaultEnhancedDocument.builder();
attributes.entrySet().forEach(key -> defaultBuilder.add(key.getKey(), key.getValue()));
return defaultBuilder.addAttributeConverterProvider(defaultProvider)
return defaultBuilder.addAttributeConverterProvider(DefaultAttributeConverterProvider.create())
.build();
}

Expand Down Expand Up @@ -343,6 +356,12 @@ <T extends Number> Map<String, T> getMapOfNumbers(String attributeName,
*/
String toJsonPretty();

/**
* Gets the current EnhancedDocument as a <String, AttributeValue> Map.
* @return EnhancedDocument as a Map with Keys as String attributes and Values as AttributeValue.
*/
Map<String, AttributeValue> toAttributeValueMap();

@NotThreadSafe
interface Builder {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import software.amazon.awssdk.protocols.jsoncore.JsonNode;
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;

/**
* Default implementation of {@link EnhancedDocument}. This class is used by SDK to create Enhanced Documents.
Expand All @@ -57,8 +59,6 @@
@SdkInternalApi
public class DefaultEnhancedDocument implements EnhancedDocument {

private static final DefaultAttributeConverterProvider DEFAULT_PROVIDER = DefaultAttributeConverterProvider.create();

private static final JsonItemAttributeConverter JSON_ITEM_ATTRIBUTE_CONVERTER = JsonItemAttributeConverter.create();

private final Map<String, AttributeValue> attributeValueMap;
Expand All @@ -67,7 +67,7 @@ public class DefaultEnhancedDocument implements EnhancedDocument {

private DefaultEnhancedDocument(Map<String, AttributeValue> attributeValueMap) {
this.attributeValueMap = attributeValueMap;
this.attributeConverterProviders = ChainConverterProvider.create(DEFAULT_PROVIDER);
this.attributeConverterProviders = ChainConverterProvider.create(DefaultAttributeConverterProvider.create());
}

public DefaultEnhancedDocument(DefaultBuilder builder) {
Expand All @@ -89,7 +89,8 @@ public Builder toBuilder() {

}

public Map<String, AttributeValue> getAttributeValueMap() {
@Override
public Map<String, AttributeValue> toAttributeValueMap() {
return attributeValueMap;
}

Expand Down Expand Up @@ -358,93 +359,120 @@ public Builder add(String attributeName, Object value) {

private ChainConverterProvider providerFromBuildAndAppendDefault() {
List<AttributeConverterProvider> converterProviders = new ArrayList<>(attributeConverterProviders);
converterProviders.add(DEFAULT_PROVIDER);
converterProviders.add(DefaultAttributeConverterProvider.create());
ChainConverterProvider attributeConverterProvider = ChainConverterProvider.create(converterProviders);
return attributeConverterProvider;
}

@Override
public Builder addString(String attributeName, String value) {
attributeValueMap.put(attributeName, AttributeValue.fromS(value));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, AttributeValue.fromS(value));
}
return this;
}

@Override
public Builder addNumber(String attributeName, Number value) {
attributeValueMap.put(attributeName, AttributeValue.fromN(value != null ? String.valueOf(value) : null));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, AttributeValue.fromN(String.valueOf(value)));
}
return this;
}

@Override
public Builder addSdkBytes(String attributeName, SdkBytes value) {
attributeValueMap.put(attributeName, AttributeValue.fromB(value));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, AttributeValue.fromB(value));
}
return this;
}

@Override
public Builder addBoolean(String attributeName, boolean value) {
attributeValueMap.put(attributeName, AttributeValue.fromBool(value));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, AttributeValue.fromBool(value));
}
return this;
}

@Override
public Builder addNull(String attributeName) {
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
attributeValueMap.put(attributeName, NULL_ATTRIBUTE_VALUE);
return this;
}

@Override
public Builder addStringSet(String attributeName, Set<String> values) {
attributeValueMap.put(attributeName, AttributeValue.fromSs(values.stream().collect(Collectors.toList())));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, values)) {
attributeValueMap.put(attributeName, AttributeValue.fromSs(values.stream().collect(Collectors.toList())));
}
return this;
}

@Override
public Builder addNumberSet(String attributeName, Set<Number> values) {
List<String> collect = values.stream().map(value -> value.toString()).collect(Collectors.toList());
attributeValueMap.put(attributeName, AttributeValue.fromNs(collect));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, values)) {
List<String> collect = values.stream().map(value -> value.toString()).collect(Collectors.toList());
attributeValueMap.put(attributeName, AttributeValue.fromNs(collect));

}
return this;
}

@Override
public Builder addSdkBytesSet(String attributeName, Set<SdkBytes> values) {
attributeValueMap.put(attributeName, AttributeValue.fromBs(values.stream().collect(Collectors.toList())));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, values)) {
attributeValueMap.put(attributeName, AttributeValue.fromBs(values.stream().collect(Collectors.toList())));
}
return this;
}

@Override
public Builder addList(String attributeName, List<?> value) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
}
return this;
}

@Override
public Builder addMap(String attributeName, Map<String, ?> value) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, value)) {
attributeValueMap.put(attributeName, convert(value, providerFromBuildAndAppendDefault()));
}
return this;
}

@Override
public Builder addJson(String attributeName, String json) {
JsonItemAttributeConverter jsonItemAttributeConverter = JsonItemAttributeConverter.create();
JsonNodeParser build = JsonNodeParser.builder().build();
JsonNode jsonNode = build.parse(json);
AttributeValue attributeValue = jsonItemAttributeConverter.transformFrom(jsonNode);
attributeValueMap.put(attributeName, attributeValue);
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, json)) {
JsonItemAttributeConverter jsonItemAttributeConverter = JsonItemAttributeConverter.create();
JsonNodeParser build = JsonNodeParser.builder().build();
JsonNode jsonNode = build.parse(json);
AttributeValue attributeValue = jsonItemAttributeConverter.transformFrom(jsonNode);
attributeValueMap.put(attributeName, attributeValue);
}
return this;
}

@Override
public Builder addEnhancedDocument(String attributeName, EnhancedDocument enhancedDocument) {
if (enhancedDocument == null) {
attributeValueMap.put(attributeName, NULL_ATTRIBUTE_VALUE);
return this;
Validate.isTrue(!StringUtils.isEmpty(attributeName), "attributeName cannot empty or null");
if (!isNullValueAdded(attributeName, enhancedDocument)) {
attributeValueMap.put(attributeName, AttributeValue.fromM(enhancedDocument.toAttributeValueMap()));
}
DefaultEnhancedDocument defaultEnhancedDocument =
enhancedDocument instanceof DefaultEnhancedDocument
? (DefaultEnhancedDocument) enhancedDocument
: (DefaultEnhancedDocument) enhancedDocument.toBuilder().json(enhancedDocument.toJson()).build();
attributeValueMap.put(attributeName, AttributeValue.fromM(defaultEnhancedDocument.attributeValueMap));
return this;
}

Expand Down Expand Up @@ -492,6 +520,14 @@ public DefaultBuilder attributeValueMap(Map<String, AttributeValue> attributeVal
this.attributeValueMap = attributeValueMap != null ? new LinkedHashMap<>(attributeValueMap) : null;
return this;
}

private boolean isNullValueAdded(String attributeName, Object value) {
if (value == null) {
addNull(attributeName);
return true;
}
return false;
}
}

@Override
Expand Down
Loading