Do want to get a better understanding of Spring web application architecture? If so, get started right now!

Spring Data Solr Tutorial: Sorting

Sorted card catalog

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.

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:

  1. Add a new Sort parameter to the query method. This method parameter specifies the used sort options.
  2. 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:

  1. Create a private sortByIdDesc() method which specifies that the query results are sorted in descending order by using the id of the document.
  2. Get the sorted query results by calling the query method declared in the TodoDocumentRepository interface.
  3. 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:

  1. 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.
  2. 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).

If you want to learn more about Spring Data Solr, you should read my Spring Data Solr tutorial.

About the Author

Petri Kainulainen is passionate about software development and continuous improvement. He is specialized in software development with the Spring Framework and is the author of Spring Data book.

About Petri Kainulainen →

2 comments… add one

  • Hi Petri!
    It is an awesome work you have done here with this tutorials!
    I have a question, I was working with the @Query annotation
    @Query(value = “id:?0″, fields = {“rev”})
    is it possible to include in the query annotation the sort parameter “rev desc”?
    additionally I would like to include the start=0&rows=1 in order to find the maximum value of rev and retrieve only that document, the query that I perform in Solar is:
    http://localhost:8080/SolrDemo/Index/select?q=id%3A3&sort=rev+desc&start=0&rows=1&wt=json&indent=true
    Thanks and keep the great work!

    Reply
    • Hi Alf,

      Sorry that it took me a couple of days before I could answer to your question.

      As far as I know, you cannot specify the sort parameter by using the @Query annotation. I have always created a Sort object and passed it as a method parameter to my query method (this is the approach explained in this blog post).

      You can get the document which has the maximum value for rev field by using pagination (click the link for more details). The key is to set page size to 1 and ask Spring Data Solr to return the first page. This should return the correct document assuming that the query results are sorted correctly.

      Reply

Leave a Comment