该版本仍在开发中,尚未被视为稳定。对于最新稳定版本,请使用Spring Data Elasticsearch 5.5.5spring-doc.cadn.net.cn

其他弹性搜索作支持

本章介绍了无法通过仓库界面直接访问的 Elasticsearch作的额外支持。 建议将这些作添加为自定义实现,详见自定义仓库实现spring-doc.cadn.net.cn

索引设置

在使用 Spring Data Elasticsearch 创建 Elasticsearch 索引时,可以通过以下方式定义不同的索引设置@Setting注解。 以下论点可供选择:spring-doc.cadn.net.cn

也可以定义索引排序(可查看链接的 Elasticsearch 文档了解可能的字段类型和值):spring-doc.cadn.net.cn

@Document(indexName = "entities")
@Setting(
  sortFields = { "secondField", "firstField" },                                  (1)
  sortModes = { Setting.SortMode.max, Setting.SortMode.min },                    (2)
  sortOrders = { Setting.SortOrder.desc, Setting.SortOrder.asc },
  sortMissingValues = { Setting.SortMissing._last, Setting.SortMissing._first })
class Entity {
    @Nullable
    @Id private String id;

    @Nullable
    @Field(name = "first_field", type = FieldType.Keyword)
    private String firstField;

    @Nullable @Field(name = "second_field", type = FieldType.Keyword)
    private String secondField;

    // getter and setter...
}
1 定义排序字段时,请使用Java属性的名称(firstField),而非Elasticsearch(first_field)
2 sortModes,排序顺序sortMissingValues是可选的,但如果设置了,条目数量必须与sortFields元素

索引映射

当Spring Data Elasticsearch创建索引映射时IndexOperations.createMapping()方法中,它使用了映射注释概述中描述的注释,特别是@Field注解。 此外,还可以添加@Mapping类注释。 该注释具有以下性质:spring-doc.cadn.net.cn

  • 映射路径JSON格式的类路径资源;如果这不是空的,它作为映射使用,不进行其他映射处理。spring-doc.cadn.net.cn

  • 启用当设置为 false 时,该标志会写入映射,不再进行后续处理。spring-doc.cadn.net.cn

  • 日期检测数值检测当未设置为 时,将映射中的相应属性设置为默认值.spring-doc.cadn.net.cn

  • dynamicDateFormats当该字符串数组不空时,它定义用于自动日期检测的日期格式。spring-doc.cadn.net.cn

  • runtimeFieldsPath一个以JSON格式编写的类路径资源,包含运行时字段的定义,写入索引映射,例如:spring-doc.cadn.net.cn

{
  "day_of_week": {
    "type": "keyword",
    "script": {
      "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
    }
  }
}

Filter构建器

筛选器构建器提升查询速度。spring-doc.cadn.net.cn

private ElasticsearchOperations operations;

IndexCoordinates index = IndexCoordinates.of("sample-index");

Query query = NativeQuery.builder()
	.withQuery(q -> q
		.matchAll(ma -> ma))
	.withFilter( q -> q
		.bool(b -> b
			.must(m -> m
				.term(t -> t
					.field("id")
					.value(documentId))
			)))
	.build();

SearchHits<SampleEntity> sampleEntities = operations.search(query, SampleEntity.class, index);

使用滚动功能获取大结果集

Elasticsearch 有一个滚动 API,可以分段获取大结果集。 Spring Data Elasticsearch 内部使用该功能来提供<T> SearchHitsIterator<T> SearchOperations.searchForStream(查询查询,Class<T> clazz,IndexCoordinates index)方法。spring-doc.cadn.net.cn

IndexCoordinates index = IndexCoordinates.of("sample-index");

Query searchQuery = NativeQuery.builder()
    .withQuery(q -> q
        .matchAll(ma -> ma))
    .withFields("message")
    .withPageable(PageRequest.of(0, 10))
    .build();

SearchHitsIterator<SampleEntity> stream = elasticsearchOperations.searchForStream(searchQuery, SampleEntity.class,
index);

List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {
  sampleEntities.add(stream.next());
}

stream.close();

