|
15 | 15 |
|
16 | 16 | package software.amazon.awssdk.enhanced.dynamodb.internal.operations;
|
17 | 17 |
|
| 18 | +import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.isNullAttributeValue; |
18 | 19 | import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.readAndTransformSingleItem;
|
19 | 20 | import static software.amazon.awssdk.enhanced.dynamodb.internal.update.UpdateExpressionUtils.operationExpression;
|
20 | 21 | import static software.amazon.awssdk.utils.CollectionUtils.filterMap;
|
21 | 22 |
|
22 | 23 | import java.util.Collection;
|
| 24 | +import java.util.HashMap; |
23 | 25 | import java.util.List;
|
24 | 26 | import java.util.Map;
|
25 | 27 | import java.util.Optional;
|
|
53 | 55 | public class UpdateItemOperation<T>
|
54 | 56 | implements TableOperation<T, UpdateItemRequest, UpdateItemResponse, UpdateItemEnhancedResponse<T>>,
|
55 | 57 | TransactableWriteOperation<T> {
|
56 |
| - |
| 58 | + |
| 59 | + public static final String NESTED_OBJECT_UPDATE = "_NESTED_ATTR_UPDATE_"; |
57 | 60 | private final Either<UpdateItemEnhancedRequest<T>, TransactUpdateItemEnhancedRequest<T>> request;
|
58 | 61 |
|
59 | 62 | private UpdateItemOperation(UpdateItemEnhancedRequest<T> request) {
|
@@ -89,8 +92,14 @@ public UpdateItemRequest generateRequest(TableSchema<T> tableSchema,
|
89 | 92 | Boolean ignoreNulls = request.map(r -> Optional.ofNullable(r.ignoreNulls()),
|
90 | 93 | r -> Optional.ofNullable(r.ignoreNulls()))
|
91 | 94 | .orElse(null);
|
92 |
| - |
93 |
| - Map<String, AttributeValue> itemMap = tableSchema.itemToMap(item, Boolean.TRUE.equals(ignoreNulls)); |
| 95 | + |
| 96 | + Map<String, AttributeValue> itemMapImmutable = tableSchema.itemToMap(item, Boolean.TRUE.equals(ignoreNulls)); |
| 97 | + |
| 98 | + // If ignoreNulls is set to true, check for nested params to be updated |
| 99 | + // If needed, Transform itemMap for it to be able to handle them. |
| 100 | + Map<String, AttributeValue> itemMap = Boolean.TRUE.equals(ignoreNulls) ? |
| 101 | + transformItemToMapForUpdateExpression(itemMapImmutable) : itemMapImmutable; |
| 102 | + |
94 | 103 | TableMetadata tableMetadata = tableSchema.tableMetadata();
|
95 | 104 |
|
96 | 105 | WriteModification transformation =
|
@@ -141,6 +150,58 @@ public UpdateItemRequest generateRequest(TableSchema<T> tableSchema,
|
141 | 150 |
|
142 | 151 | return requestBuilder.build();
|
143 | 152 | }
|
| 153 | + |
| 154 | + /** |
| 155 | + * Method checks if a nested object parameter requires an update |
| 156 | + * If so flattens out nested params separated by "_NESTED_ATTR_UPDATE_" |
| 157 | + * this is consumed by @link EnhancedClientUtils to form the appropriate UpdateExpression |
| 158 | + */ |
| 159 | + public Map<String, AttributeValue> transformItemToMapForUpdateExpression(Map<String, AttributeValue> itemToMap) { |
| 160 | + |
| 161 | + Map<String, AttributeValue> nestedAttributes = new HashMap<>(); |
| 162 | + |
| 163 | + itemToMap.forEach((key, value) -> { |
| 164 | + if (value.hasM() && isNotEmptyMap(value.m())) { |
| 165 | + nestedAttributes.put(key, value); |
| 166 | + } |
| 167 | + }); |
| 168 | + |
| 169 | + if (!nestedAttributes.isEmpty()) { |
| 170 | + Map<String, AttributeValue> itemToMapMutable = new HashMap<>(itemToMap); |
| 171 | + nestedAttributes.forEach((key, value) -> { |
| 172 | + itemToMapMutable.remove(key); |
| 173 | + nestedItemToMap(itemToMapMutable, key, value); |
| 174 | + }); |
| 175 | + return itemToMapMutable; |
| 176 | + } |
| 177 | + |
| 178 | + return itemToMap; |
| 179 | + } |
| 180 | + |
| 181 | + private Map<String, AttributeValue> nestedItemToMap(Map<String, AttributeValue> itemToMap, |
| 182 | + String key, |
| 183 | + AttributeValue attributeValue) { |
| 184 | + attributeValue.m().forEach((mapKey, mapValue) -> { |
| 185 | + String nestedAttributeKey = key + NESTED_OBJECT_UPDATE + mapKey; |
| 186 | + if (attributeValueNonNullOrShouldWriteNull(mapValue)) { |
| 187 | + if (mapValue.hasM()) { |
| 188 | + nestedItemToMap(itemToMap, nestedAttributeKey, mapValue); |
| 189 | + } else { |
| 190 | + itemToMap.put(nestedAttributeKey, mapValue); |
| 191 | + } |
| 192 | + } |
| 193 | + }); |
| 194 | + return itemToMap; |
| 195 | + } |
| 196 | + |
| 197 | + private boolean isNotEmptyMap(Map<String, AttributeValue> map) { |
| 198 | + return !map.isEmpty() && map.values().stream() |
| 199 | + .anyMatch(this::attributeValueNonNullOrShouldWriteNull); |
| 200 | + } |
| 201 | + |
| 202 | + private boolean attributeValueNonNullOrShouldWriteNull(AttributeValue attributeValue) { |
| 203 | + return !isNullAttributeValue(attributeValue); |
| 204 | + } |
144 | 205 |
|
145 | 206 | @Override
|
146 | 207 | public UpdateItemEnhancedResponse<T> transformResponse(UpdateItemResponse response,
|
|
0 commit comments