James Hao
James Hao

Reputation: 905

How can I query any field with MongoRepository

Assume the domain object (MyDomain) has many fileds (f1, f2, f3 ... f100), define a MyDomainRepository from MongoRepository, I want to take field name and value as parameters instead of hard code the field name as part of query method, like below:

List<MyDomain> findByNameAndValue(string name, string value);

if the name and value is "f1" and "foo", the method will find all documents whose field "f1" equals "foo".

I have googled hours and no luck.

Any help from anybody, thanks!

Upvotes: 1

Views: 7690

Answers (2)

Marc Tarin
Marc Tarin

Reputation: 3169

pvpkiran is right, there is no such thing out of the box. You need to build your own using an injected MongoTemplate, for instance:

List<MyDomain> findByNameAndValue(string name, string value) {

    Document document = new Document(name, value);
    Query query = new BasicQuery(document.toJson());

    return mongoTemplate.find(query, MyDomain.class);
}

The interesting thing is that you can go a little further and pass several name/value using a Map:

List<MyDomain> findByNamesAndValues(Map<String, String> parameters) {

    Document document = new Document(parameters);
    Query query = new BasicQuery(document.toJson());

    return mongoTemplate.find(query, MyDomain.class);
}

Just in case, that works with a QueryDSL predicate too:

List<MyDomain> findByNamesAndValues(Predicate predicate) {

    AbstractMongodbQuery mongoQuery = new SpringDataMongodbQuery(mongoTemplate, MyDomain.class)
            .where(predicate)
    Query query = new BasicQuery(mongoQuery.toString());

    return mongoTemplate.find(query, MyDomain.class);
}

These methods can be further improved to handle pagination, and other cools feature such as field inclusion/exclusion.

Upvotes: 1

Marc Tarin
Marc Tarin

Reputation: 3169

You need to use QueryDSL predicates.

First, add the following dependencies to your pom.xml (assuming you're using maven to build your project):

<dependencies>
    ...
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-mongodb</artifactId>
    </dependency>
    ...
</dependencies>

Also add this to your build plugins:

<build>
    <plugins>
    ...
    <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <version>1.1.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/java</outputDirectory>
                        <processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    ...
    </plugins>

Your repository must extend QueryDslPredicateExecutor:

public interface MyDomainRepository extends MongoRepository<MyDomain, String>,
                                             QueryDslPredicateExecutor<MyDomain> { }

Your repository will then inherit

public Iterable<MyDomain> findAll(Predicate predicate)

and a few other methods.

When you build your project, QueryDSL will generate Q-classes for you, that you can use to programmatically build predicates and query documents matching your predicates:

QMyDomain q = QMyDomain.mydomain;
Predicate p = q.f1.eq(value);

Iterable<MydDomain> i = repository.findAll(p);

To query your resources using a REST controller, you'll need something similar to:

@RestController
@RequestMapping(/"mydomain")
public class MyDomainController {

    @Autowired private MyDomainRepository repository;

    @GetMapping("/search/query")
    public List<MyDomain> query(@QuerydslPredicate(root = MyDomain.class) Predicate predicate) {
        return repository.findAll(predicate);
    }
}

This last piece of code is quick and dirty made, it won't probably work as is (at least return some kind of List), but you get the idea.

Upvotes: 2

Related Questions