【问题标题】:Is it possible to use SpringData MongoDB repository to execute an arbitrary query, with pagination?是否可以使用 SpringData MongoDB 存储库通过分页执行任意查询?
【发布时间】:2013-06-19 19:21:01
【问题描述】:

我们有一个用例,用户可以为集合传入任意搜索条件,并希望输出分页。使用 Spring Data 存储库,如果我们通过简单的扩展 MongoRepository 并声明一个:

Page<Thing> findByFooAndBarAndBaz(Type foo, Type bar, Type baz, Pageable page)

但是,如果我们自己生成查询,或者使用 fluent 接口或构造一个 mongo 字符串并将其包装在 BasicQuery 类中,我无法找到将其放入存储库实例的方法。没有:

Page<Thing> findByQuery(Query q, Pageable page)

我已经看到的功能。

我也看不到如何使用 Page 抽象来挂钩 MongoTemplate 查询功能。

我希望我不必滚动自己的分页(计算跳过和限制参数,我想这并不难)并直接调用模板,但如果这是最好的选择,我想我可以。

【问题讨论】:

    标签: mongodb pagination spring-data


    【解决方案1】:

    我不认为这可以按照我希望的方式完成,但我想出了一个解决方法。作为背景,我们将所有用于访问数据的方法放在 DAO 中,一些委托给存储库,一些委托给模板。

    • 编写了一个 DAO 方法,它采用我们的任意过滤器字符串(我有一个实用程序可以将其转换为标准 mongo JSON 查询语法。
    • 将其包装在 BasicQuery 中以获得“countQuery”。
    • 使用该 countQuery 获取使用 MongoTemplate#count(Query, Class) 的记录总数
    • 使用Query#with(Pageable) 附加我的分页条件以创建“pageQuery”
    • 使用MongoTemplate#find(Query, Pageable) 运行pageQuery
    • 从中获取List<T> 结果、用于查询的Pageable 和从countQuery 运行返回的count,并构造一个新的PageImp 以返回给调用者。

    基本上,这个(DocDbDomain 是一个用于测试文档数据库内容的测试域类):

        Query countQuery = new BasicQuery(toMongoQueryString(filterString));
        Query pageQuery = countQuery.with(pageRequest);
        long total = template.count(countQuery, DocDbDomain.class);
    
        List<DocDbDomain> content = template.find(pageQuery, DocDbDomain.class);
    
        return new PageImpl<DocDbDomain>(content, pageRequest, total);
    

    【讨论】:

    • 看起来这是实现这一目标的唯一选择。 3 年后是否有其他替代方案推出?
    【解决方案2】:

    您可以使用@Query 注解通过存储库方法执行任意查询:

    interface PersonRepository extends Repository<Person, Long> {
    
      @Query("{ 'firstname' : ?0 }")
      Page<Person> findByCustomQuery(String firstname, Pageable pageable);
    }
    

    一般来说,@Query 可以包含您可以通过 shell 执行的任何 JSON 查询,但使用 ?0 类型的语法来替换参数值。你可以在reference documentation找到更多关于基本机制的信息。

    【讨论】:

    • 嗨。这仍然是唯一的选择吗?因为它不适合实际情况。例如,我有很多字段的详细搜索查询,但这些字段可能会或可能不会出现在查询中。所以将它们写入@Query不是一种选择。 Mongodb 不接受多次 null 条件,并且还为查询字段更改查询提供 null 值。
    【解决方案3】:

    如果您无法在 @Query-Annotation 中表达您的查询,您可以使用 Spring Repository PageableExecutionUtils 进行自定义查询。

    例如这样:

    @Override
    public Page<XXX> findSophisticatedXXX(/* params, ... */ @NotNull Pageable pageable) {
    
        Query query = query(
                where("...")
                // ... sophisticated query ...
        ).with(pageable);
    
        List<XXX> list = mongoOperations.find(query, XXX.class);
        return PageableExecutionUtils.getPage(list, pageable,
                  () -> mongoOperations.count((Query.of(query).limit(-1).skip(-1), XXX.class));
    }
    

    就像在原始 Spring Data Repository 中一样,PageableExecutionUtils 将执行一个单独的 count 请求并将其包装成一个漂亮的 Page 供您使用。

    Here你可以看到spring也在做同样的事情。

    【讨论】:

    • 这会将所有结果加载到内存中,不是吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 2021-07-04
    • 1970-01-01
    • 2020-12-13
    • 1970-01-01
    相关资源
    最近更新 更多