没有方法在搜索作如果需要访问滚动ID,API使用以下方法摘要弹性搜索模板可以使用(这是不同 的基础实现弹性搜索作实现:spring-doc.cadn.net.cn

@Autowired ElasticsearchOperations operations;

AbstractElasticsearchTemplate template = (AbstractElasticsearchTemplate)operations;

IndexCoordinates index = IndexCoordinates.of("sample-index");

Query query = NativeQuery.builder()
    .withQuery(q -> q
        .matchAll(ma -> ma))
    .withFields("message")
    .withPageable(PageRequest.of(0, 10))
    .build();

SearchScrollHits<SampleEntity> scroll = template.searchScrollStart(1000, query, SampleEntity.class, index);

String scrollId = scroll.getScrollId();
List<SampleEntity> sampleEntities = new ArrayList<>();
while (scroll.hasSearchHits()) {
  sampleEntities.addAll(scroll.getSearchHits());
  scrollId = scroll.getScrollId();
  scroll = template.searchScrollContinue(scrollId, 1000, SampleEntity.class);
}
template.searchScrollClear(scrollId);

要使用带仓库方法的 Scroll API,返回类型必须定义为在Elasticsearch仓库中。 该方法的实现将使用ElasticsearchTemplate中的滚动方法。spring-doc.cadn.net.cn

interface SampleEntityRepository extends Repository<SampleEntity, String> {

    Stream<SampleEntity> findBy();

}

排序选项

除了分页和排序中描述的默认排序选项外,Spring Data Elasticsearch 还提供了org.springframework.data.elasticsearch.core.query.Order其推导自org.springframework.data.domain.Sort.Order. 它提供了额外的参数,可以在指定结果排序时发送给 Elasticsearch(见 www.elastic.co/guide/en/elasticsearch/reference/7.15/sort-search-results.html)。spring-doc.cadn.net.cn

还有org.springframework.data.elasticsearch.core.query.GeoDistanceOrder类 可用于按地理距离排序搜索作的结果。spring-doc.cadn.net.cn

如果要检索的类具有地理点名为位置的财产,如下排序将按距离给定点排序结果:spring-doc.cadn.net.cn

Sort.by(new GeoDistanceOrder("location", new GeoPoint(48.137154, 11.5761247)))

运行时字段

从 7.12 版本起,Elasticsearch 增加了运行时字段(www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime.html)的功能。 Spring Data Elasticsearch 通过两种方式支持此功能:spring-doc.cadn.net.cn

索引映射中的运行时字段定义

定义运行时字段的第一种方法是将定义添加到索引映射中(见 www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-mapping-fields.html)。 要在 Spring Data Elasticsearch 中使用这种方法,用户必须提供包含对应定义的 JSON 文件,例如:spring-doc.cadn.net.cn

例子1。runtime-fields.json
{
  "day_of_week": {
    "type": "keyword",
    "script": {
      "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
    }
  }
}

该 JSON 文件的路径必须存在于 classpath 中,必须在@Mapping实体注释:spring-doc.cadn.net.cn

@Document(indexName = "runtime-fields")
@Mapping(runtimeFieldsPath = "/runtime-fields.json")
public class RuntimeFieldEntity {
	// properties, getter, setter,...
}

运行时字段定义设置在查询上

定义运行时字段的第二种方式是将定义添加到搜索查询中(见 www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-search-request.html)。 以下代码示例展示了如何使用 Spring Data Elasticsearch 实现这一点:spring-doc.cadn.net.cn

所用实体是一个具有价格财产:spring-doc.cadn.net.cn

@Document(indexName = "some_index_name")
public class SomethingToBuy {

	private @Id @Nullable String id;
	@Nullable @Field(type = FieldType.Text) private String description;
	@Nullable @Field(type = FieldType.Double) private Double price;

	// getter and setter
}

以下查询使用一个运行时字段,计算价格含税通过将价格增加19%,并在搜索查询中使用该值找到所有在其中的实体价格含税大于或等于给定值:spring-doc.cadn.net.cn

RuntimeField runtimeField = new RuntimeField("priceWithTax", "double", "emit(doc['price'].value * 1.19)");
Query query = new CriteriaQuery(new Criteria("priceWithTax").greaterThanEqual(16.5));
query.addRuntimeField(runtimeField);

SearchHits<SomethingToBuy> searchHits = operations.search(query, SomethingToBuy.class);

这适用于所有查询接口。spring-doc.cadn.net.cn

时间点(PIT) API

弹性搜索作支持Elasticsearch的点点API(见 www.elastic.co/guide/en/elasticsearch/reference/8.3/point-in-time-api.html)。 以下代码片段展示了如何将此功能应用于虚构的类:spring-doc.cadn.net.cn

ElasticsearchOperations operations; // autowired
Duration tenSeconds = Duration.ofSeconds(10);

String pit = operations.openPointInTime(IndexCoordinates.of("person"), tenSeconds); (1)

// create query for the pit
Query query1 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Smith"))
    .withPointInTime(new Query.PointInTime(pit, tenSeconds))                        (2)
    .build();
SearchHits<Person> searchHits1 = operations.search(query1, Person.class);
// do something with the data

// create 2nd query for the pit, use the id returned in the previous result
Query query2 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Miller"))
    .withPointInTime(
        new Query.PointInTime(searchHits1.getPointInTimeId(), tenSeconds))          (3)
    .build();
SearchHits<Person> searchHits2 = operations.search(query2, Person.class);
// do something with the data

operations.closePointInTime(searchHits2.getPointInTimeId());                        (4)
1 创建一个索引的时间点(可以是多个名称)和一个保持活着的持续时间,并检索其 ID
2 将该 ID 传递到查询中,与下一个保持生命值一起搜索
3 下一次查询时,使用上一次搜索返回的ID。
4 完成后,用最后返回的ID关闭该时间点

搜索模板支持

支持使用搜索模板 API。 要使用这个,首先需要创建一个存储脚本。 这弹性搜索作接口扩展脚本作它提供了必要的功能。 这里的例子假设我们有具有 名为名称. 搜索模板脚本可以这样保存:spring-doc.cadn.net.cn

import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.script.Script;

operations.putScript(                            (1)
  Script.builder()
    .withId("person-firstname")                  (2)
    .withLanguage("mustache")                    (3)
    .withSource("""                              (4)
      {
        "query": {
          "bool": {
            "must": [
              {
                "match": {
                  "firstName": "{{firstName}}"   (5)
                }
              }
            ]
          }
        },
        "from": "{{from}}",                      (6)
        "size": "{{size}}"                       (7)
      }
      """)
    .build()
);
1 使用该putScript()存储搜索模板脚本的方法
2 脚本的名称 / ID
3 搜索模板中使用的脚本必须使用胡须语言。
4 剧本来源
5 脚本中的搜索参数
6 寻呼请求偏移量
7 分页请求大小

在搜索查询中使用搜索模板时,Spring Data Elasticsearch 提供了SearchTemplateQuery, 是org.springframework.data.elasticsearch.core.query.Query接口。spring-doc.cadn.net.cn

虽然SearchTemplateQuery查询接口,基类提供的所有功能并非全部适用于SearchTemplateQuery比如设置一个可页面或者排序.该功能的值必须添加到存储脚本中,如以下示例中所示的分页参数。如果这些值被设置为查询反对,他们将被忽视。

在接下来的代码中,我们将添加一个使用搜索模板查询到自定义仓库实现的调用(参见自定义仓库实现),作为如何将其集成到仓库调用中的示例。spring-doc.cadn.net.cn

我们首先定义自定义仓库片段接口:spring-doc.cadn.net.cn

interface PersonCustomRepository {
	SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable);
}

