此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Elasticsearch 5.5.2! |
Elasticsearch 对象映射
Spring Data Elasticsearch 对象映射是将 Java 对象(域实体)映射到存储在 Elasticsearch 中的 JSON 表示形式并返回的过程。内部用于此映射的类是MappingElasticsearchConverter
.
元模型对象映射
基于元模型的方法使用域类型信息从 Elasticsearch 读取/写入 Elasticsearch。这允许注册Converter
特定域类型映射的实例。
映射注释概述
这MappingElasticsearchConverter
使用元数据来驱动对象到文档的映射。元数据取自实体的属性,可以进行注释。
可以使用以下注释:
-
@Document
:在类级别应用,以指示此类是映射到数据库的候选者。最重要的属性是(查看 API 文档以获取完整的属性列表):-
indexName
:要存储此实体的索引的名称。这可以包含一个 SpEL 模板表达式,例如"log-#{T(java.time.LocalDate).now().toString()}"
-
createIndex
:标记是否在存储库引导时创建索引。默认值为 true。请参阅使用相应映射自动创建索引
-
-
@Id
:在字段级别应用以标记用于标识目的的字段。 -
@Transient
,@ReadOnlyProperty
,@WriteOnlyProperty
:有关详细信息,请参阅以下部分 控制向 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 映射类型。如果未指定字段类型,则默认为FieldType.Auto
. 这意味着,不会为该属性写入任何映射条目,并且 Elasticsearch 将在存储此属性的第一个数据时动态添加一个映射条目(有关动态映射规则,请查看 Elasticsearch 文档)。 -
format
:一种或多种内置日期格式,请参阅下一节日期格式映射。 -
pattern
:一个或多个自定义日期格式,请参阅下一节日期格式映射。 -
store
:标记原始字段值是否应存储在 Elasticsearch 中,默认值为 false。 -
analyzer
,searchAnalyzer
,normalizer
用于指定自定义分析器和规范化器。
-
-
@GeoPoint
:将字段标记为geo_point数据类型。如果该字段是GeoPoint
类。 -
@ValueConverter
定义一个用于转换给定属性的类。与已注册的 Spring 不同Converter
这只会转换带注释的属性,而不是给定类型的每个属性。
映射元数据基础设施在与技术无关的单独 spring-data-commons 项目中定义。
控制向 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
属性清空。{}
下表显示了不同的属性以及根据其值创建的映射:
注解 | 格式字符串 |
---|---|
@Field(类型=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 客户端)的配置中。
例如,如果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 ) 在首次从存储中读取数据时已经具有可用的实体信息。 |
@TypeAlias("human") (1)
public class Person {
@Id String id;
// ...
}
{
"_class" : "human", (1)
"id" : ...
}
1 | 写入实体时使用配置的别名。 |
除非属性类型为Object 、接口或实际值类型与属性声明不匹配。 |
禁用类型提示
当应该使用的索引已经存在,但在其映射中没有定义类型提示并且映射模式设置为严格时,可能需要禁用类型提示的写入。在这种情况下,写入类型提示将产生错误,因为无法自动添加该字段。
可以通过重写方法来禁用整个应用程序的类型提示writeTypeHints()
在派生自AbstractElasticsearchConfiguration
(参见 Elasticsearch 客户端)。
或者,可以使用@Document
注解:
@Document(indexName = "index", writeTypeHint = WriteTypeHint.FALSE)
我们强烈建议不要禁用类型提示。只有在被迫时才这样做。禁用类型提示可能会导致在多态数据的情况下无法从 Elasticsearch 正确检索文档,或者文档检索可能会完全失败。 |
地理空间类型
地理空间类型,如Point
& GeoPoint
转换为纬度/经度对。
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
收集
对于集合中的值,在类型提示和自定义转换方面应用与聚合根相同的映射规则。
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 从搜索结果。 |