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.10/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>
A custom attribute extractor could be passed an Employee object to extract a specific attribute:
returnVal = employee.getdept();
If you need to pass state to your custom extractor, specify properties:
<cache>
<searchable>
<searchAttribute name="age"
class="net.sf.ehcache.search.TestAttributeExtractor"
properties="foo=this,bar=that,etc=12" />
</searchable>
</cache>
If properties are provided, the attribute extractor implementation must have a public constructor that accepts a single java.util.Properties instance.
Dynamic Attributes Extractor
The DynamicAttributesExtractor provides flexibility by allowing the search configuration to be changed after the cache is initialized. This is done with one method call, at the point of element insertion into the cache. The DynamicAttributesExtractor method returns a map of attribute names to index and their respective values. This method is called for every Ehcache.put( ) and replace( ) invocation.
Assuming that we have previously created Person objects containing attributes such as name, age, and gender, the following example shows how to create a dynamically searchable cache and register the DynamicAttributesExtractor:
Configuration config = new Configuration();
config.setName("default");
CacheConfiguration cacheCfg = new CacheConfiguration("PersonCache");
cacheCfg.setEternal(true);
cacheCfg.terracotta(new TerracottaConfiguration().clustered(true));
Searchable searchable = new Searchable().allowDynamicIndexing(true);
cacheCfg.addSearchable(searchable);
config.addCache(cacheCfg);
CacheManager cm = new CacheManager(config);
Ehcache cache = cm.getCache("PersonCache");
final String attrNames[] = {"first_name", "age"};
// Now you can register a dynamic attribute extractor to index
// the cache elements, using a subset of known fields
cache.registerDynamicAttributesExtractor(new DynamicAttributesExtractor() {
Map<String, Object> attributesFor(Element element) {
Map<String, Object> attrs = new HashMap<String, Object>();
Person value = (Person)element.getObjectValue();
// For example, extract first name only
String fName = value.getName() == null ? null : value.getName().
split("\\s+")[0];
attrs.put(attrNames[0], fName);
attrs.put(attrNames[1], value.getAge());
return attrs;
}
});
// Now add some data to the cache
cache.put(new Element(10, new Person("John Doe", 34, Person.Gender.MALE)));
Given the code above, the newly put element would be indexed on values of name and age fields, but not gender. If, at a later time, you would like to start indexing the element data on gender, you would need to create a new DynamicAttributesExtractor instance that extracts that field for indexing.
Dynamic Search Rules
To use the DynamicAttributesExtractor, the cache must be configured to be searchable and dynamically indexable. For information about making a cache searchable, see
Making a Cache Searchable.
A dynamically searchable cache must have a dynamic extractor registered BEFORE data is added to it. (This is to prevent potential races between extractor registration and cache loading which might result in an incomplete set of indexed data, leading to erroneous search results.)
Each call on the DynamicAttributesExtractor method replaces the previously registered extractor, because there can be at most one extractor instance configured for each such cache.
If a dynamically searchable cache is initially configured with a predefined set of search attributes, this set of attributes is always be queried for extracted values, regardless of whether or not a dynamic search attribute extractor has been configured.
The initial search configuration takes precedence over dynamic attributes, so if the dynamic attribute extractor returns an attribute name already used in the initial searchable configuration, an exception is thrown.