该仓库片段的实现如下:spring-doc.cadn.net.cn

public class PersonCustomRepositoryImpl implements PersonCustomRepository {

  private final ElasticsearchOperations operations;

  public PersonCustomRepositoryImpl(ElasticsearchOperations operations) {
    this.operations = operations;
  }

  @Override
  public SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable) {

    var query = SearchTemplateQuery.builder()                               (1)
      .withId("person-firstname")                                           (2)
      .withParams(
        Map.of(                                                             (3)
          "firstName", firstName,
          "from", pageable.getOffset(),
          "size", pageable.getPageSize()
          )
      )
      .build();

    SearchHits<Person> searchHits = operations.search(query, Person.class); (4)

    return SearchHitSupport.searchPageFor(searchHits, pageable);
  }
}
1 创建一个SearchTemplateQuery
2 请提供搜索模板的ID。
3 参数通过传递地图<字符串,对象>
4 搜索方式和其他查询类型一样。

嵌套类型

以下示例取自org.springframework.data.elasticsearch.core.query.sort.NestedSortIntegrationTests类,展示了如何定义嵌套排序。spring-doc.cadn.net.cn

var filter = StringQuery.builder("""
	{ "term": {"movies.actors.sex": "m"} }
	""").build();
var order = new org.springframework.data.elasticsearch.core.query.Order(Sort.Direction.DESC,
	"movies.actors.yearOfBirth")
	.withNested(
		Nested.builder("movies")
			.withNested(
				Nested.builder("movies.actors")
					.withFilter(filter)
					.build())
			.build());

var query = Query.findAll().addSort(Sort.by(order));

关于筛选查询:无法使用标准查询因为该查询会被转换为Elasticsearch嵌套查询,而在筛选上下文中无法工作。所以只有字符串查询原生查询可以在这里使用。使用上述查询术语时,必须使用Elasticsearch字段名称,因此在重新定义时需注意@Field(name=“...")定义。spring-doc.cadn.net.cn

对于顺序路径和嵌套路径的定义,应使用 Java 实体属性名称。spring-doc.cadn.net.cn