Spring Data Solr Tutorial: Sorting
When we are implementing a word search function, we typically want to sort the search results in descending order by using the relevancy of each search result. This is also the default behaviour of Solr.
However, there are situations when it makes to sense to specify the sort order manually. One such situation is an implementation of a “regular” search function which was discussed in the previous part of my Spring Data Solr tutorial.
This blog post describes how we can sort our query results with Spring Data Solr. To be more specific, we have to modify the search function of our example application to sort the search results in descending order by using the value of the id field.
This blog post is divided into three sections:
- The first section describes how we can specify the sorting options used in our queries.
- The second section describes how we can sort our query results when we are building our queries by using query methods.
- The third section teaches us to sort the query results of dynamic queries.
Let’s get started.
Note: These blog posts provide additional information which helps us to understand the concepts described in this blog post:
- Running Solr with Maven
- Spring Data Solr Tutorial: Introduction to Solr
- Spring Data Solr Tutorial: Configuration
- Spring Data Solr Tutorial CRUD (Almost)
- Spring Data Solr Tutorial: Adding Custom Methods to a Single Repository
- Spring Data Solr Tutorial: Dynamic Queries
Specifying the Sort Options of a Query
The sort options of a query are specified by using the Sort class. The typical requirements for sorting query results are given in the following:
- Sort the query results by using the value of a single field.
- Sort the query results by using the values of multiple fields when the sort order of different fields is the same.
- Sort the query results by using the values of multiple fields when sort order of different fields is not the same.
Let’s take a look how we can create a Sort object which fulfills the given requirements.
First, we must create a Sort object which specifies that query results are sorted by using a single field. Lets assume that we want to sort the query results in ascending order by using the id field. We can create the Sort object by using the following code:
new Sort(Sort.Direction.ASC, "id")
Second, we must create a Sort object which states that query results are sorted by using the values of multiple fields when the sort order of different fields is the same. Lets assume that we have to sort the query results in descending order by using the id and description fields. We can create the Sort object by using the following code:
new Sort(Sort.Direction.DESC, "id", "description")
Third, we want to sort the query results by using the values of multiple fields when the sort order of different fields is not the same. Lets assume that we want to sort the query results in descending order by using the description field and in ascending order by using the id field. We can create the Sort object by using the following code:
new Sort(Sort.Direction.DESC, "description").and(new Sort(Sort.Direction.ASC, "id"))
We now know how we can create new Sort objects. Let’s move on and put this theory into practice.
Sorting the Query Results of Query Methods
When we are building our queries by using query methods, we can sort the query results by following these steps:
- Add a new Sort parameter to the query method. This method parameter specifies the used sort options.
- Create a new Sort object in the service layer and pass it as a method parameter when the query method is called.
Let’s move on and find out how this is done.
Modifying the Repository Interface
We can sort the query results of our query by adding a new Sort parameter to the our query method. This method parameter specifies the sort options of the executed query. Let’s move on and take a look at the declarations of our query methods.
Query Generation From Method Name
When the executed query is created by using the query generation from method name strategy, we have to add a Sort parameter to the findByTitleContainsOrDescriptionContains() method of the TodoDocumentRepository interface. The source code of our repository interface looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository; import java.util.List; public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> { public List<TodoDocument> findByTitleContainsOrDescriptionContains(String title, String description, Sort sort); }
Named Queries
When the executed query is created by using named queries, we have to add a Sort parameter to the findByNamedQuery() method of the TodoDocumentRepository interface. The source code of our repository interface looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository; import java.util.List; public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> { @Query(name = "TodoDocument.findByNamedQuery") public List<TodoDocument> findByNamedQuery(String searchTerm, Sort sort); }
Note: This approach does not work if we are using Spring Data Solr RC1 because of a known bug. We have to either use the build snapshot dependency or wait for the release of RC2.
The @Query Annotation
When the executed query is created by using the @Query annotation, we have to add a Sort parameter to the findByQueryAnnotation() method of the TodoDocumentRepository interface. The source code of our repository interface looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository; import java.util.List; public interface TodoDocumentRepository extends PartialUpdateRepository, SolrCrudRepository<TodoDocument, String> { @Query("title:*?0* OR description:*?0*") public List<TodoDocument> findByQueryAnnotation(String searchTerm, Sort sort); }
Note: This approach does not work if we are using Spring Data Solr RC1 because of a known bug. We have to either use the build snapshot dependency or wait for the release of RC2.
Using the Query Method
We can use the modified query method by making the following changes to the search() method of the RepositoryIndexService class:
- Create a private sortByIdDesc() method which specifies that the query results are sorted in descending order by using the id of the document.
- Get the sorted query results by calling the query method declared in the TodoDocumentRepository interface.
- Return the query results.
Let’s move on and take a look at the different implementations of the search() method.
Query Generation From Method Name
When we are building our queries by using the query generation from method name strategy, we can get the query results by using the findByTitleContainsOrDescriptionContains() method of the TodoDocumentRepository interface.
The source code of the relevant part of RepositoryTodoIndexService class looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class RepositoryTodoIndexService implements TodoIndexService { @Resource private TodoDocumentRepository repository; @Override public List<TodoDocument> search(String searchTerm) { return repository.findByTitleContainsOrDescriptionContains(searchTerm, searchTerm, sortByIdDesc()); } private Sort sortByIdDesc() { return new Sort(Sort.Direction.DESC, "id"); } //Other methods are omitted }
Named Queries
When we are building our queries by using named queries, we can get the query results by using the findByNamedQuery() method of the TodoDocumentRepository interface.
The relevant part of the RepositoryTodoIndexService looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class RepositoryTodoIndexService implements TodoIndexService { @Resource private TodoDocumentRepository repository; @Override public List<TodoDocument> search(String searchTerm) { return repository.findByNamedQuery(searchTerm, sortByIdDesc()); } private Sort sortByIdDesc() { return new Sort(Sort.Direction.DESC, "id"); } //Other methods are omitted }
The @Query annotation
When we are building our queries by using the @Query annotation, we can get the query results by using the findByQueryAnnotation() method of the TodoDocumentRepository interface.
The relevant part of the RepositoryTodoIndexService class looks as follows:
import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class RepositoryTodoIndexService implements TodoIndexService { @Resource private TodoDocumentRepository repository; @Override public List<TodoDocument> search(String searchTerm) { return repository.findByQueryAnnotation(searchTerm, sortByIdDesc()); } private Sort sortByIdDesc() { return new Sort(Sort.Direction.DESC, "id"); } //Other methods are omitted }
Sorting the Query Results of Dynamic Queries
Because dynamic queries are created by adding a custom method to a repository interface, the steps required to sort the query results of a dynamic query have no effect to the service layer of our example application.
We can sort the query results of dynamic queries by making the following changes to the implementation of our custom repository interface:
- Add a private sortByIdDesc() method to the TodoDocumentRepositoryImpl class. This method returns a Sort object which specifies that the query results are sorted in descending order by using the id of the document.
- Modify the search() method of the TodoDocumentRepositoryImpl class. Set the sort options to the executed query by using the addSort() method of the Query interface and pass the created Sort object as a method parameter.
The relevant part of the TodoDocumentRepositoryImpl class looks as follows:
import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.core.query.Criteria; import org.springframework.data.solr.core.query.SimpleQuery; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.List; @Repository public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository { @Resource private SolrTemplate solrTemplate; @Override public List<TodoDocument> search(String searchTerm) { String[] words = searchTerm.split(" "); Criteria conditions = createSearchConditions(words); SimpleQuery search = new SimpleQuery(conditions); //SET SORT OPTIONS search.addSort(sortByIdDesc()); Page results = solrTemplate.queryForPage(search, TodoDocument.class); return results.getContent(); } private Criteria createSearchConditions(String[] words) { Criteria conditions = null; for (String word: words) { if (conditions == null) { conditions = new Criteria("id").contains(word) .or(new Criteria("description").contains(word)); } else { conditions = conditions.or(new Criteria("id").contains(word)) .or(new Criteria("description").contains(word)); } } return conditions; } private Sort sortByIdDesc() { return new Sort(Sort.Direction.DESC, "id"); } //Other methods are omitted }
Summary
We have now learned how we can sort query results with Spring Data Solr. This tutorial has taught us three things:
- We know that we can specify the used sort options by using the Sort class.
- We learned that we can sort the query result of query methods by adding a new method parameter to the query method.
- We learned that we can set the sort options to a dynamic query by using the addSort() method of the Query interface.
The next part of my Spring Data Solr tutorial describes how we can paginate the query results of our queries.
P.S. The example applications of this blog posts are available at Github (query methods and dynamic queries).