对于最新的稳定版本,请使用 Spring Data Elasticsearch 5.4.0! |
Elasticsearch 对象映射
Spring Data Elasticsearch 对象映射是将 Java 对象(域实体)映射到存储在 Elasticsearch 中的 JSON 表示形式并返回的过程。
内部用于此映射的类是MappingElasticsearchConverter
.
元模型对象映射
基于 Metamodel 的方法使用域类型信息从 Elasticsearch 读取/写入 Elasticsearch。
这允许注册Converter
特定域类型映射的实例。
映射注释概述
这MappingElasticsearchConverter
使用元数据来驱动对象到文档的映射。
元数据取自实体的属性,这些属性可以进行批注。
可以使用以下注释:
-
@Document
:在类级别应用,以指示此类是映射到数据库的候选项。 最重要的属性是(查看 API 文档以获取完整的属性列表):-
indexName
:用于存储此实体的索引的名称。 这可以包含一个 SPEL 模板表达式,例如"log-#{T(java.time.LocalDate).now().toString()}"
-
createIndex
:标记是否在存储库引导时创建索引。 默认值为 true。 请参阅使用相应的映射自动创建索引
-
-
@Id
:在字段级别应用,以标记用于身份目的的字段。 -
@Transient
,@ReadOnlyProperty
,@WriteOnlyProperty
:有关详细信息,请参阅以下部分 控制写入 Elasticsearch 和从 Elasticsearch 读取的属性 。 -
@PersistenceConstructor
:标记给定的构造函数 - 甚至是受包保护的构造函数 - 在实例化数据库中的对象时使用。 构造函数参数按名称映射到检索到 Document 中的键值。 -
@Field
:在字段级别应用并定义字段的属性,大多数属性映射到相应的 Elasticsearch 映射定义(以下列表不完整,请查看注释 Javadoc 以获取完整参考):-
name
:字段的名称,它将在 Elasticsearch 文档中表示,如果未设置,则使用 Java 字段名称。 -
type
:字段类型可以是文本、关键字、长整型、整型、短整型、字节、双精度、浮点型、Half_Float、Scaled_Float、日期、Date_Nanos、布尔值、二进制、Integer_Range、Float_Range、Long_Range、Double_Range、Date_Range、Ip_Range、对象、嵌套、Ip、TokenCount、Percolator、Flattened Search_As_You_Type 之一。 请参阅 Elasticsearch 映射类型。 如果未指定字段类型,则默认为FieldType.Auto
. 这意味着,不会为该属性写入映射条目,并且 Elasticsearch 将在存储此属性的第一个数据时动态添加映射条目(请查看 Elasticsearch 文档以了解动态映射规则)。 -
format
:一个或多个内置日期格式,请参阅下一部分 日期格式映射. -
pattern
:一个或多个自定义日期格式,请参阅下一部分 日期格式映射. -
store
:标记是否应将原始字段值存储在 Elasticsearch 中,默认值为 false。 -
analyzer
,searchAnalyzer
,normalizer
用于指定自定义分析器和规范化器。
-
-
@GeoPoint
:将字段标记为geo_point数据类型。 如果字段是GeoPoint
类。 -
@ValueConverter
定义用于转换给定属性的类。 与已注册的 Spring 不同Converter
这只会转换 Annotated 属性,而不是给定类型的每个属性。
映射元数据基础结构在单独的spring-data-commons项目中定义,该项目与技术无关。
控制哪些属性写入 Elasticsearch 和从 Elasticsearch 读取哪些属性
本节详细介绍了定义属性值是写入 Elasticsearch 还是从 Elasticsearch 读取的注释。
@Transient
:使用此注释注释的属性不会写入映射,其值不会发送到 Elasticsearch,并且当从 Elasticsearch 返回文档时,不会在生成的实体中设置此属性。
@ReadOnlyProperty
:具有此注释的属性不会将其值写入 Elasticsearch,但在返回数据时,该属性将填充从 Elasticsearch 返回的文档中的值。
其中一个用例是在索引映射中定义的运行时字段。
@WriteOnlyProperty
:具有此注释的属性的值将存储在 Elasticsearch 中,但在读取文档时不会设置任何值。
例如,这可用于应进入 Elasticsearch 索引但不在其他地方使用的合成字段。
日期格式映射
派生自TemporalAccessor
或java.util.Date
必须具有@Field
类型的注释FieldType.Date
或者必须为此类型注册自定义转换器。
本段描述了FieldType.Date
.
有两个属性@Field
注释,用于定义将哪些日期格式信息写入映射(另请参阅 Elasticsearch 内置格式和 Elasticsearch 自定义日期格式)
这format
属性用于定义至少一种预定义格式。
如果未定义,则使用默认值 _date_optional_time 和 epoch_millis。
这pattern
属性可用于添加其他自定义格式字符串。
如果只想使用自定义日期格式,则必须设置format
属性设置为空 。{}
下表显示了不同的属性以及根据其值创建的映射:
注解 | format 字符串 |
---|---|
@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。 这是由于 Elasticsearch 7 中的更改。 |
检查org.springframework.data.elasticsearch.annotations.DateFormat
enum 获取预定义值及其模式的完整列表。
范围类型
当字段使用 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
接口。
映射的字段名称
无需进一步配置, Spring Data Elasticsearch 将使用对象的属性名称作为 Elasticsearch 中的字段名称。
这可以通过使用@Field
注解。
也可以定义FieldNamingStrategy
在客户端的配置中 (Elasticsearch Clients)。
例如,如果SnakeCaseFieldNamingStrategy
配置后,对象的属性 sampleProperty 将映射到 Elasticsearch 中的 sample_property。
一个FieldNamingStrategy
适用于所有实体;可以通过使用@Field
在属性上。
非字段支持的属性
通常,实体中使用的属性是实体类的字段。
在某些情况下,属性值是在实体中计算的,并且应该存储在 Elasticsearch 中。
在这种情况下,getter 方法 (getProperty()
) 可以使用@Field
注解,此外,该方法还必须使用@AccessType(AccessType.Type
.PROPERTY)
.
在这种情况下,需要的第三个注释是@WriteOnlyProperty
,因此该值仅写入 Elasticsearch。
一个完整的例子:
@Field(type = Keyword)
@WriteOnlyProperty
@AccessType(AccessType.Type.PROPERTY)
public String getProperty() {
return "some value that is calculated here";
}
映射规则
类型提示
映射使用嵌入在发送到服务器的文档中的类型提示来允许泛型类型映射。
这些类型提示表示为_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 添加到初始实体集 (AbstractElasticsearchConfiguration#getInitialEntitySet ) 在首次从 store 读取数据时已经有可用的实体信息。 |
@TypeAlias("human") (1)
public class Person {
@Id String id;
// ...
}
{
"_class" : "human", (1)
"id" : ...
}
1 | 在写入实体时使用配置的别名。 |
除非 properties 类型为Object 、接口或实际值类型与 properties 声明不匹配。 |
禁用类型提示
当应该使用的索引已经存在,而没有在其 Map 中定义类型提示并且 mapping 模式设置为 strict 时,可能需要禁用类型提示的写入。 在这种情况下,编写 type hint 将产生错误,因为无法自动添加字段。
可以通过覆盖writeTypeHints()
在派生自AbstractElasticsearchConfiguration
(请参阅 Elasticsearch 客户端)。
作为替代方法,可以使用@Document
注解:
@Document(indexName = "index", writeTypeHint = WriteTypeHint.FALSE)
我们强烈建议不要禁用 Type Hints。 仅在被迫时执行此作。 禁用类型提示可能会导致无法从 Elasticsearch 中正确检索文档,以防多态数据或文档检索可能完全失败。 |
地理空间类型
地理空间类型,如Point
& GeoPoint
转换为 Lat/Land 对。
public class Address {
String city, street;
Point location;
}
{
"city" : "Los Angeles",
"street" : "2800 East Observatory Road",
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
}
GeoJson 类型
Spring Data Elasticsearch 通过提供接口来支持 GeoJson 类型GeoJson
以及不同几何形状的实现。
它们根据 GeoJson 规范映射到 Elasticsearch 文档。
实体的相应属性在索引映射中指定为geo_shape
写入索引映射时。(另请查看 Elasticsearch 文档)
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 中的值,在类型提示和自定义转换方面,应用与聚合根相同的映射规则。
public class Person {
// ...
List<Person> friends;
}
{
// ...
"friends" : [ { "firstname" : "Kyle", "lastname" : "Reese" } ]
}
地图
对于 Maps 中的值,在类型提示和自定义转换方面,应用与聚合根相同的映射规则。 但是,Map 键需要为 String 才能由 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 }
}
}
}
自定义转化
查看Configuration
上一节的摘录 ElasticsearchCustomConversions
允许注册用于映射域和简单类型的特定规则。
@Configuration
public class Config extends ElasticsearchConfiguration {
@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 | 加Converter 实现。 |
2 | 设置Converter 用于写入DomainType 到 Elasticsearch。 |
3 | 设置Converter 用于读取DomainType 从搜索结果。 |