Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use SpringData MongoDB repository to execute an arbitrary query, with pagination?

We have a use case where a user can pass in arbitrary search criteria for a collection, and wants the output paged. Using Spring Data repositories, this is quite simple if we know ahead of time what attributes they may be searching on by simple extending MongoRepository, and declaring a:

Page<Thing> findByFooAndBarAndBaz(Type foo, Type bar, Type baz, Pageable page)

However, if we generate the query ourselves either using the fluent interface or constructing a mongo string and wrapping it in a BasicQuery class, I can not find a way to get that into a repository instance. There is no:

Page<Thing> findByQuery(Query q, Pageable page)

functionality that I have been able to see.

Nor can I see how to hook into the MongoTemplate querying functionality with the Page abstraction.

I'm hoping I don't have to roll my own paging (calculating skip and limit parameters, which I guess is not hard) and call into the template directly, but I guess I can if that's the best choice.

like image 920
Michael Campbell Avatar asked Jun 22 '13 22:06

Michael Campbell


2 Answers

I don't think this can be done in the way I'd hoped, but I've come up with a workaround. As background, we put all our methods to do data access in a DAO, and some delegate to the repository, some to the template.

  • Wrote a DAO method which takes our arbitrary filter string (which I have a utility that converts it to standard mongo JSON query syntax.
  • Wrap that in a BasicQuery to get a "countQuery".
  • Use that countQuery to get a total count of records using MongoTemplate#count(Query, Class)
  • Append my paging criteria to create a "pageQuery" using Query#with(Pageable)
  • Run the pageQuery with MongoTemplate#find(Query, Pageable)
  • Get the List<T> result from that, the Pageable that was used for the query and the count returned from the countQuery run, and construct a new PageImp to return to the caller.

Basically, this (DocDbDomain is a test domain class for testing out document db stuff):

    Query countQuery = new BasicQuery(toMongoQueryString(filterString));
    Query pageQuery = countQuery.with(pageRequest);
    long total = template.count(countQuery, DocDbDomain.class);

    List<DocDbDomain> content = template.find(pageQuery, DocDbDomain.class);

    return new PageImpl<DocDbDomain>(content, pageRequest, total);
like image 172
Michael Campbell Avatar answered Nov 15 '22 03:11

Michael Campbell


You can use the @Query annotation to execute an arbitrary query through a repository method:

interface PersonRepository extends Repository<Person, Long> {

  @Query("{ 'firstname' : ?0 }")
  Page<Person> findByCustomQuery(String firstname, Pageable pageable);
}

Generally speaking, @Query can contain any JSON query you can execute via the shell but with the ?0 kind of syntax to replace parameter values. You can find more information on the basic mechanism in the reference documentation.

like image 41
Oliver Drotbohm Avatar answered Nov 15 '22 04:11

Oliver Drotbohm