3

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

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));但在这种情况下,我无法手动构造“分页”结果,因为我不知道元素的总数。

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

提前致谢!

4

4 回答 4

3

贝娄是我想出的解决方案。回顾一下,我遇到的问题是,我无法在给定 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)自定义查询方法。我发现这个解决方案是我尝试过的最优雅的解决方案。

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

问候, 克里斯蒂安

于 2017-07-31T09:22:02.163 回答
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 进行了搜索,该搜索再次被跳过和限制(不是很有效,我知道)。回到控制器,您可以使用PagedResourcesAssembler 的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);
}
于 2017-07-27T20:08:57.733 回答
0

有一种非常简单的方法可以做到这一点,类似于您的代码:

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

但是将您的查询作为 BSONObject 而不是 String

@Query("?0")
Page<Query> findByQuery(BSONObject query, Pageable pageable)
于 2021-10-06T19:50:09.640 回答
0

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

在您的 REST 调用中,您可以请求pagepageSize. 然后您可以简单地计算要写入的内容limitskip值。

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

希望能帮助到你。

于 2017-05-24T07:57:33.073 回答