【问题标题】:Use limit and skip in MongoRepository<Customer,String>在 MongoRepository<Customer,String> 中使用限制和跳过
【发布时间】:2022-11-13 04:33:03
【问题描述】:

我们正在开发一个从 mongoDB 获取数据的项目。我们创建了如下的存储库类

@Repository
public interface CustomerRepository extends MongoRepository<Customer,String>{
     List<Customer> customers = findByCustomerId(final String customerId);
}

我们正在寻找添加跳过/偏移和限制参数以用作 findByCustomerId 方法的一部分。其中limit用于定义返回的记录数,skip/offset定义我们需要获取记录的记录数。

请帮助我们如何使用 MongoRepository 以最佳方式实现这一点。

【问题讨论】:

  • 仅当您想要慢速查询时才使用“$skip”

标签: mongodb mongodb-query spring-data-mongodb mongorepository


【解决方案1】:

有两种方法可以做到这一点。

  1. 使用@Aggregation 注释,如本答案中所述。 https://stackoverflow.com/a/71292598/8470055

    例如:

    @Repository
    public interface CustomerRepository extends MongoRepository<Customer,String>{
    
      @Aggregation(pipeline = {
        "{ '$match': { 'customerId' : ?0 } }", 
        "{ '$sort' : { 'customerId' : 1 } }", 
        "{ '$skip' : ?1 }", 
        "{ '$limit' : ?2 }"
      })
      List<Customer> findByCustomerId(final String customerId, int skip, int limit);
    
      @Aggregation(pipeline = {
        "{ '$match': { 'customerId' : ?0 } }", 
        "{ '$sort' : { 'customerId' : 1 } }", 
        "{ '$skip' : ?1 }"
      })
      Page<Customer> findCustomers(final String customerId, int skip, Pageable pageable);
    
    }
    

    $match 运算符的查询可能需要修改,以便更好地反映匹配文档需要满足的条件。

    1. 在查询方法中使用Pageable 参数,并从调用存储库方法的层提供PageRequest,如本答案所示。 https://stackoverflow.com/a/10077534/8470055

    对于问题中的代码sn-p,这就变成了。

    @Repository
    public interface CustomerRepository extends MongoRepository<Customer,String> {
    
      Page<Customer> findByCustomerId(final String customerId, Pageable pageable);
    
    }
    
    // -------------------------------------------------------
    // Call the repository method from a service
    @Service
    public class CustomerService {
    
      private final CustomerRepository customerRepository;
    
      public CustomerService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
      }
    
      public List<Customer> getCustomers(String customerId, int skip, int limit) {
        // application-specific handling of skip and limit arguments
        int page = 1; // calculated based on skip and limit values
        int size = 5; // calculated based on skip and limit values
        Page<Customer> page = customerRepository.findByCustomerId(customerId, 
                     PageRequest.of(page, size, Sort.Direction.ASC, "customerId"));
        List<Customer> customers = page.getContent();
    
        /*
        Here, the query method will retrieve 5 documents from the second 
        page.
        It skips the first 5 documents in the first page with page index 0. 
        This approach requires calculating the page to retrieve based on 
        the application's definition of limit/skip.
        */
        return Collections.unmodifiableList(customers);
      }
    }
    

    聚合方法更有用。 如果结果仅限于几个文档,则查询方法可以返回List&lt;Customer&gt;。 如果有很多文档,则可以修改查询方法以使用返回Page&lt;Customer&gt;Pageable 参数来翻阅文档。

    请参阅 Spring Data 和 MongoDB 文档。

    https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#mongo.repositories

    https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#mongodb.repositories.queries.aggregation

    https://docs.spring.io/spring-data/mongodb/docs/3.2.10/api/org/springframework/data/mongodb/repository/Aggregation.html

    https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Pageable.html

    https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageRequest.html

    MongoDB 聚合 - https://www.mongodb.com/docs/manual/meta/aggregation-quick-reference/

    动态查询

    自定义 Spring 数据存储库实现以及使用 MongoTemplate 应该有助于实现动态查询。

    自定义存储库 - https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#repositories.custom-implementations

    MongoTemplate - https://docs.spring.io/spring-data/mongodb/docs/3.2.10/api/org/springframework/data/mongodb/core/MongoTemplate.html

【讨论】:

  • 我们是否可以修改 match 语句以仅在 customerId 不为 null 时才包含它。案例 1:当我们获得 customerId 例如为 1234 时,查询应返回 customerID 为 1234 的客户。案例 2:当 customerId 为空时,查询应返回所有客户。我们可以使用上面的查询来实现这一点吗?请建议
  • @PrasadReddy 如果查询是动态的,那么方法应该是使用自定义存储库和MongoTemplate。更新的答案是指它的文档。
  • 我在下面添加了类似@Aggregation(pipeline = { "{ '$match': { 'customerId' : ?0, '$exists' : true } }", "{ '$sort' : { 'customerId' : 1 } }", "{ '$skip' : ?1 }" }) 但我收到异常,如命令失败,错误未知顶级运算符 $exists。我是不是遗漏了什么,或者这种方法本身是错误的。请帮助@Zorawar
