Index Definitions
Search indexes are defined using Java interfaces that describe what entities and what attributes must be indexed. You should create such interfaces for each entity for which you want to enable the full-text search.
Index Definition Interface
A Java interface for an index definition should meet the following criteria:
-
Can have any name.
-
Should be annotated with the
@JmixEntitySearchIndex
annotation. -
The annotation must have the
entity
parameter which specifies an entity this Index Definition is created for. One Index Definition should be created per one searchable entity.
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
}
Indexed entity properties are defined using methods of the interface. These methods should meet the following criteria:
-
Should return
void
. -
Can have any name.
-
Do not have parameters.
-
Do not have implementation.
-
Should be annotated with the
@AutoMappedField
annotation.
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
@AutoMappedField(includeProperties =
{"number", "product", "customer.status", "customer.lastName"})
void orderMapping();
}
AutoMappedField Annotation
The @AutoMappedField
annotation allows you to map specified entity attributes depending on their data type (see below). It can have the following parameters:
-
includeProperties
- a list of entity attributes that should be indexed. Accepts dot notation to specify attributes of related entities. None by default - no properties are indexed and searchable. -
excludeProperties
- a list of entity attributes that should not be indexed. Accepts dot notation to specify attributes of related entities. None by default. -
analyzer
- a name of analyzer defined in Elasticsearch that will be used in index field mapping. If nothing is specified - the Elasticsearch default analyzer is used. -
indexFileContent
- a boolean flag defining if the content of the file found in matched file properties should be indexed.True
by default.
Both includeProperties
and excludeProperties
support *
wildcard. It is expanded to local properties on the corresponding level:
-
*
- local properties of the indexed entity. -
refField.*
- local properties of the entity referenced by therefField
property.
The wildcard doesn’t cover back-reference attributes and attributes of the entity traits: version
, createdBy
, etc.
excludeProperties
is useful only if includeProperties
contains a wildcard - to limit its expansion. For example:
@AutoMappedField(
includeProperties = {"*", "customer.*"},
excludeProperties = {"number", "customer.firstName"})
void orderCustomerMapping();
An analyzer is used to transform incoming text values in different ways including some language morphologies. A specified analyzer is used on both indexing and searching steps.
@JmixEntitySearchIndex(entity = Customer.class)
public interface CustomerIndexDefinition {
@AutoMappedField(
includeProperties = {"firstName", "lastName"},
analyzer = "russian")
void customerMapping();
}
Multiple mapping annotations can be set on a single method or split between multiple methods for some kind of grouping. The following examples represent the same definition:
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
@AutoMappedField(includeProperties =
{"number", "product", "customer.status", "customer.lastName"})
void orderMapping();
}
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
@AutoMappedField(includeProperties = {"number", "product"})
@AutoMappedField(includeProperties = {"customer.status", "customer.lastName"})
void orderMapping();
}
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
@AutoMappedField(includeProperties = {"number", "product"})
void orderMapping();
@AutoMappedField(includeProperties = {"customer.status", "customer.lastName"})
void customerMapping();
}
Automatic Mapping
Automatic mapping by the @AutoMappedField
annotation is supported for entity attributes of the following types:
All of these properties are captured by the wildcard.
Textual Attributes
These are attributes of the String
type. It is the most common case, a value of an attribute is used as the indexed value. A field in the Elasticsearch index looks as follows:
"textualFieldName": "value"
In case of multiple values:
"textualFieldName": ["value1", "value2"]
Reference Attributes
These are references to related entities. Only the instance name of the related instance is used as the indexed value. None of the nested properties are included. To index nested properties of a related entity, add refProperty.nestedProperty
or refProperty.*
to your mapping explicitly.
A field in the Elasticsearch index looks as follows:
"refFieldName": {
"_instance_name": "instanceNameValue"
}
In case of multiple values:
"refFieldName": {
"_instance_name": ["instanceNameValue1", "instanceNameValue2"]
}
Files
These are attributes of the FileRef
type referring files in File Storage. Both the file name and content are used as indexed values by default. If you want to index a file name only, you need to set the indexFileContent
parameter of @AutoMappedField
to false
:
@AutoMappedField(
includeProperties = {"*"},
indexFileContent = false)
void fileMapping();
A field in the Elasticsearch index looks as follows:
"fileRefField": {
"_file_name" : "File name",
"_content" : "File content if enabled"
}
In case of multiple values:
"fileRefField": [
{
"_file_name" : "File name 1",
"_content" : "File content 1"
},
{
"_file_name" : "File name 2",
"_content" : "File content 2"
}
]
Enumeration Attributes
For enumeration attributes, localized values for all available locales are used as indexed values.
A field in the Elasticsearch index looks as follows:
"enumFieldName": ["enValue", "ruValue"]
In case of multiple enum values there is multiplication - all values in all available locales:
"enumFieldName": ["enValue1", "ruValue1", "enValue2", "ruValue2"]
Embedded Attributes
These are references to embedded JPA entities. Inclusion of an embedded attribute is equal to inclusion of all its nested attributes ("someEmbeddedProperty" = "someEmbeddedProperty.*")
. Index value depends on the type of the nested attribute. Unsupported types will be ignored.
Consider for example a root entity with the customer
attribute related to the embedded Customer
entity with the firstName
and lastName
attributes. If you include the customer
attribute, it will lead to the implicit inclusion of the customer.firstName
and customer.lastName
attributes.
Nested Attributes and Collections
You can specify nested properties using the dot notation: refProperty.nestedRefProperty.targetDataProperty
.
Collection attributes are also supported including nested collections on multiple levels. In this case index stores all values of the attribute on the lowest level. For example, property collectionOfReferences.nestedCollectionOfAnotherReferences.name
is stored as:
"collectionOfEntityA": {
"nestedCollectionOfEntityB": {
"name": ["value1", ..., "valueN"]
}
}
The array contains values of the name
attribute of all EntityB
instances of all EntityA
instances within the root entity.
Programmatic Mapping
Instead of using annotations, you can build mapping definition programmatically.
For that purpose you need to create a method in your index definition interface. The method should meet the following criteria:
-
Should be default.
-
Can have any name.
-
Can take Spring beans as parameters for custom configuration.
-
Should have the
MappingDefinition
return type. -
Should be annotated with the
@ManualMappingDefinition
annotation.
You can create a mapping definition within the method body using MappingDefinition.builder()
.
@JmixEntitySearchIndex(entity = Customer.class)
public interface CustomerIndexDefinition {
@ManualMappingDefinition (1)
default MappingDefinition mapping(FilePropertyValueExtractor filePropertyValueExtractor) { (2)
return MappingDefinition.builder()
.addElement(
MappingDefinitionElement.builder()
.includeProperties("*") (3)
.excludeProperties("hobby", "maritalStatus") (4)
.withFieldMappingStrategyClass(AutoMappingStrategy.class) (5)
.withPropertyValueExtractor(filePropertyValueExtractor) (6)
.build()
)
.build();
}
}
1 | The @ManualMappingDefinition annotation marks methods with manual creation of mapping definition. |
2 | You can pass Spring beans as parameters for custom mapping configuration. |
3 | The list of properties that should be indexed. Here the * wildcard is used to include all local properties of the indexed Customer entity. |
4 | The list of properties that should not be indexed. |
5 | The FieldMappingStrategy implementation class that should be used to map properties. The mapping strategy can also be defined as instance using the withFieldMappingStrategy() method with the strategy instance as a parameter. |
6 | An explicit property value extractor. For example, a FilePropertyValueExtractor instance can be used for processing attributes of the FileRef type. |
There can be only one method with programmatic mapping. If a method with programmatic mapping exists, all mapping annotations are ignored. |