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, , : 有关详细信息,请参阅以下部分控制向 Elasticsearch 写入和读取哪些属性。@ReadOnlyProperty@WriteOnlyProperty - 
@PersistenceConstructor:标记给定的构造函数 - 甚至是受包保护的构造函数 - 在从数据库实例化对象时使用。 构造函数参数按名称映射到检索到的 Document 中的键值。 - 
@Field:应用于字段级别并定义字段的属性,大多数属性映射到相应的 Elasticsearch 映射定义(以下列表不完整,请查看 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 映射类型。 如果未指定字段类型,则默认为 。 这意味着,不会为该属性编写映射条目,并且 Elasticsearch 将在存储此属性的第一个数据时动态添加映射条目(查看 Elasticsearch 文档,了解动态映射规则)。FieldType.Auto - 
format:一种或多种内置日期格式,请参阅下一节日期格式映射。 - 
pattern:一种或多种自定义日期格式,请参阅下一节日期格式映射。 - 
store:标记原始字段值是否应存储在 Elasticsearch 中,默认值为 false。 - 
analyzer、 、 用于指定自定义分析器和归一化器。searchAnalyzernormalizer 
 - 
 - 
@GeoPoint:将字段标记为geo_point数据类型。 如果字段是类的实例,则可以省略。GeoPoint - 
@ValueConverter定义用于转换给定属性的类。 与已注册的 Spring 不同,它只转换带注释的属性,而不是给定类型的每个属性。Converter 
映射元数据基础结构在一个单独的 spring-data-commons 项目中定义,该项目与技术无关。
控制向 Elasticsearch 写入和读取哪些属性
本节详细介绍了定义属性值是写入 Elasticsearch 还是从 Elasticsearch 读取属性值的注释。
@Transient:使用此注释注释的属性不会写入映射,其值不会发送到 Elasticsearch,并且当从 Elasticsearch 返回文档时,不会在生成的实体中设置此属性。
@ReadOnlyProperty:具有此注释的属性不会将其值写入 Elasticsearch,但在返回数据时,该属性将填充 Elasticsearch 在文档中返回的值。
其中一个用例是在索引映射中定义的运行时字段。
@WriteOnlyProperty:具有此注释的属性的值将存储在 Elasticsearch 中,但在读取文档时不会设置任何值。
例如,这可以用于合成字段,这些字段应该进入 Elasticsearch 索引,但不在其他地方使用。
日期格式映射
派生自或属于类型的属性必须具有类型的批注,或者必须为此类型注册自定义转换器。
本段介绍 的用法。TemporalAccessorjava.util.Date@FieldFieldType.DateFieldType.Date
注释有两个属性用于定义将哪些日期格式信息写入映射(另请参阅 Elasticsearch 内置格式和 Elasticsearch 自定义日期格式@Field)
该属性用于定义至少一种预定义格式。
如果未定义,则使用默认值 _date_optional_time 和 epoch_millis。format
该属性可用于添加其他自定义格式字符串。
如果只想使用自定义日期格式,则必须将该属性设置为空。patternformat{}
下表显示了不同的属性以及根据其值创建的映射:
| 注解 | 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 来表示年份。 这是由于 Elasticsearch 7 中的更改。 | 
检查枚举的代码,获取预定义值及其模式的完整列表。org.springframework.data.elasticsearch.annotations.DateFormat
范围类型
当字段使用 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>IntegerLongFloatDoubleDateTemporalAccessor
映射的字段名称
无需进一步配置,Spring Data Elasticsearch 将在 Elasticsearch 中使用对象的属性名称作为字段名称。
这可以通过使用该属性上的注释来更改单个字段。@Field
也可以在客户端(Elasticsearch 客户端)的配置中定义一个。
例如,如果配置了 a,则对象的属性 sampleProperty 将映射到 Elasticsearch 中的 sample_property。
A 适用于所有实体;可以通过在属性上设置特定名称来覆盖它。FieldNamingStrategySnakeCaseFieldNamingStrategyFieldNamingStrategy@Field
非字段支持的属性
通常,实体中使用的属性是实体类的字段。
在某些情况下,属性值是在实体中计算的,并且应存储在 Elasticsearch 中。
在这种情况下,getter 方法 () 可以用注释进行注释,此外,该方法必须用 .
在这种情况下,需要的第三个注解是 ,因为这样的值只写入 Elasticsearch。
一个完整的例子:getProperty()@Field@AccessType(AccessType.Type
.PROPERTY)@WriteOnlyProperty
@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
确保将类型 with 添加到初始实体集 () 中,以便在首次从存储中读取数据时已具有可用的实体信息。@TypeAliasAbstractElasticsearchConfiguration#getInitialEntitySet | 
@TypeAlias("human")                (1)
public class Person {
  @Id String id;
  // ...
}
{
  "_class" : "human",              (1)
  "id" : ...
}
| 1 | 写入实体时使用配置的别名。 | 
除非属性类型为 ,接口或实际值类型与属性声明不匹配,否则不会为嵌套对象编写类型提示。Object | 
禁用类型提示
当应使用的索引已存在且未在其映射中定义类型提示且映射模式设置为 strict 时,可能需要禁用类型提示的写入。 在这种情况下,写入类型提示将产生错误,因为无法自动添加该字段。
可以通过覆盖派生自的配置类中的方法,为整个应用程序禁用类型提示(请参阅 Elasticsearch 客户端)。writeTypeHints()AbstractElasticsearchConfiguration
作为替代方法,可以对带有注释的单个索引禁用它们:@Document
@Document(indexName = "index", writeTypeHint = WriteTypeHint.FALSE)
| 我们强烈建议不要禁用类型提示。 只有在被迫的情况下才这样做。 在多态数据或文档检索可能完全失败的情况下,禁用类型提示可能会导致无法从 Elasticsearch 正确检索文档。 | 
地理空间类型
像 & 这样的地理空间类型被转换为纬度/经度对。PointGeoPoint
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 规范映射到 Elasticsearch 文档。
实体的相应属性在索引映射中指定,就像写入索引映射时一样。(另请查看 Elasticsearch 文档)GeoJsongeo_shape
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 }
     }
  }
}
自定义转换
查看上一节的内容,可以注册用于映射域和简单类型的特定规则。ConfigurationElasticsearchCustomConversions
@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 | 设置用于写入 Elasticsearch。ConverterDomainType | 
| 3 | 设置用于从搜索结果中读取。ConverterDomainType | 
| 注解 | 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 来表示年份。 这是由于 Elasticsearch 7 中的更改。 | 
| 1 | 默认情况下,域类型类名用于类型提示。 | 
确保将类型 with 添加到初始实体集 () 中,以便在首次从存储中读取数据时已具有可用的实体信息。@TypeAliasAbstractElasticsearchConfiguration#getInitialEntitySet | 
| 1 | 写入实体时使用配置的别名。 | 
除非属性类型为 ,接口或实际值类型与属性声明不匹配,否则不会为嵌套对象编写类型提示。Object | 
| 我们强烈建议不要禁用类型提示。 只有在被迫的情况下才这样做。 在多态数据或文档检索可能完全失败的情况下,禁用类型提示可能会导致无法从 Elasticsearch 正确检索文档。 | 
| 1 | 添加实现。Converter | 
| 2 | 设置用于写入 Elasticsearch。ConverterDomainType | 
| 3 | 设置用于从搜索结果中读取。ConverterDomainType |