Field configuration

picturesafe-search supports a FieldConfiguration to define the mapping for an Elasticsearch index.

Field configurations are necessary to determine the data type and other properties of an index field. If no FieldConfiguration is defined, Elasticsearch creates the mapping based on the data stored in the index.

For simple use cases (e.g. if there is no need for a fulltext search or only simple data types will be used), an empty FieldConfiguration list can be returned:

Spring configuration

@Configuration
@ComponentScan(basePackages = {"de.picturesafe.search.elasticsearch"})
@Import({DefaultElasticConfiguration.class})
public class Config {

    @Bean
    List<FieldConfiguration> fieldConfigurations() {
        return Collections.emptyList();
    }
}

The following example defines three fields for the Elasticsearch index:

  • Field ‘id’ (Elasticsearch type text, sortable)
  • Field ‘fulltext’ (Elasticsearch type text)
  • Field ‘title’ (Elasticsearch type text, within fulltext, aggregatable, sortable)

Spring configuration

@Configuration
@ComponentScan(basePackages = {"de.picturesafe.search.elasticsearch"})
@Import({DefaultElasticConfiguration.class})
public class Config {

    @Bean
    List<FieldConfiguration> fieldConfigurations() {
        return Arrays.asList(
            FieldConfiguration.ID_FIELD,
            FieldConfiguration.FULLTEXT_FIELD,
            StandardFieldConfiguration.builder(
                "title", ElasticsearchType.TEXT)
                .copyToFulltext(true).sortable(true).build()
        );
    }
}

The above field definition results in the following Elasticsearch mapping:

{
  "default-20200307-162008-396": {
    "mappings": {
      "properties": {
        "fulltext": {
          "type": "text"
        },
        "id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        },
        "title": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "fulltext"
          ]
        }
      }
    }
  }
}

Field properties

Field data types

The simplest case is to define only the data type of the index field via a field configuration:

StandardFieldConfiguration.builder("title", ElasticsearchType.TEXT).build();
StandardFieldConfiguration.builder("myNumber", ElasticsearchType.LONG).build();

The following predefined Elasticsearch field data types are supported:

Elasticsearch type
TEXT
LONG
INTEGER
SHORT
BYTE
DOUBLE
FLOAT
DATE
BOOLEAN
NESTED
COMPLETION

Other field data types supported by Elasticsearch can be passed as string, for example:

StandardFieldConfiguration.builder("mySpecialField", "scaled_float").build();

System field configurations

The following system field configurations exist to give other fields additional functionality, e.g. for full text search.

// Unique id for each index document
FieldConfiguration.ID_FIELD 
// Default fulltext field for storing various field values
FieldConfiguration.FULLTEXT_FIELD
// Default suggest field for storing various field values 
FieldConfiguration.SUGGEST_FIELD 

System field: ID

The system field configuration FieldConfiguration.ID_FIELD is required if documents with a (unique) ID are to be added to the Elasticsearch index, for example:

singleIndexElasticsearchService.addToIndex(DataChangeProcessingMode.BLOCKING,
    DocumentBuilder.id(1).put("title", "This is a test title").build());

If no FieldConfiguration.ID_FIELD is defined, the documents can be added to the index without an ID:

singleIndexElasticsearchService.addToIndex(DataChangeProcessingMode.BLOCKING,
    DocumentBuilder.withoutId().put("title", "This is a test title").build());

System field: FULLTEXT_FIELD

If a fulltext search over several index fields is needed, the system field FieldConfiguration.FULLTEXT_FIELD must be defined.

In the following example, two more fields are defined in addition to the fulltext field, whose contents are to be found using the fulltext search:

@Bean
List<FieldConfiguration> fieldConfigurations() {
    return Arrays.asList(
        FieldConfiguration.FULLTEXT_FIELD,
        StandardFieldConfiguration.builder("title", ElasticsearchType.TEXT)
            .copyToFulltext(true).build(),
        StandardFieldConfiguration.builder("caption", ElasticsearchType.TEXT)
            .copyToFulltext(true).build()
    );
}

With this configuration, a simple fulltext search using the fields title and caption can be performed as follows:

singleIndexElasticsearchService
    .search(new FulltextExpression("test"), SearchParameter.DEFAULT);

System field: SUGGEST_FIELD

If a suggest (‘search-as-you-type’) feature is to be implemented, the system field FieldConfiguration.SUGGEST_FIELD must be defined.

The following example defines ‘suggest’ support for the field city:

@Bean
List<FieldConfiguration> fieldConfigurations() {
    return Arrays.asList(
        FieldConfiguration.SUGGEST_FIELD,
        StandardFieldConfiguration.builder("city", ElasticsearchType.TEXT)
            .copyToSuggest(true).build()
    );
}

With this configuration, a suggest query for a maximum of ten suggestions can be performed as follows:

// might return 'Hamburg', 'Hannover', 'Halle', ...
singleIndexElasticsearchService.suggest(new SuggestExpression("Ha", 10));
// might return 'Hamburg', 'Hamm', ...
singleIndexElasticsearchService.suggest(new SuggestExpression("Ham", 10));

Additional field properties

Field property: sortable

Fields can be defined as sortable:

StandardFieldConfiguration.builder("keyword", ElasticsearchType.TEXT)
    .sortable(true).build();

In this case they can be used as SortOption:

final SearchParameter searchParameter 
    = SearchParameter.builder()
        .sortOptions(SortOption.asc("keyword")).build();

final SearchResult searchResult 
    = singleIndexElasticsearchService.search(expression, searchParameter);

Field property: aggregatable

Fields can be defined as aggregatable:

StandardFieldConfiguration.builder("keyword", ElasticsearchType.TEXT)
    .aggregatable(true).build();

In this case they can be returned as facets in the search result:

final SearchResult searchResult 
    = singleIndexElasticsearchService
        .search(new FulltextExpression("test"), SearchParameter.DEFAULT);
    
final List<ResultFacet> facets = searchResult.getFacets();