Я зашел в тупик со ScanFilters, в основном они не поддерживают вложенные атрибуты для поиска в документах Dynamodb (вложенный объект). Это старый способ фильтрации, и FilterExpressions являются его заменой.
FilterExpressions также поддерживает вложенные атрибуты.Выражения фильтра в основном являются строками, которые имеют заполнители для имен атрибутов и значений атрибутов.Можно не использовать заполнители, но в динамодебе так много зарезервированных ключевых слов, что рекомендуется.И эти заполнители предоставляются с использованием карт, expressionAttributeNames
и expressionAttributeValues
.
FilterExpression выглядит как #category.#uid = :categoryuid
, где моя карта имен атрибутов выглядит как {"#category":"category","#uid":"uid"}
, а карта значений атрибутов выглядит как {":categoryuid":{"s":"1d5e9cea-3c4d-4a73-8e1e-aeaa868b9d89"}}
.
Это прекрасно сработало для меня, для лучшей организации кода я записал класс-обертку
public class FilterExpression {
private List<Filter> filters;
private String filterExpression;
private Map<String, AttributeValue> attributeValues;
private Map<String, String> attributeNames;
public FilterExpression(List<Filter> filters) {
this.filters = filters;
populateFilterExpression();
}
private void populateFilterExpression() {
StringBuilder filterExpressionBuilder = new StringBuilder();
attributeNames = new HashMap<>();
attributeValues = new HashMap<>();
for (Filter filter: filters) {
if (filterExpressionBuilder.length() > 0) {
filterExpressionBuilder.append(" AND ");
}
String attributeName = filter.getAttributeName();
String[] attributes = attributeName.split("\\.");
StringBuilder expNestedAttributes = new StringBuilder();
for (String attributeInPath: attributes) {
attributeNames.put("#"+attributeInPath, attributeInPath);
if(expNestedAttributes.length() > 0) {
expNestedAttributes.append(".");
}
expNestedAttributes.append("#" + attributeInPath);
}
String attributeValueKey = ":" + String.join("", attributes);
AttributeValue attributeValue;
switch (filter.getAttributeType()) {
case STRING:
attributeValue = new AttributeValue().withS(filter.getAttributeValue());
break;
case NUMBER:
attributeValue = new AttributeValue().withN(filter.getAttributeValue());
break;
default:
throw new UnsupportedOperationException("The attribute type is not supported");
}
attributeValues.put(attributeValueKey, attributeValue);
switch (filter.getOperation()) {
case EQ:
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(" = ");
filterExpressionBuilder.append(attributeValueKey);
break;
case GE:
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(" >= ");
filterExpressionBuilder.append(attributeValueKey);
break;
case LE:
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(" <= ");
filterExpressionBuilder.append(attributeValueKey);
break;
case GT:
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(" > ");
filterExpressionBuilder.append(attributeValueKey);
break;
case LT:
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(" < ");
filterExpressionBuilder.append(attributeValueKey);
break;
case STARTS_WITH:
filterExpressionBuilder.append("begins_with (");
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(", ");
filterExpressionBuilder.append(attributeValueKey);
filterExpressionBuilder.append(")");
break;
case CONTAINS:
filterExpressionBuilder.append("contains (");
filterExpressionBuilder.append(expNestedAttributes);
filterExpressionBuilder.append(", ");
filterExpressionBuilder.append(attributeValueKey);
filterExpressionBuilder.append(")");
break;
default:
throw new UnsupportedOperationException("filter is not supported");
}
}
filterExpression = filterExpressionBuilder.toString();
}
public String getFilterExpression() {
return filterExpression;
}
public Map<String, AttributeValue> getAttributeValues() {
return attributeValues;
}
public Map<String, String> getAttributeNames() {
return attributeNames;
}
@Override
public String toString() {
return filterExpression;
}
}
, а мой Filter
выглядит следующим образом:
public class Filter {
private String attributeName;
private String attributeValue;
private Operation operation;
private AttributeType attributeType;
public enum Operation {
EQ, GE, LE, GT, LT, CONTAINS, STARTS_WITH
}
public enum AttributeType {
STRING, NUMBER
}
}
фрагмент от DAO
выглядит так:
public List<ProductDO> get(List<Filter> filters, String previousPageLastKey, int count) {
FilterExpression filterExpression = new FilterExpression(filters);
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
.withLimit(count)
.withFilterExpression(filterExpression.getFilterExpression())
.withExpressionAttributeNames(filterExpression.getAttributeNames())
.withExpressionAttributeValues(filterExpression.getAttributeValues());
dynamoDBMapper.scan(ProductDO.class, scanExpression)
}
Отправка, так как я не нашел подходящего решения по этому поводу.