MongoRepository dynamic queries

Viewed 8056

I have the following problem. Lets say I have the following model object:

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

In the properties map, you can insert any kind of property, there are no constrains.

The above object is saved in a MongoDB which looks like this:

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

When a person is saved into the repository the Map<String, String> properties is flatten up. As an example, if we have the following object:

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

the document saved in the MongoRepository will be the following:

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

Now my problem is that I have to look for objects based on (for example), if they have a specific property or not. Lets say I want all Persons for which an age property has been defined. One important additional requirement is that I should return a paged result.

I've tried using the

findAll(Example<Person> example, Pageable pageable)

But this does not work for some reason. I suspect that it's the fact that my model object and the MongoDB Document have different structures.

I've also tried with the QueryDsl (here you have an example: http://www.baeldung.com/queries-in-spring-data-mongodb) but with no success either, and also to me this solution is not to elegant (having to mantain generated classes and alike. Also I have a feeling it will not work because of my Map<String, String> properties object member).

Another solution that came to my mind and would be elegant enough, is to have the following function:

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

In this case I would be able to manually construct the query and I wouldn't have to hardcode the key by which I run the search. My question now is, how can set the query value to be exactly my first parameter? With the example showned above I get the following error

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

One other solution would be to use mongoTemplate and query given some Criteria as in the following example:

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

    mongoTemplate. find(query, Person.class);

The problem with this solution is that it returns a list of objects instead for a paged result. It cal also return a specific page if I add query.with(new PageRequest(3, 2)); but in this case I cannot manually construct the "paged" result because I do not know the total number of elements.

Do you have any other ideas that could help me?

Thanks in advance!

4 Answers

There is a realy easiest way to do this, similar to your code:

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

But passing your query as a BSONObject instead of String

@Query("?0")
Page<Query> findByQuery(BSONObject query, Pageable pageable)
Related