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);
}