MongoDB聚合-我如何将查询表达式应用到使用spring-data-mongodb的匹配阶段? &



我有一个带有动态字段的文档,我需要为给定的复杂查询条件找到匹配记录的计数

实体例子

@Document(collection = "UserAttributes")
public class UserAttributesEntity {
@Id
@Getter
private String id;
@NotNull
@Size(min = 1)
@Getter @Setter
private String userId;
@NotNull
@Getter @Setter
private Map<String, Object> attributes = new HashMap<>();
}

示例数据

{
"_id" : ObjectId("6164542362affb14f3f2fef6"),
"userId" : "89ee6942-289a-48c9-b0bb-210ea7c06a88",
"attributes" : {
"age" : 61,
"name" : "Name1"
}
},
{
"_id" : ObjectId("6164548045cc4456792d5325"),
"userId" : "538abb29-c09d-422e-97c1-df702dfb5930",
"attributes" : {
"age" : 40,
"name" : "Name2",
"location" : "IN"
}
}

期望查询表达式

"((attributes.name == 'Name1' && attributes.age > 40) OR (attributes.location  == 'IN'))

MongoDB的聚合查询如下$match,但相同的是不可通过spring MongoDB db api:

{ 
$expr: 
{ 
"$and": [{
"$gt": ["$attributes.age", 40]
}, {
"$eq": ["$attributes.name", "Name2"]
}] 
}
}

我在这里错过了什么吗?

使用:org.springframework.data:spring-data-mongodb:3.1.1

在spring-data-mongodb库中仍然不支持$expr操作符。然而,有一个工作解决方案,使用蒙古模板来解决这个问题-

aggregate .match()提供了一个接受AggregationExpression作为参数的重载方法。该方法可以使用$expr操作符创建$match聚合管道的查询,如下-

AggregationExpression用于$match操作符的示例-

Aggregation aggregationQuery = Aggregation.newAggregation(Aggregation.match(AggregationExpression.from(MongoExpression.create("'$expr': { '$gte': [ '$foo', '$bar'] }"))));
mongoTemplate.aggregate(aggregationQuery, Entity.class);

以上代码相当于query -

db.collection.aggregate([{"$match": {"$expr": {"$gte: ["$foo", "$bar"]}}}])

问题的代码应该是这样的-

Aggregation aggregationQuery = Aggregation.newAggregation(Aggregation.match(AggregationExpression.from(MongoExpression.create("'$expr': { '$and': [{ '$gt': ['$attributes.age', 40] }, { '$eq': ['$attributes.name', "Name2"] }] }"))));
mongoTemplate.aggregate(aggregationQuery, Entity.class);

您可以实现自己的AggregationOperation来处理您的不同条件。还没有试过我自己的代码,但应该是这样的:

AggregationOperation myMatch (List<Document> conditions) {
return new AggregationOperation() {
@Override
public String getOperator() {
return "$match";
}
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$match",
new Document("$expr",
new Document("$and", conditions)
)
);
}
};
}

并这样调用它(以匹配您的问题查询):

void callMyMatch() {
myMatch(List.of(
new Document("$gt", List.of("$attributes.age", 40)),
new Document("$eq", List.of("$attributes.name", "Name2"))
));
}

项目阶段允许我们使用查询表达式,我将我的方法转换为以下方式来实现结果:

private Mono<Long> aggregate() {
final Aggregation aggregation = Aggregation
.newAggregation(
Aggregation.project("userAttributes.playerLevel", "userAttributes.name")
.andExpression("((userAttributes.name == 'Name1' && userAttributes.age > 40) OR (userAttributes.location  == 'IN'))")
.as("result"),
Aggregation.match(Criteria.where("result").is(true)),
Aggregation.group().count().as("count"));
return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(UserAttributesEntity.class), Map.class)
.map(result -> Long.valueOf(result.get("count").toString()))
.next()
}

相关内容

  • 没有找到相关文章

最新更新