Using Infinispan Query API

Applications using a NoSQL storage often need to query data using a full-text search that can be hardly accomplished using traditional RDBMS. This tutorial shows how to use Infinispan Query module in order to search through data added to the cache using an Object oriented fluent API.

In order to do its job, Infinispan uses behind the scenes the popular Apache Lucene and Hibernate search framework.
Apache Lucene is a high-performance, extensible full-text search-engine library written in Java. Hibernate Search encloses Lucene indexing processes into the transaction contexts of Hibernate/JPA, and transparently manages the lifecycle of Lucene Document objects through the event handler mechanism of Hibernate Core.
Let's see with a concrete example how to perform queries using Infinispan Query API. The first thing you need to do, is adding Infinispan query API library to your project, besides the infinispan-core artifactId:

<dependencies>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-core</artifactId>
            <version>${version.infinispan}</version>
        </dependency>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-query</artifactId>
            <version>${version.infinispan}</version>
        </dependency>
</dependencies>

Now you need to annotate your data that you are going to store in your cache. Here's a minimalist model for our example:

package com.sample;

mport org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;

@Indexed
public class Customer {

	@Field(store = Store.YES, analyze = Analyze.NO)
	long id;

	@Field(store = Store.YES, analyze = Analyze.NO)
	String name;

	@Field(store = Store.YES, analyze = Analyze.NO, indexNullAs = Field.DEFAULT_NULL_TOKEN)
	String surname;

	public Customer(long id, String name, String surname) {
		this.id = id;
		this.name = name;
		this.surname = surname;
	}

	@Override
	public String toString() {
		return "Customer [id="+id+" name=" + name + ", surname=" + surname + "]";
	}
}

Most of these annotations are fairly straightforward to understand. @Indexed indicates that we want Hibernate Search to manage indexes for this entity. @Field indicates that a particular property is to be indexed. Now let's write a simple class that stores and retrieves this object from the cache:   

package com.sample;

import java.util.List;

import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.query.Search;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;

public class InfinispanQuery {

   public static void main(String[] args) {
      ConfigurationBuilder builder = new ConfigurationBuilder();
      builder.indexing().index(Index.ALL)
         .addProperty("default.directory_provider", "ram")
         .addProperty("lucene_version", "LUCENE_CURRENT");
      // Build a local cache manager with default configuration
      DefaultCacheManager cacheManager = new DefaultCacheManager(builder.build());
      // Retrieve the default cache
      Cache<String, Customer> cache = cacheManager.getCache();
      // Add some entries
      cache.put("acme", new Customer(1,"John", "Doe"));
      cache.put("lego", new Customer(2,"Pat", "Williams"));
      
      // Retrieve the QueryFactory for the cache
      QueryFactory factory = Search.getQueryFactory(cache);
      // Build up a query
      Query query = factory.from(Customer.class).having("name").eq("John").toBuilder().build();
      // Execute the query
      List<Customer> list = query.list();
      // List the data
      list.forEach(person -> System.out.printf("Match: %s", person));
      // Stop the cache manager and release resources
      cacheManager.stop();
   }

}

This code at first creates a Cache to be used for the example which has index enabled on it. This is mandatory in order to use Infinispan query API. Next we create a QueryFactory for the cache, which will be used to construct our Query.
As a side note, consider that you can also enable indexes using Infinispan configuration file:

<infinispan>
 <namedCache name="replicated">
   <clustering mode="replicated" />
   <indexing enabled="true" indexLocalOnly="false" />
  </namedCache>
</infinispan>

Follow us on Twitter