Enterprise Java

Spring Data Solr Tutorial: Dynamic Queries

Solr is often referred as a search server which we can use when we are implementing full-text search functions. However, it is often wise to leverage the performance of Solr when we are implementing a search function which takes its input from a search form.

In this scenario, the executed search query depends from the received input. This means that the number of query parameters depends from the input entered to the search form. In other words, the executed search query is dynamic.

The previous part of my Spring Data Solr tutorial taught us how we can add custom methods to a single repository. It is time put this information in to use and find out how we can create dynamic queries with Spring Data Solr. Let’s get started.

Note: These blog entries provide additional information which helps us to understand the concepts described in this blog post:

Creating Dynamic Queries

This section describes how we can create dynamic queries with Spring Data Solr. It is divided into two subsections which are described in the following:

  • The first subsection describes the basics which we need to know before we can start working on the actual search function.
  • The second subsection describes how we can implement the search function of our example application by adding a custom method to our Spring Data Solr repository.

Learning the Basics

Before we can start implementing the search function of our example application, we need to know how we can create queries “manually” by using Spring Data Solr. We can create a query “manually” by following these steps:

  1. Create the search criteria.
  2. Create the query which holds the used search criteria.
  3. Execute the created query.

These steps are described with more details in the following.

Creating the Search Criteria

First, we have to create the search criteria for our query. We can do this by using the criteria classes which are described in the following:

Creating the Executed Query

Second, we have to create the executed query. The query classes of Spring Data Solr are described in the following:

Executing the Created Query

Third, we have to execute the created query. The SolrTemplate class implements several methods which we can use for this purpose. These methods are described in the following:

  • The long count(final SolrDataQuery query) method returns the number of documents found with the query given as a method parameter.
  • The UpdateResponse delete(SolrDataQuery query) method deletes the documents which match with the query given as a method parameter and returns an UpdateResponse object.
  • The T queryForObject(Query query, Class<T> clazz) method returns a single document which matches with the query given as a method parameter.
  • The FacetPage<T> queryForFacetPage(FacetQuery query, Class<T> clazz) method executes a facet query against Solr index and returns the query results as a FacetPage object.
  • The Page<T> queryForPage(Query query, Class<T> clazz) method executes the query against Solr index and returns the query results as an implementation of the Page interface.

Let’s move on and put this theory into practice.

Implementing the Search Function

The requirements of our search function are following:

  • The search function must return all todo entries which name or description contains some word of the given search term. In other words, if the search term is “Foo Bar”, our search function must return todo entries which title or description contains either “Foo” or “Bar”.
  • The search must be case insensitive.

Because our search function is not static, we have to create it by using a dynamic query. We can create dynamic queries with Spring Data Solr by adding custom method to our Spring Data Solr repository. In other words, we have to follow these steps:

  1. Create a custom interface which declares the added method.
  2. Implement the created interface.
  3. Modify the repository interface to extend the created interface.

These steps are described with more details in the following.

Creating the Custom Interface

First, we have to create a custom interface which declares our custom search method. We can do this by following these steps:

  1. Create an interface called CustomTodoDocumentRepository.
  2. Declare the search() method. This method takes the used search term as a method parameter and returns a list of TodoDocument objects.

The source code of the CustomTodoDocumentRepository interface looks as follows:

public interface CustomTodoDocumentRepository {

    public List<TodoDocument> search(String searchTerm);

    //Other methods are omitted.
}

Implementing the Created Interface

Second, we have to implement the custom interface which we created earlier. We can do this by following these steps:

  1. Create a class called TodoDocumentRepositoryImpl and implement the CustomTodoDocumentRepository interface.
  2. Annotate the class with the @Repository annotation.
  3. Add SolrTemplate field to the class and annotate it with the @Resource annotation.
  4. Implement the search() method.

The implementation of the search() method requires a more detailed description which is given here. We can implement the search() method by following these steps:

  1. Get the words of the search term.
  2. Construct the used search criteria by calling the private createSearchConditions() method and passing the words of the search term as a method parameter. This method creates the used search criteria by using the API of the Criteria class.
  3. Create the executed query by creating a new SimpleQuery object and pass the created Criteria object as a constructor parameter.
  4. Get the search results by calling the queryForPage() method of the SolrTemplate class. Pass the created query and the type of the expected result objects as method parameters.
  5. Return the search results by calling the getContent() method of the Page interface.

The source code of the TodoDocumentRepositoryImpl class looks as follows:

import org.springframework.data.domain.Page;
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;

@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);

        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(“title”).contains(word)
                        .or(new Criteria(“description”).contains(word));
            }
            else {
                conditions = conditions.or(new Criteria(“title”).contains(word))
                        .or(new Criteria(“description”).contains(word));
            }
        }

        return conditions;
    }

    //Other methods are omitted
}

Modifying the Repository Interface

Third, we have to make our custom search() method visible to the users of our repository. We can do this by extending the CustomTodoDocumentRepository interface. The source code of the TodoDocumentRepository interface looks as follows:

import org.springframework.data.solr.repository.SolrCrudRepository;

public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {

}

We have now added a custom search() method to our Spring Data Solr repository. Let’s find out how we can use this method.

Using the Custom Method

We can use the custom method by modifying the search() method of the RepositoryTodoIndexService class. The new implementation of this method is very simple. It gets the search results by calling the search() method of our Spring Data Solr repository and returns the search results.

The source code of the RepositoryTodoIndexService class looks as follows:

import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class RepositoryTodoIndexService implements TodoIndexService {

    @Resource
    private TodoDocumentRepository repository;

    //Other methods are omitted.

    @Override
    public List<TodoDocument> search(String searchTerm) {
        return repository.search(searchTerm);
    }
}

Summary

We have now implemented a dynamic search function with Spring Data Solr. Although our search function was rather simple, we should now be able to implement more complex queries as well.

This tutorial has taught us two things:

  • We learned how we can create queries “manually” by using Spring Data Solr.
  • We learned that we have to implement dynamic search methods by adding custom method to a single repository.

The next part of my Spring Data Solr tutorial describes how we can sort our query results.

P.S. The example application of this blog post is available at Github.
 

Reference: Spring Data Solr Tutorial: Dynamic Queries from our JCG partner Petri Kainulainen at the Petri Kainulainen blog.

Petri Kainulainen

Petri 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.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button