【解决方案2】:

一个简单的用例是使用带有 Query 和 SimpleMongoRepository 类的自定义存储库。

CustomerRepository.java

@Repository
public interface CustomerRepository extends ResourceRepository<Customer, String> {
}

资源库.java

@NoRepositoryBean
public interface ResourceRepository<T, I> extends MongoRepository<T, I> {

    Page<T> findAll(Query query, Pageable pageable);
}

ResourceRepositoryImpl.java

@SuppressWarnings("rawtypes")
public class ResourceRepositoryImpl<T, I> extends SimpleMongoRepository<T, I> implements ResourceRepository<T, I> {

    private MongoOperations mongoOperations;

    private MongoEntityInformation entityInformation;

    public ResourceRepositoryImpl(final MongoEntityInformation entityInformation, final MongoOperations mongoOperations) {
        super(entityInformation, mongoOperations);

        this.entityInformation = entityInformation;
        this.mongoOperations = mongoOperations;
    }

    @Override
    public Page<T> findAll(final Query query, final Pageable pageable) {
        Assert.notNull(query, "Query must not be null!");

        long total = mongoOperations.count(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
        List<T> content = mongoOperations.find(query.with(pageable), entityInformation.getJavaType(), entityInformation.getCollectionName());

        return new PageImpl<T>(content,pageable,total);
    }
}

客户服务.java

@RequiredArgsConstructor
@Service
public class CustomerService {

   private final CustomerRepository repository;

    /**
     * @param customerId
     * @param limit the size of the page to be returned, must be greater than 0.
     * @param page zero-based page index, must not be negative.
     * @return Page of {@link Customer}
     */
    public Page<Customer> getCustomers(String customerId, int limit, int page) {
        Query query = new Query();
        query.addCriteria(Criteria.where("customerId").is(customerId));
        return repository.findAll(query, PageRequest.of(page, limit, Sort.by(Sort.Direction.ASC, "customerId")));
    }

    public List<Customer> getCustomersList(String customerId, int limit, int page) {
        Page<Customer> customerPage = getCustomers(customerId, limit, page);
        return customerPage.getContent();
    }
}

具有特定标准的参考: https://dzone.com/articles/advanced-search-amp-filtering-api-using-spring-dat

【讨论】:

    【解决方案3】:

    我已经将聚合查询与 $skip 和 $limit 一起使用,它工作正常并且在需要对查询结果的复杂部分进行分页时非常有用。对于更简单的查询,我使用带有 Query 对象的 spring mongo 模板。查询对象采用 Pageable 对象,您可以在其中定义页码和页面大小以及排序选项。

    Criteria criterion = Criteria.where("field").is("value");//build your criteria here.
    Query query = new Query(criterion);
    
    Sort fieldSorting = Sort.by(Sort.Direction.DESC, "sortField"); // sort field 
            
    int pageNo = 1; //which page you want to fetch. NoOfPages = TotalRecords/PageZie 
    int pagesize = 10; // no of records per page
    Pageable pageable = PageRequest.of(pageNo, pagesize, fieldSorting); // define your page
    
    mongoTemplate.find(query.with(pageable), Object.class); // provide appropriate DTO class to map.
    

    对于 mongoDB 聚合选项 - https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/ https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/

    【讨论】:

      【解决方案4】:

      限制查询结果的另一种(可能更简单)方法是在使用时在方法声明中添加这些过滤器Mongo存储库.两个关键词最佳第一的可用于达到此目标,还指定所需结果的数量(或通过省略它,因此仅获得一个结果)。

      下面的代码是一个示例,可在docs.spring.io文档MongoRepositories(下方链接)。

      User findFirstByOrderByLastnameAsc();
      
      User findTopByOrderByAgeDesc();
      
      Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
      
      Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
      
      List<User> findFirst10ByLastname(String lastname, Sort sort);
      
      List<User> findTop10ByLastname(String lastname, Pageable pageable);
      

      您还可以将分页应用于您的查询(更多详细信息在 文档)。

      关于排序的一些额外信息:

      由于其他答案也对排序提供了一些见解,因此我想在这方面提出其他选择。

      如果您的方法总是以相同的方式对结果进行排序, 排序可以通过使用排序依据方法声明中的关键字,后跟上升或者描述取决于您的用例。

      List<User> findFirst10ByLastnameOrderByAgeAsc(String lastname);
      
      List<User> findFirst10ByLastnameOrderByAgeDesc(String lastname);
      

      如果您想动态排序结果,你可以使用种类关于您的方法的论点并提供。

      List<User> findFirst10ByLastname(String lastname, Sort sort);
      

      例如,提供排序方式(DESC,“年龄”)在参数中将创建{年龄:-1}对于排序参数。

      参考

      https://docs.spring.io/spring-data/mongodb/docs/3.2.10/reference/html/#repositories.query-methods

      【讨论】:

        猜你喜欢
        • 2019-01-19
        • 1970-01-01
        • 2015-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-30
        • 2013-03-20
        相关资源
        最近更新 更多