Chapter 4. Querying embedded caches
Use embedded queries when you add Data Grid as a library to custom applications.
Protobuf mapping is not required with embedded queries. Indexing and querying are both done on top of Java objects.
4.1. Querying embedded caches
This section explains how to query an embedded cache using an example cache named "books" that stores indexed Book
instances.
In this example, each Book
instance defines which properties are indexed and specifies some advanced indexing options with Hibernate Search annotations as follows:
Book.java
package org.infinispan.sample; import java.time.LocalDate; import java.util.HashSet; import java.util.Set; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.*; // Annotate values with @Indexed to add them to indexes // Annotate each fields according to how you want to index it @Indexed public class Book { @FullTextField String title; @FullTextField String description; @KeywordField String isbn; @GenericField LocalDate publicationDate; @IndexedEmbedded Set<Author> authors = new HashSet<Author>(); }
Author.java
package org.infinispan.sample; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; public class Author { @FullTextField String name; @FullTextField String surname; }
Procedure
Configure Data Grid to index the "books" cache and specify
org.infinispan.sample.Book
as the entity to index.<distributed-cache name="books"> <indexing path="${user.home}/index"> <indexed-entities> <indexed-entity>org.infinispan.sample.Book</indexed-entity> </indexed-entities> </indexing> </distributed-cache>
Obtain the cache.
import org.infinispan.Cache; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; EmbeddedCacheManager manager = new DefaultCacheManager("infinispan.xml"); Cache<String, Book> cache = manager.getCache("books");
Perform queries for fields in the
Book
instances that are stored in the Data Grid cache, as in the following example:// Get the query factory from the cache QueryFactory queryFactory = org.infinispan.query.Search.getQueryFactory(cache); // Create an Ickle query that performs a full-text search using the ':' operator on the 'title' and 'authors.name' fields // You can perform full-text search only on indexed caches Query<Book> fullTextQuery = queryFactory.create("FROM org.infinispan.sample.Book b WHERE b.title:'infinispan' AND b.authors.name:'sanne'"); // Use the '=' operator to query fields in caches that are indexed or not // Non full-text operators apply only to fields that are not analyzed Query<Book> exactMatchQuery=queryFactory.create("FROM org.infinispan.sample.Book b WHERE b.isbn = '12345678' AND b.authors.name : 'sanne'"); // You can use full-text and non-full text operators in the same query Query<Book> query=queryFactory.create("FROM org.infinispan.sample.Book b where b.authors.name : 'Stephen' and b.description : (+'dark' -'tower')"); // Get the results List<Book> found=query.execute().list();
4.2. Entity mapping annotations
Add annotations to your Java classes to map your entities to indexes.
Hibernate Search API
Data Grid uses the Hibernate Search API to define fine grained configuration for indexing at entity level. This configuration includes which fields are annotated, which analyzers should be used, how to map nested objects, and so on.
The following sections provide information that applies to entity mapping annotations for use with Data Grid.
For complete detail about these annotations, you should refer to the Hibernate Search manual.
@DocumentId
Unlike Hibernate Search, using @DocumentId
to mark a field as identifier does not apply to Data Grid values; in Data Grid the identifier for all @Indexed
objects is the key used to store the value. You can still customize how the key is indexed using a combination of @Transformable
, custom types and custom FieldBridge
implementations.
@Transformable keys
The key for each value needs to be indexed as well, and the key instance must be transformed in a String
. Data Grid includes some default transformation routines to encode common primitives, but to use a custom key you must provide an implementation of org.infinispan.query.Transformer
.
Registering a key Transformer via annotations
You can annotate your key class with org.infinispan.query.Transformable
and your custom transformer implementation will be picked up automatically:
@Transformable(transformer = CustomTransformer.class) public class CustomKey { ... } public class CustomTransformer implements Transformer { @Override public Object fromString(String s) { ... return new CustomKey(...); } @Override public String toString(Object customType) { CustomKey ck = (CustomKey) customType; return ... } }
Registering a key Transformer via the cache indexing configuration
Use the key-transformers
xml element in both embedded and server config:
<replicated-cache name="test"> <indexing auto-config="true"> <key-transformers> <key-transformer key="com.mycompany.CustomKey" transformer="com.mycompany.CustomTransformer"/> </key-transformers> </indexing> </replicated-cache>
Alternatively, use the Java configuration API (embedded mode):
ConfigurationBuilder builder = ... builder.indexing().enable() .addKeyTransformer(CustomKey.class, CustomTransformer.class);
4.3. Programmatically mapping entities
You can programmatically map entities to the index as an alternative to annotating Java classes.
In the following example we map an object Author
which is to be stored in the grid and made searchable on two properties:
import org.apache.lucene.search.Query; import org.hibernate.search.cfg.Environment; import org.hibernate.search.cfg.SearchMapping; import org.hibernate.search.query.dsl.QueryBuilder; import org.infinispan.Cache; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.cache.Index; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.query.CacheQuery; import org.infinispan.query.Search; import org.infinispan.query.SearchManager; import java.io.IOException; import java.lang.annotation.ElementType; import java.util.Properties; SearchMapping mapping = new SearchMapping(); mapping.entity(Author.class).indexed() .property("name", ElementType.METHOD).field() .property("surname", ElementType.METHOD).field(); Properties properties = new Properties(); properties.put(Environment.MODEL_MAPPING, mapping); properties.put("hibernate.search.[other options]", "[...]"); Configuration infinispanConfiguration = new ConfigurationBuilder() .indexing().index(Index.NONE) .withProperties(properties) .build(); DefaultCacheManager cacheManager = new DefaultCacheManager(infinispanConfiguration); Cache<Long, Author> cache = cacheManager.getCache(); SearchManager sm = Search.getSearchManager(cache); Author author = new Author(1, "Manik", "Surtani"); cache.put(author.getId(), author); QueryBuilder qb = sm.buildQueryBuilderForClass(Author.class).get(); Query q = qb.keyword().onField("name").matching("Manik").createQuery(); CacheQuery cq = sm.getQuery(q, Author.class); assert cq.getResultSize() == 1;