Index Definitions
Search indexes are specified through Java interfaces that outline which entities and attributes should be indexed. It is recommended to create these interfaces for every entity that requires full-text search functionality.
Index Definition Interface
A Java interface defining an index must adhere to these requirements:
-
It can be named arbitrarily.
-
It must be annotated with the
@JmixEntitySearchIndex
annotation. -
The annotation should include the
entity
parameter specifying the entity associated with this Index Definition. It is recommended to have one Index Definition per searchable entity.
@JmixEntitySearchIndex(entity = Order.class)
public interface OrderIndexDefinition {
}
Indexed entity properties are specified using methods within the interface. These methods must adhere to the following guidelines:
-
They should return
void
. -
They can be named arbitrarily.
-
They should not accept parameters.
-
They should not contain any implementation logic.
-
Each method 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 enables you to map specific entity attributes based on their data type (see below). It can include the following parameters:
-
includeProperties
- a list of entity attributes to be indexed. It accepts dot notation to specify attributes of related entities. By default, none are indexed for search. -
excludeProperties
- a list of entity attributes to be excluded from indexing. It accepts dot notation to specify attributes of related entities. By default, none are excluded. -
analyzer
- the name of an analyzer defined in Elasticsearch to be used in index field mapping. If not specified, the Elasticsearch default analyzer is used. -
indexFileContent
- a boolean flag that determines if the content of a file found in matched file properties should be indexed. By default, it is set totrue
.
Both includeProperties
and excludeProperties
support *
wildcard. It is expanded to local properties at the corresponding level:
-
*
- local properties of the indexed entity. -
refField.*
- local properties of the entity referenced by therefField
property.
The wildcard does not apply to back-reference attributes and attributes of entity traits such as version
, createdBy
, etc.
excludeProperties
is beneficial only when includeProperties
contains a wildcard to restrict its expansion. For instance:
@AutoMappedField(
includeProperties = {"*", "customer.*"},
excludeProperties = {"number", "customer.firstName"})
void orderCustomerMapping();
An analyzer is employed to alter incoming text values in various manners, including language morphologies. A specified analyzer is utilized during both the indexing and searching phases.
@JmixEntitySearchIndex(entity = Customer.class)
public interface CustomerIndexDefinition {
@AutoMappedField(
includeProperties = {"firstName", "lastName"},
analyzer = "german")
void customerMapping();
}
Multiple mapping annotations can be applied to a single method or distributed across multiple methods for some kind of grouping. The examples below illustrate the identical 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:
The wildcard encompasses all these attribute types.
Textual Attributes
These attributes are of the String
type. It is the most common case, a value of an attribute is used as the indexed value.
The field in the Elasticsearch index appears as follows:
"textualFieldName": "value"
In case of multiple values:
"textualFieldName": ["value1", "value2"]
Reference Attributes
These are references to related entities. The indexed value comprises solely the instance name of the associated entity, omitting any nested properties. To index nested properties of a related entity, make sure to explicitly incorporate refProperty.nestedProperty
or refProperty.*
into your mapping.
The field in the Elasticsearch index appears 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. By default, both the file name and content are utilized as indexed values. If you wish to index only the file name, you must adjust the indexFileContent
parameter of @AutoMappedField
to false
:
@AutoMappedField(
includeProperties = {"*"},
indexFileContent = false)
void fileMapping();
The field in the Elasticsearch index appears 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
In the case of enumeration attributes, the indexed values include localized values for all available locales.
An entry in the Elasticsearch index appears as:
"enumFieldName": ["enValue", "ruValue"]
If multiple enumeration values are present, each value in all available locales is included, leading to a multiplication of values:
"enumFieldName": ["enValue1", "ruValue1", "enValue2", "ruValue2"]
Embedded Attributes
These are references to embedded JPA entities. Adding an embedded attribute implies including all its nested attributes ("someEmbeddedProperty" = "someEmbeddedProperty.*")
. IThe index value is determined by the type of the nested attribute, and unsupported types will be ignored.
For instance, imagine a root entity with a customer
attribute linked to the embedded Customer
entity, which holds the firstName
and lastName
attributes. If you choose to include the customer
attribute, it will automatically trigger the 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
.
Furthermore, the system supports collection attributes, which includes nested collections at various levels. In such cases, the index consolidates all attribute values at the lowest level. For instance, a property like collectionOfReferences.nestedCollectionOfAnotherReferences.name
is stored in the following format:
"collectionOfEntityA": {
"nestedCollectionOfEntityB": {
"name": ["value1", ..., "valueN"]
}
}
Within the array, you’ll find values of the name
attribute from all instances of EntityB
within all instances of EntityA
in the root entity.
Programmatic Mapping
Instead of using annotations, you can build mapping definition programmatically.
To do this, you will need to define a method in your index definition interface. This method should satisfy the following conditions:
-
It must be a default method.
-
It can be named arbitrarily.
-
It can accept Spring beans as parameters for custom configuration.
-
It should return the
MappingDefinition
type. -
It must be annotated with
@ManualMappingDefinition
.
Within the method body, you can create a mapping definition 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. |