About Searching
The Search API allows you to execute arbitrarily complex queries against caches. The development of alternative indexes on values provides the ability for data to be looked up based on multiple criteria instead of just keys.
Note: Terracotta BigMemory Go and BigMemory Max products use indexing. The Search API queries open-source Ehcache using a direct search method. For more information about indexing, see Best Practices for Optimizing Searches.
Searchable attributes can be extracted from both keys and values. Keys, values, or summary values (Aggregators) can all be returned. Here is a simple example: Search for 32-year-old males and return the cache values.
Results results = cache.createQuery().includeValues() .addCriteria(age.eq(32).and(gender.eq("male"))).execute();
You can formulate queries using the Search API.
// BigMemory Search API: Attribute<Integer> age = cache.getSearchAttribute("age"); Person.createQuery().addCriteria(age.gt(30)).includeValues().execute();
Before creating a query, the cache configuration must be prepared as described in Making a Cache Searchable.
- For more information about creating queries using the Search API, see Creating a Query.
What is Searchable?
Searches can be performed against Element keys and values, but they must be treated as attributes. Some Element keys and values are directly searchable and can simply be added to the search index as attributes. Some Element keys and values must be made searchable by extracting attributes with supported search types out of the keys and values. It is the attributes themselves that are searchable.
Making a Cache Searchable
Caches can be made searchable, on a per cache basis, either by configuration or programmatically.
By Configuration
Caches are made searchable by adding a <searchable/> tag to the cache definition in the ehcache.xml file.
<cache name="cache2" maxBytesLocalHeap="16M" eternal="true" maxBytesLocalOffHeap="256M"> <persistence strategy="localRestartable"/> <searchable/> </cache>
This configuration will scan keys and values and, if they are of supported search types, add them as attributes called “key” and “value,” respectively. If you do not want automatic indexing of keys and values, you can disable it using:
<cache name="cacheName" ...> <searchable keys="false" values="false"> ... </searchable> </cache>
You might want to do this if you have a mix of types for your keys or values. The automatic indexing will throw an exception if types are mixed.
If you think that you will want to add search attributes after the cache is initialized, you can explicitly indicate the dynamic search configuration. Set the allowDynamicIndexing attribute to “true” to enable use of the Dynamic Attributes extractor. For more information about the Dynamic Attributes extractor, see Defining Attributes.
<cache name="cacheName" ...> <searchable allowDynamicIndexing="true"> ... </searchable> </cache>
Often keys or values will not be directly searchable and instead you will need to extract searchable attributes from the keys or values. The following example shows a more typical case. Attribute extractors are explained in more detail in Defining Attributes.
<cache name="cache3" maxEntriesLocalHeap="10000" eternal="true" maxBytesLocalOffHeap="10G"> <persistence strategy="localRestartable"/> <searchable> <searchAttribute name="age" class="net.sf.ehcache.search.TestAttributeExtractor"/> <searchAttribute name="gender" expression="value.getGender()"/> </searchable> </cache>
Programmatically
The following example shows how to programmatically create the cache configuration with search attributes.
Configuration cacheManagerConfig = new Configuration(); CacheConfiguration cacheConfig = new CacheConfiguration("myCache", 0).eternal(true); Searchable searchable = new Searchable(); cacheConfig.addSearchable(searchable); // Create attributes to use in queries. searchable.addSearchAttribute(new SearchAttribute().name("age")); // Use an expression for accessing values. searchable.addSearchAttribute(new SearchAttribute().name("first_name").expression("value.getFirstName()")); searchable.addSearchAttribute(new SearchAttribute().name("last_name").expression("value.getLastName()")); searchable.addSearchAttribute( new SearchAttribute().name("zip_code").className("net.sf.ehcache.search.TestAttributeExtractor")); cacheManager = new CacheManager(cacheManagerConfig); cacheManager.addCache(new Cache(cacheConfig)); Ehcache myCache = cacheManager.getEhcache("myCache"); // Now create the attributes and queries, then execute. ...
To learn more about the Search API, see the net.sf.ehcache.search* packages in the Ehcache Javadoc.
Defining Attributes
In addition to configuring a cache to be searchable, you must define the attributes to be used in searches.
Attributes are extracted from keys or values during search by using AttributeExtractors. An extracted attribute must be one of the following types:
- Boolean
- Byte
- Character
- Double
- Float
- Integer
- Long
- Short
- String
- java.util.Date
- java.sql.Date
- Enum
These types correspond to the AttributeType enum specified by the Ehcache Javadoc at http://ehcache.org/apidocs/2.9/net/sf/ehcache/search/attribute/AttributeType.html.
Type name matching is case sensitive. For example, Double resolves to the java.lang.Double class type, and double is interpreted as the primitive double type.
Search API Example
<searchable> <searchAttribute name="age" type="Integer"/> </searchable>
If an attribute cannot be found or is of the wrong type, an AttributeExtractorException is thrown on search execution. !
Note: On the first use of an attribute, the attribute type is detected, validated against supported types, and saved automatically. Once the type is established, it cannot be changed. For example, if an integer value was initially returned for attribute named “Age” by the attribute extractor, it is an error for the extractor to return a float for this attribute later on.
Well-known Attributes
The parts of an Element that are well-known attributes can be referenced by some predefined, well-known names. If a key and/or value is of a supported search type, it is added automatically as an attribute with the name “key” or “value.” These well-known attributes have the convenience of being constant attributes made available in the Query class. For example, the attribute for “key” can be referenced in a query by Query.KEY. For even greater readability, statically import so that, in this example, you would use KEY.
Well-known Attribute Name | Attribute Constant |
key | Query.KEY |
value | Query.VALUE |
Reflection Attribute Extractor
The ReflectionAttributeExtractor is a built-in search attribute extractor that uses JavaBean conventions and also understands a simple form of expression. Where a JavaBean property is available and it is of a searchable type, it can be declared:
<cache> <searchable> <searchAttribute name="age"/> </searchable> </cache>
The expression language of the ReflectionAttributeExtractor also uses method/value dotted expression chains. The expression chain must start with “key”, “value”, or “element”. From the starting object, a chain of method calls or field names follows. Method calls and field names can be freely mixed in the chain:
<cache> <searchable> <searchAttribute name="age" expression="value.person.getAge()"/> </searchable> </cache> <cache> <searchable> <searchAttribute name="name" expression="element.toString()"/> </searchable> </cache>
Note: The method and field name portions of the expression are case-sensitive.
Custom Attribute Extractor
In more complex situations, you can create your own attribute extractor by implementing the AttributeExtractor interface. The interface‘s attributeFor() method returns the attribute value for the element and attribute name you specify.
Note: These examples assume there are previously created Person objects containing attributes such as name, age, and gender.
Provide your extractor class:
<cache name="cache2" maxEntriesLocalHeap="0" eternal="true"> <persistence strategy="none"/> <searchable> <searchAttribute name="age" class="net.sf.ehcache.search.TestAttributeExtractor"/> </searchable> </cache>