MongoRepository 动态查询
MongoRepository dynamic queries
我有以下问题。假设我有以下模型对象:
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
}
现在我的问题是我必须根据(例如)是否具有特定的 属性 来查找对象。可以说我想要所有定义了年龄 属性 的人。一个重要的附加要求是我应该 return 分页结果 .
我试过使用
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);
此解决方案的问题在于它 return 是对象列表而不是分页结果。如果我添加 query.with(new PageRequest(3, 2));
,它还会 return 一个特定的页面,但在这种情况下,我无法手动构建 "paged" 结果,因为我不知道元素的总数。
您还有其他可以帮助我的想法吗?
提前致谢!
由于 spring mongo 存储库不够成熟,无法处理所有自定义要求(这是我们在 2016 年使用它的时候)我建议您使用 mongotemplate我们目前正在使用并满足所有要求。您可以实现自己的分页逻辑。让我解释一下。
在您的 REST 调用中,您可以请求 page
和 pageSize
。然后您可以简单地计算要写入 limit
和 skip
值的内容。
request.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;
}
希望对您有所帮助。
我最近遇到了类似的事情。使用 Clazz 替代我的域 class,这是我所做的:
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 进行搜索,再次跳过和限制搜索(我知道不是很有效)。回到控制器,您可以使用 PagedResourcesAssembler's 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);
}
下面是我提出的解决方案。因此,回顾一下,我遇到的问题是,我无法在给定 Query 对象作为输入的情况下执行查询,以提高过滤标准的灵活性。结果证明解决方案非常简单,我只需要仔细阅读文档:)。
- 步骤
扩展 MongoRepository 并添加您的自定义函数:
@NoRepositoryBean
public interface ResourceRepository<T, I extends Serializable> extends MongoRepository<T, I> {
Page<T> findAll(Query query, Pageable pageable);
}
- 步骤
为此接口创建一个实现:
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())
);
}
}
- 步骤
将您的实现设置为默认的 MongoRepository 实现:
@EnableMongoRepositories(repositoryBaseClass = ResourceRepositoryImpl.class)
public class MySpringApplication {
public static void main(final String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
- 步骤
为您的自定义对象创建存储库:
public interface CustomObjectRepo extends ResourceRepository<CustomObject, String> {
}
现在,如果您有多个要保存在 mongo 文档存储中的对象,定义一个扩展您的 ResourceRepository
的接口就足够了(如第 4 步所示),并且您将可以使用 Page<T> findAll(Query query, Pageable pageable)
自定义查询方法。我发现此解决方案是我尝试过的最优雅的解决方案。
如果您有任何改进建议,请与社区分享。
此致,
克里斯蒂安
有一个非常简单的方法可以做到这一点,类似于您的代码:
@Query(value = "?0")
Page<Query> findByQuery(String query, Pageable pageable)
但是将您的查询作为 JSON 对象而不是字符串传递
@Query("?0")
Page<Query> findByQuery(BSONObject query, Pageable pageable)
我有以下问题。假设我有以下模型对象:
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
}
现在我的问题是我必须根据(例如)是否具有特定的 属性 来查找对象。可以说我想要所有定义了年龄 属性 的人。一个重要的附加要求是我应该 return 分页结果 .
我试过使用
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);
此解决方案的问题在于它 return 是对象列表而不是分页结果。如果我添加 query.with(new PageRequest(3, 2));
,它还会 return 一个特定的页面,但在这种情况下,我无法手动构建 "paged" 结果,因为我不知道元素的总数。
您还有其他可以帮助我的想法吗?
提前致谢!
由于 spring mongo 存储库不够成熟,无法处理所有自定义要求(这是我们在 2016 年使用它的时候)我建议您使用 mongotemplate我们目前正在使用并满足所有要求。您可以实现自己的分页逻辑。让我解释一下。
在您的 REST 调用中,您可以请求 page
和 pageSize
。然后您可以简单地计算要写入 limit
和 skip
值的内容。
request.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;
}
希望对您有所帮助。
我最近遇到了类似的事情。使用 Clazz 替代我的域 class,这是我所做的:
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 进行搜索,再次跳过和限制搜索(我知道不是很有效)。回到控制器,您可以使用 PagedResourcesAssembler's 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);
}
下面是我提出的解决方案。因此,回顾一下,我遇到的问题是,我无法在给定 Query 对象作为输入的情况下执行查询,以提高过滤标准的灵活性。结果证明解决方案非常简单,我只需要仔细阅读文档:)。
- 步骤
扩展 MongoRepository 并添加您的自定义函数:
@NoRepositoryBean
public interface ResourceRepository<T, I extends Serializable> extends MongoRepository<T, I> {
Page<T> findAll(Query query, Pageable pageable);
}
- 步骤
为此接口创建一个实现:
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())
);
}
}
- 步骤
将您的实现设置为默认的 MongoRepository 实现:
@EnableMongoRepositories(repositoryBaseClass = ResourceRepositoryImpl.class)
public class MySpringApplication {
public static void main(final String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
- 步骤
为您的自定义对象创建存储库:
public interface CustomObjectRepo extends ResourceRepository<CustomObject, String> {
}
现在,如果您有多个要保存在 mongo 文档存储中的对象,定义一个扩展您的 ResourceRepository
的接口就足够了(如第 4 步所示),并且您将可以使用 Page<T> findAll(Query query, Pageable pageable)
自定义查询方法。我发现此解决方案是我尝试过的最优雅的解决方案。
如果您有任何改进建议,请与社区分享。
此致, 克里斯蒂安
有一个非常简单的方法可以做到这一点,类似于您的代码:
@Query(value = "?0")
Page<Query> findByQuery(String query, Pageable pageable)
但是将您的查询作为 JSON 对象而不是字符串传递
@Query("?0")
Page<Query> findByQuery(BSONObject query, Pageable pageable)