MongoRepository动态查询

时间:2017-05-24 07:40:29

标签: mongodb spring-boot mongorepository

我有以下问题。假设我有以下模型对象:

class Person {
    String id;
    String firstName;
    String lastName;
    Map<String, String> properties;
}

在属性贴图中,您可以插入任何类型的属性,没有约束。

上面的对象保存在MongoDB中,如下所示:

public interface PersonRepo extends MongoRepository<Person, String> {
}

当一个人被保存到存储库中时,Map<String, String> properties会变平。例如,如果我们有以下对象:

Person: {
    id := 1;
    firstName := John,
    lastName  := Doe,
    properties := {
        age: 42
    }
}

保存在MongoRepository中的文档如下:

Person: {
    id := 1;
    firstName := John,
    lastName  := Doe,
    age := 42
}

现在我的问题是我必须根据(例如)查找对象,如果它们具有特定属性。让我们说我想要所有定义了年龄属性的人。一个重要的附加要求是我应该返回分页结果

我尝试过使用

findAll(Example<Person> example, Pageable pageable)

但由于某些原因,这不起作用。我怀疑我的模型对象和MongoDB文档有不同的结构。

我也尝试使用QueryDsl(这里有一个例子:http://www.baeldung.com/queries-in-spring-data-mongodb),但也没有成功,而且对我来说这个解决方案并不优雅(不得不保留生成的类等等。我觉得因为我的Map<String, String> properties对象成员而无法工作。

我想到的另一种解决方案就是具有以下功能:

@Query(value = "?0")
Page<Query> findByQuery(String query, Pageable pageable)

在这种情况下,我可以手动构建查询,我不必硬编码运行搜索的密钥。 我现在的问题是,如何将查询值设置为我的第一个参数?通过上面显示的示例,我得到以下错误

java.lang.ClassCastException: java.lang.String cannot be cast to com.mongodb.DBObject

另一个解决方案是使用mongoTemplate并查询一些Criteria,如下例所示:

    final Query query = new Query();
    query.addCriteria(Criteria.where("age").regex(".*"));

    mongoTemplate. find(query, Person.class);

此解决方案的问题在于它返回对象列表而不是分页结果。如果我添加query.with(new PageRequest(3, 2));,它也会返回一个特定的页面,但在这种情况下,我无法手动构建“分页”结果,因为我不知道元素的总数。

你还有其他可以帮助我的想法吗?

提前致谢!

3 个答案:

答案 0 :(得分:2)

贝娄是我提出的解决方案。所以,回顾一下,我遇到的问题是,我不能在给定Query对象作为输入的情况下执行查询,以提高过滤标准的灵活性。解决方案结果非常简单,我只需仔细阅读文档:)。

  1. 步骤
  2. 扩展MongoRepository并添加自定义函数:

    @NoRepositoryBean
    public interface ResourceRepository<T, I extends Serializable> extends MongoRepository<T, I> {
    
        Page<T> findAll(Query query, Pageable pageable);
    }
    
    1. 步骤
    2. 为此界面创建一个实现:

      public class ResourceRepositoryImpl<T, I extends Serializable> 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!");
      
              return new PageImpl<T>(
                      mongoOperations.find(query.with(pageable), entityInformation.getJavaType(), entityInformation.getCollectionName()),
                      pageable,
                      mongoOperations.count(query, entityInformation.getJavaType(), entityInformation.getCollectionName())
              );
          }
      }
      
      1. 步骤
      2. 将您的实现设置为默认的MongoRepository实现:

        @EnableMongoRepositories(repositoryBaseClass = ResourceRepositoryImpl.class)
        public class MySpringApplication {
            public static void main(final String[] args) {
                SpringApplication.run(MySpringApplication.class, args);
            }
        }
        
        1. 步骤
        2. 为自定义对象创建存储库:

          public interface CustomObjectRepo extends ResourceRepository<CustomObject, String> {
          }
          

          现在,如果您要在mongo文档存储中保存多个对象,则定义一个扩展ResourceRepository的接口就足够了(如步骤4所示),您将可以使用{ {1}}自定义查询方法。我发现这个解决方案是我尝试过的最优雅的解决方案。

          如果您有任何改进建议,请与社区分享。

          此致 克里斯蒂安

答案 1 :(得分:1)

我最近遇到过类似的事情。使用Clazz替代我的域类,这就是我所做的:

long count = mongoTemplate.count(query, clazz.class);
query.with(pageable);
List<Clazz> found = mongoTemplate.find(query, clazz.class);
Page<Clazz> page = new PageImpl<T>(found, pageable, count);

我正在使用spring-boot-starter-data-mongodb:1.5.5而Query + MongoTemplate处理分页,排序。 所以,首先我得到了与查询匹配的所有文档的完整计数。然后我再次使用peageable进行搜索,再次搜索跳过并限制(我知道效率不高)。回到Controller,您可以使用GWT documentation toResource(Page page)方法来获取分页超链接。

 public ResponseEntity search(@Valid @RequestBody MyParams myParams, Pageable pageable, PagedResourcesAssembler assembler){
    Page<Clazz> page= searchService.search(myParams, pageable, Clazz.class);
    PagedResources<Clazz> resources = assembler.toResource(page);
    return new ResponseEntity<>(resources, HttpStatus.OK);
}

答案 2 :(得分:0)

由于spring mongo存储库还不够成熟,无法满足所有自定义要求(当我们在2016年使用它时),我建议您使用我们目前正在使用的mongotemplate并满足所有要求。您可以实现自己的分页逻辑。让我解释一下。

在您的REST通话中,您可以请求pageSizelimit。然后,您只需计算要写入skiprequest.getPage() * request.getPageSize() 值的内容。

request.getPageSize()

这将为您提供跳过值,

Integer limit = request.getPageItemCount() == null ? 0 : request.getPageItemCount();
Integer skip = getSkipValue( request.getPage(), request.getPageItemCount() );


 private Integer getSkipValue( Integer page, Integer pageItemCount ){
        if( page == null || pageItemCount == null ){ return 0; }
        int skipValue = ( page - 1 ) * pageItemCount;
        if( skipValue < 0 ){ return 0; }
        return skipValue;
    }

会给你限制值。详细地说,下面的实现可以进一步改进:

application.conf

希望它有所帮助。