Spring Data访问Elasticsearch----脚本和运行时字段Scripted and runtime fields

Spring Data访问Elasticsearch----脚本和运行时字段Scripted and runtime fields

  • [一、person 实体](#一、person 实体)
  • 二、存储库接口
  • 三、service类
  • [四、脚本化字段Scripted fields](#四、脚本化字段Scripted fields)
  • [五、运行时字段Runtime fields](#五、运行时字段Runtime fields)

Spring Data Elasticsearch支持脚本(scripted)字段和运行时(runtime)字段。有关此方面的详细信息,请参阅有关脚本的 Elasticsearch文档运行时字段。在Spring Data Elasticsearch的上下文中,你可以使用

  • 脚本化字段(scripted fields),用于返回在结果文档上计算并添加到返回文档中的字段。
  • 在存储的文档上计算的运行时字段(runtime fields),可以在查询中使用和在搜索结果中返回。
    以下代码片段将展示你可以做什么(这些代码片段展示了命令式代码,但反应式reactive实现的工作原理类似)。

一、person 实体

在这些例子中使用的enity是一个Person实体。此实体具有birthDate和age属性。虽然出生日期是固定的,但年龄取决于发出查询的时间,需要动态计算。

java 复制代码
@Document(indexName = "persons")
public record Person(
        @Id
        @Nullable
        String id,
        @Field(type = Text)
        String lastName,
        @Field(type = Text)
        String firstName,
        @Field(type = Keyword)
        String gender,
        @Field(type = Date, format = DateFormat.basic_date)
        LocalDate birthDate,
        @Nullable
        @ScriptedField Integer age       --------1            
) {
    public Person(String id,String lastName, String firstName, String gender, String birthDate) {
        this(id,                         --------2            
            lastName,
            firstName,
            LocalDate.parse(birthDate, DateTimeFormatter.ISO_LOCAL_DATE),
            gender,
            null);
    }
}

1. age属性将被计算并填充在搜索结果中。
2. 一个方便的构造函数来设置测试数据。

注意,age属性是用@ScriptedField注解的。这将禁止在索引映射中写入相应的条目,并将属性标记为目标,以便从搜索响应中放置计算字段。

二、存储库接口

本例中使用的存储库:

java 复制代码
public interface PersonRepository extends ElasticsearchRepository<Person, String> {

    SearchHits<Person> findAllBy(ScriptedField scriptedField);

    SearchHits<Person> findByGenderAndAgeLessThanEqual(String gender, Integer age, RuntimeField runtimeField);
}

三、service类

服务类注入了一个存储库和一个ElasticsearchOperations实例,以展示填充和使用age属性的几种方法。我们将代码分成不同的部分,以便将解释放入其中

java 复制代码
@Service
public class PersonService {
    private final ElasticsearchOperations operations;
    private final PersonRepository repository;

    public PersonService(ElasticsearchOperations operations, SaRPersonRepository repository) {
        this.operations = operations;
        this.repository = repository;
    }

    public void save() { --------1
        List<Person> persons = List.of(
                new Person("1", "Smith", "Mary", "f", "1987-05-03"),
                new Person("2", "Smith", "Joshua", "m", "1982-11-17"),
                new Person("3", "Smith", "Joanna", "f", "2018-03-27"),
                new Person("4", "Smith", "Alex", "m", "2020-08-01"),
                new Person("5", "McNeill", "Fiona", "f", "1989-04-07"),
                new Person("6", "McNeill", "Michael", "m", "1984-10-20"),
                new Person("7", "McNeill", "Geraldine", "f", "2020-03-02"),
                new Person("8", "McNeill", "Patrick", "m", "2022-07-04"));

        repository.saveAll(persons);
    }

1. 在Elasticsearch中存储一些数据的utility方法。

四、脚本化字段Scripted fields

下一部分将展示如何使用脚本字段来计算和返回人员的年龄。脚本字段只能向返回的数据添加一些内容,不能在查询中使用年龄(请参阅运行时字段)。

java 复制代码
    public SearchHits<Person> findAllWithAge() {

        var scriptedField = ScriptedField.of("age",                            --------1   
                ScriptData.of(b -> b
                        .withType(ScriptType.INLINE)
                        .withScript("""
                                Instant currentDate = Instant.ofEpochMilli(new Date().getTime());
                                Instant startDate = doc['birth-date'].value.toInstant();
                                return (ChronoUnit.DAYS.between(startDate, currentDate) / 365);
                                """)));

        // version 1: use a direct query
        var query = new StringQuery("""
                { "match_all": {} }
                """);
        query.addScriptedField(scriptedField);                                 --------2   
        query.addSourceFilter(FetchSourceFilter.of(b -> b.withIncludes("*"))); --------3   

        var result1 = operations.search(query, Person.class);                  --------4   

        // version 2: use the repository
        var result2 = repository.findAllBy(scriptedField);                     --------5   

        return result1;
    }

1. 定义计算一个人年龄的ScriptedField。
2. 使用Query时,将脚本字段添加到查询中。
3. 将脚本化字段添加到查询时,还需要一个额外的源filter来从文档源检索普通字段。
4. 获取Person实体,现在在其age属性中设置了值。
5. 在使用存储库时,所需要做的就是添加脚本字段作为方法参数。

五、运行时字段Runtime fields

使用运行时字段时,可以在查询本身中使用计算值。在以下代码中,它用于对给定的gender和最大age的人员运行查询:

java 复制代码
    public SearchHits<Person> findWithGenderAndMaxAge(String gender, Integer maxAge) {

        var runtimeField = new RuntimeField("age", "long", """                    --------1
                                Instant currentDate = Instant.ofEpochMilli(new Date().getTime());
                                Instant startDate = doc['birth-date'].value.toInstant();
                                emit (ChronoUnit.DAYS.between(startDate, currentDate) / 365);
                """);

        // variant 1 : use a direct query
        var query = CriteriaQuery.builder(Criteria
                        .where("gender").is(gender)
                        .and("age").lessThanEqual(maxAge))
                .withRuntimeFields(List.of(runtimeField))                         --------2
                .withFields("age")                                                --------3
                .withSourceFilter(FetchSourceFilter.of(b -> b.withIncludes("*"))) --------4
                .build();

        var result1 = operations.search(query, Person.class);                     --------5

        // variant 2: use the repository                                          --------6
        var result2 = repository.findByGenderAndAgeLessThanEqual(gender, maxAge, runtimeField);

        return result1;
    }
}

1. 定义用于计算人员年龄的运行时字段。//有关内置属性,请参阅(https://docs.asciidoctor.org/asciidoc/latest/attributes/document-attributes-ref/)
2. 使用Query时,添加运行时字段。
3. 将脚本化字段添加到查询时,需要额外的字段参数才能返回计算值。
4. 将脚本化字段添加到查询时,还需要一个额外的源filter来从文档源检索普通字段。
5. 获取使用查询筛选的数据,返回的实体设置了age属性。
6. 当使用存储库时,所需要做的就是添加运行时字段作为方法参数。

除了在查询上定义运行时字段外,还可以通过设置@Mapping注解的runtimeFieldsPath属性来在索引中定义它们,以指向包含运行时字段定义的JSON文件。

相关推荐
小白冲鸭39 分钟前
【报错解决】使用@SpringJunitConfig时报空指针异常
spring·java后端开发
LuckyLay1 小时前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
Stringzhua1 小时前
【SpringCloud】Kafka消息中间件
spring·spring cloud·kafka
Elastic 中国社区官方博客3 小时前
如何将数据从 AWS S3 导入到 Elastic Cloud - 第 3 部分:Elastic S3 连接器
大数据·elasticsearch·搜索引擎·云计算·全文检索·可用性测试·aws
掘金-我是哪吒3 小时前
微服务mysql,redis,elasticsearch, kibana,cassandra,mongodb, kafka
redis·mysql·mongodb·elasticsearch·微服务
研究是为了理解4 小时前
Git Bash 常用命令
git·elasticsearch·bash
成富6 小时前
文本转SQL(Text-to-SQL),场景介绍与 Spring AI 实现
数据库·人工智能·sql·spring·oracle
鹿屿二向箔7 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis
豪宇刘7 小时前
SpringBoot+Shiro权限管理
java·spring boot·spring
一只爱打拳的程序猿8 小时前
【Spring】更加简单的将对象存入Spring中并使用
java·后端·spring