Elasticsearch Object Mapping
Spring Data Elasticsearch 对象映射是将 Java 对象(领域实体)映射到存储在 Elasticsearch 中的 JSON 表示形式并返回的过程。用于此映射的类在内部是 MappingElasticsearchConverter
。
Meta Model Object Mapping
基于元模型的方法使用领域类型信息从 Elasticsearch 中读取/写入。这允许为特定领域类型映射注册“转换器”实例。
Mapping Annotation Overview
MappingElasticsearchConverter
使用元数据来推动将对象映射到文档。元数据来自实体的属性,这些属性可以添加注释。
以下注释可用:
-
@Document
:在类级别应用,表示此类可映射到数据库。最重要的属性是(查看 API 文档中的完整属性列表):-
indexName
:存储此实体的索引的名称。它可以包含像"log-#{T(java.time.LocalDate).now().toString()}"
这样的 SpEL 模板表达式。 -
createIndex
:标记是否在存储库引导时创建索引。默认值为 true。请参阅 Automatic creation of indices with the corresponding mapping
-
-
@Id
: 在字段级别应用以标记用于标识目的的字段。 -
@Transient
,@ReadOnlyProperty
,@WriteOnlyProperty
:有关详细信息,请参阅以下小节 Controlling which properties are written to and read from Elasticsearch。 -
@PersistenceConstructor
: 标记给定的构造函数(甚至是包保护的构造函数),以便在从数据库实例化对象时使用。构造函数参数按名称映射到检索到的文档中的键值。 -
@Field
:在字段级别应用,定义字段的属性,大多数属性都映射到各自的 Elasticsearch Mapping 定义(以下列表不完整,请查看注释 JavaDoc 以获取完整参考):-
name
: 字段在 Elasticsearch 文档中显示时的名称;如果未设置,则使用 Java 字段名称。 -
type
:字段类型,可以是 Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type 之一。请参阅 Elasticsearch Mapping Types。如果未指定字段类型,则默认为FieldType.Auto
。这意味着,尚未为该属性写入映射条目,并且 Elasticsearch 将在为此属性存储第一个数据时动态地添加一个映射条目(请查看 Elasticsearch 文档以了解动态映射规则)。 -
format
:一种或多种内置日期格式,请参阅下一小节 Date format mapping。 -
pattern
: 一个或多个自定义日期格式,请参阅下一部分 Date format mapping. -
store
: 标记原始字段值是否应存储在 Elasticsearch 中,默认值为 false。 -
analyzer
,searchAnalyzer
,normalizer
用于指定自定义分析器和规范化器。
-
-
@GeoPoint
: 将字段标记为 geo_point 数据类型。如果字段是GeoPoint
类的实例,则可以省略。 -
@ValueConverter
定义要用于转换给定属性的类。与注册的 SpringConverter
不同,这仅转换带注释的属性,而不转换给定类型的每个属性。
映射元数据基础设施定义在一个独立的 spring-data-commons 项目中,该项目与技术无关。
Controlling which properties are written to and read from Elasticsearch
本节详细介绍定义是否将属性值写入或从 Elasticsearch 中读取的注释。
@Transient
:使用此注释注释的属性不会写入映射,其值不会发送到 Elasticsearch,并且当从 Elasticsearch 返回文档时,此属性不会设置在结果实体中。
@ReadOnlyProperty
:使用此注释的属性不会将其值写入 Elasticsearch,但在返回数据时,属性会填充 Elasticsearch 中文档中返回的值。一个用例是索引映射中定义的运行时字段。
@WriteOnlyProperty
:使用此注释的属性将在 Elasticsearch 中存储其值,但读取文档时不会使用任何值对其进行设置。例如,这可用于综合字段,这些字段应进入 Elasticsearch 索引,但不会在其他地方使用。
Date format mapping
源自 TemporalAccessor
或类型为 java.util.Date
的属性必须具有类型为 FieldType.Date
的 @Field
注释,或者必须为该类型注册自定义转换器。本段描述了 FieldType.Date
的用法。
`@Field`注解有两个属性,它们定义将哪些日期格式信息写入映射(另请参见 Elasticsearch Built In Formats和 Elasticsearch Custom Date Formats)。
format
属性用于定义至少一种预定义的格式。如果没有定义,那么将使用 _date_optional_time 和 epoch_millis 的默认值。
pattern
属性可用于添加其他自定义格式字符串。如果您只希望使用自定义日期格式,则必须将 format
属性设置为一个空集 {}
。
下表显示了不同的属性以及从其值创建的映射:
annotation | Elasticsearch 映射中的格式字符串 |
---|---|
@Field(type=FieldType.Date) |
"date_optional_time |
epoch_millis", |
|
@Field(type=FieldType.Date, format=DateFormat.basic_date) |
"basic_date" |
@Field(type=FieldType.Date, format={DateFormat.basic_date, DateFormat.basic_time}) |
"basic_date |
basic_time" |
|
@Field(type=FieldType.Date, pattern="dd.MM.uuuu") |
"date_optional_time |
epoch_millis |
|
dd.MM.uuuu", |
|
@Field(type=FieldType.Date, format={}, pattern="dd.MM.uuuu") |
"dd.MM.uuuu" |
如果您使用的是自定义日期格式,则需要对年份使用 uuuu 而不是 yyyy。这是由于一个 change in Elasticsearch 7。 |
查看 org.springframework.data.elasticsearch.annotations.DateFormat
枚举的代码,以获取预定义值及其模式的完整列表。
Range types
当字段添加了 Integer_Range、Float_Range、Long_Range、Double_Range、Date_Range、 或 Ip_Range 中一种类型的注释时,该字段必须是一个类的实例,该类将映射到一个 Elasticsearch 范围,例如:
class SomePersonData {
@Field(type = FieldType.Integer_Range)
private ValidAge validAge;
// getter and setter
}
class ValidAge {
@Field(name="gte")
private Integer from;
@Field(name="lte")
private Integer to;
// getter and setter
}
作为 Spring Data Elasticsearch 提供的替代方案,Range<T>
类允许将前面的例子写成:
class SomePersonData {
@Field(type = FieldType.Integer_Range)
private Range<Integer> validAge;
// getter and setter
}
<T>
类型支持的类是 Integer
、Long
、Float
、Double
、Date
以及实现了`TemporalAccessor` 接口的类。
Mapped field names
在没有进一步配置的情况下,Spring Data Elasticsearch 会将对象的属性名称用作 Elasticsearch 中的字段名称。这可以通过对该属性使用 @Field
注释来对各个字段进行更改。
也可以在客户端的配置中定义 FieldNamingStrategy
(Elasticsearch Clients)。例如,如果配置了 SnakeCaseFieldNamingStrategy
,则对象 sampleProperty 将被映射到 Elasticsearch 中的 sample_property。FieldNamingStrategy
适用于所有实体;可以通过在属性上使用 @Field
设置特定名称来覆盖它。
Non-field-backed properties
通常,实体中使用的属性是实体类的字段。在某些情况下,属性值是在实体中计算的,并且应存储在 Elasticsearch 中。在这种情况下,getter 方法 (getProperty()
除了必须使用 @AccessType(AccessType.Type.PROPERTY)
注释外,还可以使用 @Field
注释。在此类情况下所需的第三个注释是 @WriteOnlyProperty
,因为这样值才会写入 Elasticsearch。一个完整的示例:
@Field(type = Keyword)
@WriteOnlyProperty
@AccessType(AccessType.Type.PROPERTY)
public String getProperty() {
return "some value that is calculated here";
}
Mapping Rules
Type Hints
映射使用嵌入在发送到服务器的文档中的 类型提示 来允许通用类型映射。这些类型提示在文档中表示为 _class
属性,并且为每个聚合根编写。
public class Person { 1
@Id String id;
String firstname;
String lastname;
}
{
"_class" : "com.example.Person", 1
"id" : "cb7bef",
"firstname" : "Sarah",
"lastname" : "Connor"
}
1 | 默认情况下,域类型类名用于类型提示。 |
可以配置类型提示以保存自定义信息。为此,请使用 @TypeAlias
注释。
确保将 |
@TypeAlias("human") 1
public class Person {
@Id String id;
// ...
}
{
"_class" : "human", 1
"id" : ...
}
1 | 配置的别名用于编写实体。 |
以下情况下不会为嵌套对象编写类型提示:属性类型不是 |
Disabling Type Hints
当应该使用的索引已经存在、在它的映射中没有定义类型提示并且映射模式设置为严格时,可能有必要禁用类型提示的写入。在这种情况下,编写类型提示将产生错误,因为无法自动添加字段。
可以通过在从 AbstractElasticsearchConfiguration
派生的配置类中重写方法 writeTypeHints()
为整个应用程序禁用类型提示(请参阅 Elasticsearch Clients)。
作为一种替代方案,可以使用 @Document
注释为单个索引禁用它们:
@Document(indexName = "index", writeTypeHint = WriteTypeHint.FALSE)
我们强烈建议不要禁用类型提示。只有在必要情况下才执行此操作。禁用类型提示可能导致 Elasticsearch 在多态数据情况下无法正确检索文档,或者文档检索可能完全失败。
Geospatial Types
Point
和 GeoPoint
等地理空间类型被转换为 lat/lon 对。
public class Address {
String city, street;
Point location;
}
{
"city" : "Los Angeles",
"street" : "2800 East Observatory Road",
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
}
GeoJson Types
Spring Data Elasticsearch通过提供界面`GeoJson`以及针对不同几何的不同实现支持GeoJson类型。它们根据GeoJson规范映射到Elasticsearch文档。当写入索引映射时,将实体的相应属性在索引映射中指定为`geo_shape`。(也请检查 Elasticsearch documentation)
public class Address {
String city, street;
GeoJsonPoint location;
}
{
"city": "Los Angeles",
"street": "2800 East Observatory Road",
"location": {
"type": "Point",
"coordinates": [-118.3026284, 34.118347]
}
}
已经实现了以下 GeoJson 类型:
-
GeoJsonPoint
-
GeoJsonMultiPoint
-
GeoJsonLineString
-
GeoJsonMultiLineString
-
GeoJsonPolygon
-
GeoJsonMultiPolygon
-
GeoJsonGeometryCollection
Collections
对于集合内的值,当涉及到_type hints_和Custom Conversions时,应用与聚合根相同的映射规则。
public class Person {
// ...
List<Person> friends;
}
{
// ...
"friends" : [ { "firstname" : "Kyle", "lastname" : "Reese" } ]
}
Maps
对于映射内的值,当涉及到_type hints_和Custom Conversions时,应用与聚合根相同的映射规则。但是,映射键需要是字符串才能被Elasticsearch处理。
public class Person {
// ...
Map<String, Address> knownLocations;
}
{
// ...
"knownLocations" : {
"arrivedAt" : {
"city" : "Los Angeles",
"street" : "2800 East Observatory Road",
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
}
}
}
Custom Conversions
通过 previous section ElasticsearchCustomConversions
中的 Configuration
,可以注册特定规则,以映射域和简单类型。
@Configuration
public class Config extends ElasticsearchConfiguration {
@NonNull
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
@Bean
@Override
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
return new ElasticsearchCustomConversions(
Arrays.asList(new AddressToMap(), new MapToAddress())); 1
}
@WritingConverter 2
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 3
static class MapToAddress implements Converter<Map<String, Object>, Address> {
@Override
public Address convert(Map<String, Object> source) {
// ...
return address;
}
}
}
{
"ciudad" : "Los Angeles",
"calle" : "2800 East Observatory Road",
"localidad" : { "lat" : 34.118347, "lon" : -118.3026284 }
}
1 | Add Converter implementations. |
2 | 设置用于将 DomainType 写入 Elasticsearch 的 Converter 。 |
3 | 设置用于从搜索结果中读取 DomainType 的 Converter 。 |