Spring 启动 MongoDB 聚合 $in 不工作
Spring Boot MongoDB Aggregation $in not working
我在反应式 Spring 启动应用程序(使用 Webflux)中使用 MongoDB,我想对我们的一个集合进行聚合,但它只是返回一个空列表。下面的代码显示了命令行中的聚合和等效查询。
运行 命令行中的聚合工作正常,在测试代码后似乎只有 .in(users)
部分不起作用。因此,如果我只使用 Criteria.where("_id").is("de28b2fa-4c9b-49fa-b29f-d8ad9d270c36")
那么将返回一个用户。
public Flux<User> findAllInIdList(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
aggs.add(match(Criteria.where("_id").in(users)));
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
db.User.aggregate([
{"$match" : {"_id" : {"$in" : ["de28b2fa-4c9b-49fa-b29f-d8ad9d270c36", "9f80bd61-be4d-4b3f-b78f-6172d88165b8", "f33e4769-a9d4-41d2-9f57-ac94afe932d7", "357397d8-ef12-42db-92cd-2cf5c31405ed", "5fc8acb6-c73e-4e8e-91b6-1347317da78b"]}}},
{"$sort" : {"record.createdOn" : 1}},
{"$skip" : 0},
{"$limit" : 50}
])
我在同一 class 中还有其他聚合可以正常工作:
public Flux<User> findAll(long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
编辑:
正如 prasad 在评论中指出的那样,数据库中的 id 是字符串类型,所以我也尝试将代码中的 UUID 映射到字符串,但它仍然 returns 没有:
public Flux<User> findAllWithCompany(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
List<String> idsAsString = users.stream().map(UUID::toString).collect(Collectors.toList());
aggs.add(match(Criteria.where("_id").in(idsAsString)));
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
编辑
添加附加信息:
用户实体:
@Value
@Jacksonized
@Document(collection = "Users")
@Builder(builderClassName = "Builder", toBuilder = true)
@CompoundIndexes({
@CompoundIndex(def = "{'id' : 1, 'record': 1}"),
})
public class User implements UserDetails {
@Serial
private static final long serialVersionUID = 1L;
@MongoId(targetType = FieldType.STRING)
UUID id;
@NonNull Person person;
Notification notification; // Not stored in DB
@NonNull Security security;
Record record;
@Override
public boolean isAccountNonExpired() {
return isEnabled();
}
@Override
public boolean isAccountNonLocked() {
return isEnabled();
}
@Override
public boolean isCredentialsNonExpired() {
return isEnabled();
}
@Override
public boolean isEnabled() {
return Objects.equals(this.security.status.getCode(), Fields.ACTIVE.getValue());
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return security.hashPassword;
}
@Override
public String getUsername() {
return this.person.email;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Person {
@NonNull String firstName;
@NonNull String lastName;
String givenName;
@Indexed(unique = true)
@NonNull String email;
Set<String> phones;
String pictureUrl;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Security {
@NonNull String hashPassword;
@NonNull StatusRef status;
Instant securityLoggedOn;
Instant securityLastLoggedOn;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Notification {
Set<UserAlerts.Alerts> alerts;
Set<String> subscribedAlertTypes;
}
}
数据库中的示例记录:
{
"_id": "6bee42cc-1a16-11ec-9621-0242ac130002",
"person": {
"firstName": "Test",
"lastName": "1",
"givenName": "Test user",
"email": "test@onepointltd.com",
"phones": [
"+91-1234566",
"+91-1234566"
],
"picture": "https://picsum.photos/nik.jpg"
},
"notification": {},
"security": {
"hashPassword": "cBrlgyL2GI2GINuLUUwgojITuIufFycpLG4490dhGtY=",
"securityLoggedOn": 2021-09-23T07:36:21.330+00:00,
"securityLastLoggedOn": 2021-09-23T07:36:21.330+00:00,
"status": {
"code": "active",
"changedBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee",
"changedOn": 2021-09-23T07:36:21.330+00:00
}
},
"record": {
"createdOn": 2021-09-23T07:36:21.330+00:00,
"createdBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee",
"updatedOn": 2021-09-23T07:36:21.330+00:00,
"updatedBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee"
}
}
以下代码基于@prasad_ 链接以下 post 的评论工作:Spring Mongo Aggregation Project Filter
我刚刚将此代码改编为我的 reactive/webflux 解决方案。
public Flux<User> findAllWithCompany(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<String> ids = users.stream().map(UUID::toString).collect(Collectors.toList());
Aggregation agg = newAggregation(
match(Criteria.where("_id").in(ids )),
sort(direction, Fields.CREATED_ON.getValue()),
skip(skip),
limit(limit)
);
return mongoTemplate.aggregate(agg, "Users", User.class);
}
我在反应式 Spring 启动应用程序(使用 Webflux)中使用 MongoDB,我想对我们的一个集合进行聚合,但它只是返回一个空列表。下面的代码显示了命令行中的聚合和等效查询。
运行 命令行中的聚合工作正常,在测试代码后似乎只有 .in(users)
部分不起作用。因此,如果我只使用 Criteria.where("_id").is("de28b2fa-4c9b-49fa-b29f-d8ad9d270c36")
那么将返回一个用户。
public Flux<User> findAllInIdList(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
aggs.add(match(Criteria.where("_id").in(users)));
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
db.User.aggregate([
{"$match" : {"_id" : {"$in" : ["de28b2fa-4c9b-49fa-b29f-d8ad9d270c36", "9f80bd61-be4d-4b3f-b78f-6172d88165b8", "f33e4769-a9d4-41d2-9f57-ac94afe932d7", "357397d8-ef12-42db-92cd-2cf5c31405ed", "5fc8acb6-c73e-4e8e-91b6-1347317da78b"]}}},
{"$sort" : {"record.createdOn" : 1}},
{"$skip" : 0},
{"$limit" : 50}
])
我在同一 class 中还有其他聚合可以正常工作:
public Flux<User> findAll(long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
编辑:
正如 prasad 在评论中指出的那样,数据库中的 id 是字符串类型,所以我也尝试将代码中的 UUID 映射到字符串,但它仍然 returns 没有:
public Flux<User> findAllWithCompany(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<AggregationOperation> aggs = new ArrayList<>();
List<String> idsAsString = users.stream().map(UUID::toString).collect(Collectors.toList());
aggs.add(match(Criteria.where("_id").in(idsAsString)));
aggs.add(sort(direction, Fields.CREATED_ON.getValue()));
aggs.add(Aggregation.skip(skip));
aggs.add(Aggregation.limit(limit));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class, aggs);
return mongoTemplate.aggregate(aggregation, User.class);
}
编辑
添加附加信息:
用户实体:
@Value
@Jacksonized
@Document(collection = "Users")
@Builder(builderClassName = "Builder", toBuilder = true)
@CompoundIndexes({
@CompoundIndex(def = "{'id' : 1, 'record': 1}"),
})
public class User implements UserDetails {
@Serial
private static final long serialVersionUID = 1L;
@MongoId(targetType = FieldType.STRING)
UUID id;
@NonNull Person person;
Notification notification; // Not stored in DB
@NonNull Security security;
Record record;
@Override
public boolean isAccountNonExpired() {
return isEnabled();
}
@Override
public boolean isAccountNonLocked() {
return isEnabled();
}
@Override
public boolean isCredentialsNonExpired() {
return isEnabled();
}
@Override
public boolean isEnabled() {
return Objects.equals(this.security.status.getCode(), Fields.ACTIVE.getValue());
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return security.hashPassword;
}
@Override
public String getUsername() {
return this.person.email;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Person {
@NonNull String firstName;
@NonNull String lastName;
String givenName;
@Indexed(unique = true)
@NonNull String email;
Set<String> phones;
String pictureUrl;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Security {
@NonNull String hashPassword;
@NonNull StatusRef status;
Instant securityLoggedOn;
Instant securityLastLoggedOn;
}
@Value
@Document
@Jacksonized
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public static class Notification {
Set<UserAlerts.Alerts> alerts;
Set<String> subscribedAlertTypes;
}
}
数据库中的示例记录:
{
"_id": "6bee42cc-1a16-11ec-9621-0242ac130002",
"person": {
"firstName": "Test",
"lastName": "1",
"givenName": "Test user",
"email": "test@onepointltd.com",
"phones": [
"+91-1234566",
"+91-1234566"
],
"picture": "https://picsum.photos/nik.jpg"
},
"notification": {},
"security": {
"hashPassword": "cBrlgyL2GI2GINuLUUwgojITuIufFycpLG4490dhGtY=",
"securityLoggedOn": 2021-09-23T07:36:21.330+00:00,
"securityLastLoggedOn": 2021-09-23T07:36:21.330+00:00,
"status": {
"code": "active",
"changedBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee",
"changedOn": 2021-09-23T07:36:21.330+00:00
}
},
"record": {
"createdOn": 2021-09-23T07:36:21.330+00:00,
"createdBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee",
"updatedOn": 2021-09-23T07:36:21.330+00:00,
"updatedBy": "416bad39-02c5-45a5-802f-4a33cd24a2ee"
}
}
以下代码基于@prasad_ 链接以下 post 的评论工作:Spring Mongo Aggregation Project Filter
我刚刚将此代码改编为我的 reactive/webflux 解决方案。
public Flux<User> findAllWithCompany(List<UUID> users, long skip, long limit, Sort.Direction direction) {
List<String> ids = users.stream().map(UUID::toString).collect(Collectors.toList());
Aggregation agg = newAggregation(
match(Criteria.where("_id").in(ids )),
sort(direction, Fields.CREATED_ON.getValue()),
skip(skip),
limit(limit)
);
return mongoTemplate.aggregate(agg, "Users", User.class);
}