mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-11-26 11:41:32 +00:00
288 lines
7.0 KiB
Plaintext
288 lines
7.0 KiB
Plaintext
|
|
[[elasticsearch.mapping]]
|
||
|
|
= Elasticsearch Object Mapping
|
||
|
|
|
||
|
|
Spring Data Elasticsearch allows to choose between two mapping implementations abstracted via the `EntityMapper` interface:
|
||
|
|
|
||
|
|
* <<elasticsearch.mapping.jackson2>>
|
||
|
|
* <<elasticsearch.mapping.meta-model>>
|
||
|
|
|
||
|
|
[[elasticsearch.mapping.jackson2]]
|
||
|
|
== Jackson Object Mapping
|
||
|
|
|
||
|
|
The Jackson2 based approach (used by default) utilizes a customized `ObjectMapper` instance with spring data specific modules.
|
||
|
|
Extensions to the actual mapping need to be customized via Jackson annotations like `@JsonInclude`.
|
||
|
|
|
||
|
|
.Jackson2 Object Mapping Configuration
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
@Configuration
|
||
|
|
public class Config extends AbstractElasticsearchConfiguration { <1>
|
||
|
|
|
||
|
|
@Override
|
||
|
|
public RestHighLevelClient elasticsearchClient() {
|
||
|
|
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
----
|
||
|
|
<1> `AbstractElasticsearchConfiguration` already defines a Jackson2 based `entityMapper` via `ElasticsearchConfigurationSupport`.
|
||
|
|
====
|
||
|
|
|
||
|
|
WARNING: `CustomConversions`, `@ReadingConverter` & `@WritingConverter` cannot be applied when using the Jackson based `EntityMapper`.
|
||
|
|
|
||
|
|
[[elasticsearch.mapping.meta-model]]
|
||
|
|
== Meta Model Object Mapping
|
||
|
|
|
||
|
|
The Metamodel based approach uses domain type information for reading/writing from/to Elasticsearch.
|
||
|
|
This allows to register `Converter` instances for specific domain type mapping.
|
||
|
|
|
||
|
|
.Meta Model Object Mapping Configuration
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
@Configuration
|
||
|
|
public class Config extends AbstractElasticsearchConfiguration {
|
||
|
|
|
||
|
|
@Override
|
||
|
|
public RestHighLevelClient elasticsearchClient() {
|
||
|
|
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
|
||
|
|
}
|
||
|
|
|
||
|
|
@Bean
|
||
|
|
@Override
|
||
|
|
public EntityMapper entityMapper() { <1>
|
||
|
|
|
||
|
|
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||
|
|
new DefaultConversionService()); <2>
|
||
|
|
entityMapper.setConversions(elasticsearchCustomConversions()); <3>
|
||
|
|
|
||
|
|
return entityMapper;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
----
|
||
|
|
<1> Overwrite the default `EntityMapper` from `ElasticsearchConfigurationSupport` and expose it as bean.
|
||
|
|
<2> Use the provided `SimpleElasticsearchMappingContext` to avoid inconsistencies and provide a `GenericConversionService`
|
||
|
|
for `Converter` registration.
|
||
|
|
<3> Optionally set `CustomConversions` if applicable.
|
||
|
|
====
|
||
|
|
|
||
|
|
[[elasticsearch.mapping.meta-model.rules]]
|
||
|
|
=== Mapping Rules
|
||
|
|
|
||
|
|
==== Type Hints
|
||
|
|
|
||
|
|
Mapping uses _type hints_ embedded in the document sent to the server to allow generic type mapping. Those type hints are
|
||
|
|
represented as `_class` attributes within the document on the server and will be written for each aggregate root.
|
||
|
|
|
||
|
|
.Type Hints
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
public class Person { <1>
|
||
|
|
|
||
|
|
@Id String id;
|
||
|
|
String firstname;
|
||
|
|
String lastname;
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
"_class" : "com.example.Person", <1>
|
||
|
|
"id" : "cb7bef",
|
||
|
|
"firstname" : "Sarah",
|
||
|
|
"lastname" : "Connor"
|
||
|
|
}
|
||
|
|
----
|
||
|
|
<1> By default the domain types class name is used for the type hint.
|
||
|
|
====
|
||
|
|
|
||
|
|
Type hints can be configured to hold custom information. Use the `@TypeAlias` annotation to do so.
|
||
|
|
|
||
|
|
NOTE: Make sure to add types with `@TypeAlias` to the initial entity set (`AbstractElasticsearchConfiguration#getInitialEntitySet`)
|
||
|
|
to already have entity information available when first reading data from the store.
|
||
|
|
|
||
|
|
.Type Hints with Alias
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
@TypeAlias("human") <1>
|
||
|
|
public class Person {
|
||
|
|
|
||
|
|
@Id String id;
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
"_class" : "human", <1>
|
||
|
|
"id" : ...
|
||
|
|
}
|
||
|
|
----
|
||
|
|
<1> The configured alias is used when writing the entity.
|
||
|
|
====
|
||
|
|
|
||
|
|
NOTE: Type hints will not be written for nested Objects unless the properties type is `Object`, an interface or the
|
||
|
|
actual value type does not match the properties declaration.
|
||
|
|
|
||
|
|
==== Geospatial Types
|
||
|
|
|
||
|
|
Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
|
||
|
|
|
||
|
|
.Geospatial types
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
public class Address {
|
||
|
|
|
||
|
|
String city, street;
|
||
|
|
Point location;
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
"city" : "Los Angeles",
|
||
|
|
"street" : "2800 East Observatory Road",
|
||
|
|
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||
|
|
}
|
||
|
|
----
|
||
|
|
====
|
||
|
|
|
||
|
|
==== Collections
|
||
|
|
|
||
|
|
For values inside Collections apply the same mapping rules as for aggregate roots when it comes to _type hints_
|
||
|
|
and <<elasticsearch.mapping.meta-model.conversions>>.
|
||
|
|
|
||
|
|
.Collections
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
public class Person {
|
||
|
|
|
||
|
|
// ...
|
||
|
|
|
||
|
|
List<Person> friends;
|
||
|
|
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
// ...
|
||
|
|
|
||
|
|
"friends" : [ { "firstname" : "Kyle", "lastname" : "Reese" } ]
|
||
|
|
}
|
||
|
|
----
|
||
|
|
====
|
||
|
|
|
||
|
|
==== Maps
|
||
|
|
|
||
|
|
For values inside Maps apply the same mapping rules as for aggregate roots when it comes to _type hints_
|
||
|
|
and <<elasticsearch.mapping.meta-model.conversions>>.
|
||
|
|
However the Map key needs to a String to be processed by Elasticsearch.
|
||
|
|
|
||
|
|
.Collections
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
public class Person {
|
||
|
|
|
||
|
|
// ...
|
||
|
|
|
||
|
|
Map<String, Address> knownLocations;
|
||
|
|
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
// ...
|
||
|
|
|
||
|
|
"knownLocations" : {
|
||
|
|
"arrivedAt" : {
|
||
|
|
"city" : "Los Angeles",
|
||
|
|
"street" : "2800 East Observatory Road",
|
||
|
|
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
----
|
||
|
|
====
|
||
|
|
|
||
|
|
[[elasticsearch.mapping.meta-model.conversions]]
|
||
|
|
=== Custom Conversions
|
||
|
|
|
||
|
|
Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, previous section>> `ElasticsearchCustomConversions`
|
||
|
|
allows to register specific rules for mapping domain and simple types.
|
||
|
|
|
||
|
|
.Meta Model Object Mapping Configuration
|
||
|
|
====
|
||
|
|
[source,java]
|
||
|
|
----
|
||
|
|
@Configuration
|
||
|
|
public class Config extends AbstractElasticsearchConfiguration {
|
||
|
|
|
||
|
|
@Override
|
||
|
|
public RestHighLevelClient elasticsearchClient() {
|
||
|
|
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
|
||
|
|
}
|
||
|
|
|
||
|
|
@Bean
|
||
|
|
@Override
|
||
|
|
public EntityMapper entityMapper() {
|
||
|
|
|
||
|
|
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||
|
|
new DefaultConversionService());
|
||
|
|
entityMapper.setConversions(elasticsearchCustomConversions()); <1>
|
||
|
|
|
||
|
|
return entityMapper;
|
||
|
|
}
|
||
|
|
|
||
|
|
@Bean
|
||
|
|
@Override
|
||
|
|
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||
|
|
return new ElasticsearchCustomConversions(Arrays.asList(new AddressToMap(), new MapToAddress())); <2>
|
||
|
|
}
|
||
|
|
|
||
|
|
@WritingConverter <3>
|
||
|
|
static class AddressToMap implements Converter<Address, Map<String, Object>> {
|
||
|
|
|
||
|
|
@Override
|
||
|
|
public Map<String, Object> convert(Address source) {
|
||
|
|
|
||
|
|
LinkedHashMap<String, Object> target = new LinkedHashMap<>();
|
||
|
|
target.put("ciudad", source.getCity());
|
||
|
|
// ...
|
||
|
|
|
||
|
|
return target;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@ReadingConverter <4>
|
||
|
|
static class MapToAddress implements Converter<Map<String, Object>, Address> {
|
||
|
|
|
||
|
|
@Override
|
||
|
|
public Address convert(Map<String, Object> source) {
|
||
|
|
|
||
|
|
// ...
|
||
|
|
return address;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
----
|
||
|
|
[source,json]
|
||
|
|
----
|
||
|
|
{
|
||
|
|
"ciudad" : "Los Angeles",
|
||
|
|
"calle" : "2800 East Observatory Road",
|
||
|
|
"localidad" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||
|
|
}
|
||
|
|
----
|
||
|
|
<1> Register `ElasticsearchCustomConversions` with the `EntityMapper`.
|
||
|
|
<2> Add `Converter` implementations.
|
||
|
|
<3> Set up the `Converter` used for writing `DomainType` to Elasticsearch.
|
||
|
|
<4> Set up the `Converter` used for reading `DomainType` from search result.
|
||
|
|
====
|