1

我正在尝试从“消息”表中获取所有记录,但问题是“角色”在 dynamo DB 中保存为数组,我需要获取每个记录具有“INSTRUCTOR”角色的所有记录。

dynamoDB 中可用的记录如下。

记录 1

{ "product": "Maths", "messageSummary": "test message", "roles": [ "ADMIN", "INSTRUCTOR" ], "title": "My course1", "createdBy": 0, "authorName" : "测试作者", "id": "1" }

记录 2

{ "product": "Maths", "messageSummary": "test message", "roles": [ "STUDENT" ], "title": "My course2", "createdBy": 0, "authorName": "test author ", "id": "2" }

记录 3

{ "product": "Maths", "messageSummary": "test message", "roles": [ "INSTRUCTOR", "STUDENT" ], "title": "My course3", "createdBy": 0, "authorName" : "测试作者", "id": "3" }

消息模型类如下,引用“消息”表

@DynamoDBTable(tableName = "Messages")
public class Message {

    @Id
    @ApiModelProperty(accessMode = ApiModelProperty.AccessMode.READ_ONLY, position = 1)
    private String id;

    /* authorName, messageSummary, title .. attributes goes here */

    @ApiModelProperty(required = true, allowableValues = "maths", position = 6)
    private Product product;

    @ApiModelProperty(allowableValues = "student, instructor, admin", position = 7)
    private List<Role> roles;


    // getters and setters
}

消息存储库如下扩展 CrudRepository

@EnableScan
public interface MessageRepository extends CrudRepository<Message, String> {

    List<Message> findMessageByProductAndRoles(Product product, List roles); // Need to filter by given role
}

由于我需要获取所有具有 INSTRUCTOR 角色的记录,因此记录 1 和记录 3应该在结果列表中。但是,我只能使用产品值过滤它,但不能使用角色过滤。

此外,我尝试使用一些关键字,例如包含、喜欢、在等,但这些对我来说都没有成功。根据我的观察,这些关键字不支持从数组中过滤特定值。

并得到这个错误

{
    "timestamp": "2022-01-27T08:42:54.786+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Request processing failed; nested exception is com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException: One or more parameter values were invalid: ComparisonOperator CONTAINS is not valid for SS AttributeValue type (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: xxxxxxx)",
    "path": "/api/xxx/my route"
}

如何使用 CrudRepository 实现这一目标并实现这样的条件?

4

2 回答 2

0

就像您指出的那样,您不能在过滤器表达式中使用这些运算符( CONTAINS, LIKE, )。IN

一个或多个参数值无效:ComparisonOperator CONTAINS 对 SS AttributeValue 类型无效

FilterExpression 标准遵循 KeyConditionExpression 的标准,并添加了不等于运算符。

您当前的表结构似乎与此访问模式不兼容。DynamoDB 仅在基表围绕访问模式设计时作为解决方案,在必要时通过附加二级索引扩展基表的功能。

一种选择是简单地通过分区键(和排序键,如果适用)进行查询,并让您的应用程序进行过滤。DynamoDB 无论如何都会FilterExpressions初始查询(或扫描)完成后应用,因此您需要为已读取的所有数据付费。过滤器只返回少于您的应用程序的内容。除了将应用程序的负担卸载到 DyanamoDB 之外,没有任何真正的性能或成本优势。

另一种选择是利用二级索引,而不是调整您的关键结构。更具体地说,稀疏索引(在您的情况下为多个,每个角色一个),但前提是您可以灵活地将该String Set属性分解为几个“关键友好”值。您不能Sets用作键。请记住,二级索引的实用性完全取决于访问模式的性质。

在考虑为基表创建索引时,您应该始终问自己:

此查询是否频繁发生以证明二级索引的成本是合理的?

如果答案是肯定的,那么您还应该问自己:

我是否向新索引投射了足够多的正确属性,以便我可以避免更新未使用的投射和获取缺失投射的费用?

在这些问题之后,还有很多需要考虑的内容,包括二级指数基础知识最佳实践

于 2022-01-27T22:52:25.480 回答
0

在尝试了许多查询数据库的选项后,我能够通过在 DynamoDB 中实现 GSI 来获取相关记录列表。由于我使用的是 java,所以我使用 QuerySpec 来获取它们。

QuerySpec spec = new QuerySpec()
                .withKeyConditionExpression("lifeCycle = :life_cycle AND startDate < :s_date")
                .withFilterExpression("contains (#user_roles, :roles))
                .withNameMap(expressionAttributeNames)
                .withValueMap(new ValueMap()
                        .withString(":life_cycle", lifeCycle)
                        .withString(":roles", userRole)
                        .with(":s_date", startDate)
                );

        ItemCollection<QueryOutcome> items = index.query(spec);
于 2022-03-02T06:27:05.433 回答