如何直接过滤DynamoDB Field表中config的fieldName属性?
问题描述
现有名为Field的DynamoDB表,包含pageId(哈希键)和config字段,其中config字段存储JSON对象列表。需要编写代码直接基于config字段的fieldName属性过滤表中记录,无需先获取所有记录再过滤。
现有实体类
Field实体类
@Data @DynamoDBTable(tableName = "field") public class Field{ @DynamoDBHashKey(attributeName = "pageId") private String pageId; @DynamoDBAttribute(attributeName = "config") private List<Config> config; }
Config实体类
@Data @DynamoDBDocument public class Config{ @DynamoDBAttribute(attributeName = "fieldName") private String fieldName; @DynamoDBAttribute(attributeName = "fieldType") private String fieldType; }
DynamoDB中的数据结构(JSON表示)
{ "pageId": { "S": "page_1" }, "config": { "L": [ { "M": { "fieldName": { "S": "field_name_1" }, "fieldType": { "S": "type_2" } } }, { "M": { "fieldName": { "S": "field_name-2" }, "fieldType": { "S": "type_1" } } } ] } }
技术栈版本
- Java版本:8
- Spring版本:
5.1.18.RELEASE - Spring Boot版本:
2.1.6.RELEASE - AWS DynamoDB SDK:
com.amazonaws:aws-java-sdk-dynamodb:1.11.548
解决方案
要在DynamoDB服务端直接过滤列表嵌套属性,需使用条件表达式结合contains函数,通过表达式属性名称和值定位到config列表中的fieldName字段。
方式1:使用DynamoDBMapper扫描操作
这是基于Spring整合的简化实现,适合大多数场景:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class FieldService { private final DynamoDBMapper dynamoDBMapper; // 构造注入DynamoDBMapper public FieldService(DynamoDBMapper dynamoDBMapper) { this.dynamoDBMapper = dynamoDBMapper; } public List<Field> findByConfigFieldName(String targetFieldName) { // 定义表达式属性名称,避免字段名与DynamoDB保留关键字冲突 Map<String, String> expressionAttributeNames = new HashMap<>(); expressionAttributeNames.put("#cfg", "config"); expressionAttributeNames.put("#fn", "fieldName"); // 定义表达式属性值 Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":fnVal", new AttributeValue().withS(targetFieldName)); // 构造过滤条件:检查config列表中是否存在fieldName匹配的元素 DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() .withFilterExpression("contains(#cfg.#fn, :fnVal)") .withExpressionAttributeNames(expressionAttributeNames) .withExpressionAttributeValues(expressionAttributeValues); // 执行扫描并返回映射后的实体列表 return dynamoDBMapper.scan(Field.class, scanExpression); } }
方式2:使用原生DynamoDB客户端API
如果需要更精细的控制,可直接调用低级API:
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.model.ScanRequest; import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class FieldRawService { private final AmazonDynamoDB amazonDynamoDB; private final DynamoDBMapper dynamoDBMapper; public FieldRawService(AmazonDynamoDB amazonDynamoDB, DynamoDBMapper dynamoDBMapper) { this.amazonDynamoDB = amazonDynamoDB; this.dynamoDBMapper = dynamoDBMapper; } public List<Field> findByConfigFieldName(String targetFieldName) { Map<String, String> expressionAttributeNames = new HashMap<>(); expressionAttributeNames.put("#cfg", "config"); expressionAttributeNames.put("#fn", "fieldName"); Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":fnVal", new AttributeValue().withS(targetFieldName)); ScanRequest scanRequest = new ScanRequest() .withTableName("field") .withFilterExpression("contains(#cfg.#fn, :fnVal)") .withExpressionAttributeNames(expressionAttributeNames) .withExpressionAttributeValues(expressionAttributeValues); ScanResult scanResult = amazonDynamoDB.scan(scanRequest); // 将原生返回结果映射为实体类 return dynamoDBMapper.marshallIntoObjects(Field.class, scanResult.getItems()); } }
关键注意事项
- 性能优化:扫描操作默认遍历全表,但
FilterExpression会在DynamoDB服务端过滤结果,避免无效数据传输。由于列表嵌套属性无法直接创建全局二级索引(GSI),扫描是当前场景下最可行的方案。 - 表达式语法:必须使用
expressionAttributeNames替代原始字段名,防止与DynamoDB保留关键字冲突。 contains函数特性:该函数会检查列表中是否存在任意元素的fieldName属性匹配目标值,完全满足需求。
内容的提问来源于stack exchange,提问作者JiboOne
相关产品推荐
相关产品推荐

