当前位置: 首页 > news >正文

ElasticSearch的学习

介绍

ElasticSearch(简称ES)是一个开源的分布式搜索和数据分析引擎,是用Java开发并且是当前最流行的开源的企业级搜索引擎,能够达到近实时搜索,它专门设计用于处理大规模的文本数据和实现高性能的全文检索。

ElasticSearch是基于Restfull风格进行数据操作

应用场景:全文检索、日志分析、商业智能决策 等

ElasticStack生态介绍

ElasticSearch:核心搜索和分析引擎,提供存储、索引和分布式搜索功能

Logstach:数据处理管道,负责数据收集、处理和传输

Beats:边缘数据采集器,负责从各种来源采集数据并发送到Logstash或者ElasticSearch

Kibana:可视化和管理工具,提供数据展示和交互式查询

分词器

分词器可以让搜索的时候,能将指定的搜索词分成几个不同部分的搜索词来进行分别搜索。官方称之为文本分析器,顾名思义,是对文本进行分析处理的一种手段,基本处理逻辑为按照预先制定的分词规则,把原始文档分割成若干更小粒度的词项,粒度大小取决于分词器规则。

一般中文使用ik分词器,使用"analyzer":"ik_max_word","search_analyzer":"ik_smart"的方案。

ElasticSearch的核心概念

全文检索(Full-Text-Serach):全文检索是一种从大量文本数据中快速检索出包含指定词汇或者词语的信息的技术。

倒排索引:在一个文档集合中,每个文档都可以视为一个词语的集合,倒排索引则是将词语映射到包含这个词语的文档的数据结构

如何实现倒排索引:

  1. 文档预处理
  2. 构建词典
  3. 创建倒排列表
  4. 存储索引文件
  5. 查询处理

索引:类似于MySQL中的表,用于存储不同的数据

映射:类似于表中的schema,用于表示索引中的数据的结构

文档:索引中的一个数据实体,类似于MySQL中的一行记录

索引操作

创建索引

PUT /index_name

index_name代表索引名

可以直接用上述的语法去创建一个空的索引,也可以直接在创建的时候直接初始化好表的信息,例如:

PUT /index_name
{"settings":{//索引设置},"mapping":{//字段映射}
}

必要的参数:

  • 索引名称(index_name):索引名必须是小写字母,可以包含数字和下划线

  • 索引设置(settings)

    • 分片数量(numberofshards):一个索引的分片数量决定了索引的并行度和数据分布,默认是一个
    • 副本数量(numberofreplicas):副本提高了数据的可用性和容错能力,默认是一个
  • 映射(mappings)

    • 字段属性(properties):定义索引中文档的字段及其类型。常用的字段包括:text,keyword,integer,float,date等。示例:
    {"properties":{"field_name1":{"type":"text",……},"field_name2":{"type":"integer",……}}
    }
    

删除索引

DELETE /index_name

修改索引

PUT /index_name_settings

修改的时候,带上要修改的跟设置相关的参数,例如:

PUT /index_name_settings
{"index": {"number_of_replicas": 2}
}

PUT /index_name/_mapping

修改的时候(添加新字段),带上要修改的跟映射相关的参数,例如:

PUT /index_name/_mapping
{"properties":{"content":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"}}
}

索引库和mapping一旦创建就无法更改,但是可以添加新的字段。

索引别名

aliases是一个和setting和mapping同级的参数,用于指定索引的别名。例如:

PUT /index2/_mapping
{"aliases":{"index_alias":{}}"properties":{"content":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"}}
}

如此,就创建了一个索引index2,index2这个索引就有了index_alias这个别名

也可以给现有的索引增加一个别名:

POST /_aliases
{"actions":[{"add":{"index":"my_index","aliases":"my_index_alias"}}]
}

如此,现有的my_index索引就有了一个别名叫做my_index_alias

索引别名有什么作用呢?

如果要多索引检索,即同时检索多个索引,有两种方案:

  • 使用逗号对多个索引名称进程分隔

    POST index1,index2,index3,index4/_search
    
  • 使用通配符的方式

    POST index*/_search
    

这种方式会有很大的局限性,建议使用别名的方式

使用别名的方式:

  • 使用别名关联已有索引

    POST /_aliases
    {"actions":[{"add":{"index":"my_index1","aliases":"my_index_alias"}},{"add":{"index":"my_index2","aliases":"my_index_alias"}},{"add":{"index":"my_index3","aliases":"my_index_alias"}}]
    }
    

查询的时候,GET my_index_alias 就会查询关联这个别名的所有的表

若索引和和别名指向相同,则在相同检索条件下的检索效率是一致的,因为索引别名只是物理索引的软链接的名称而已。

对相同索引别名的物理索引建议由一致的映射,以提升检索效率

推荐充分发挥索引别名在检索方面的优势,但是在写入和更新的时候还要使用物理索引。

mapping的字段的属性

mapping是对索引库中文档的约束,常见的mapping的字段的属性包括:

  • type:字段数据类型,创建的类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址,不可分词,是一个不可分割的整体)
    • 数值:long、integer、short、byte、double、float
    • 布尔:boolean
    • 日期:date
    • 对象:object

如果要使用数组的形式,type直接指定数组元素的类型即可,数据值也是以[xxx,xxx,xxx,……]数组的形式。

  • index:是否创建索引,给了值为true,就会为其创建倒排索引
  • analyzer:使用那种分词器,结合text类型的字段使用
  • properties:该字段的子字段
  • copy_to:可以将该字段的属性值拼接到指定哪个属性中

文档操作

新增文档

新增文档的DSL语法:

POST /索引库名/_doc/文档id
{"字段1":"值1","字段2":{"子属性1":"值2","子属性2":"值3"}
}

如果不指定新建的文档id,就会自动随机生成一个id,这种情况显然不好。建议在新增文档的时候,加上指定的文档id。

查看文档

GET /索引库名/_doc/文档id

删除文档

DELETE /索引库名/_doc/文档id

更新文档

方式一:全量修改,会删除旧文档,添加新文档

PUT /索引库名/_doc/文档id
{"字段1":"值1","字段2":{"子属性1":"值2","子属性2":"值3"}
}

如果文档id对应的文档存在,就删除旧文档,添加新文档。如果不存在,就直接增加一个新文档。

方式二:增量修改,修改指定字段值。

POST /索引库名/_update/文档id
{"doc":{"字段1":"值1","字段2":{"子属性1":"值2","子属性2":"值3"}}
}

这种方式只会修改一条文档中的指定的字段值

DSL查询文档

DSL Query的分类

ElasticSearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:

  • 查询所有:查询出所有数据。match_all
  • 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引中匹配。
  • 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型的字段。
  • 地理(geo)查询:根据经纬度查询。
  • 复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。

DSL Query查询语法

查询的基本语法如下:

GET /index_name/_search
{"query":{"查询类型":{"查询条件":"条件值"}}
}

全文检索查询

全文检索查询,会对用户的输入内容进行分词,常用于搜索框搜索

  • match查询:全文检索查询中的一种,会对用户输入内容进行分词,然后去倒排索引库检索。

    GET /index_name/_search
    {"query":{"match":{"字段名":"搜索内容"}}
    }
    
  • multi_match:与match查询类似,只不过允许同时查询多个字段

    GET /hotel/_search
    {"query":{"multi_match":{"query":"搜索词","fields":["字段1","字段2","字段3",……]}}
    }
    

推荐使用copy_to将要共同查询的字段汇总到一个总字段之后,使用match查询。使用multi_match查询的时候,参与查询的字段越多,查询性能越差。

精确查询

精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的方式有:

  • term:根据词条精确值查询

    GET /index_name/_search
    {"query":{"term":{"字段名":{"value":"搜索值"}}}
    }
    
  • range:根据值的范围查询

    GET /index_name/_search
    {"query":{"range":{"字段名":{"gte":"最小值","lte":"最大值"}}}
    }
    
    • gte是表示大于等于,如果只想表示大于,使用gt
    • lte是表示小于等于,如果只想表示小于,使用lt

地理查询

根据经纬度查询。常见方式有:

  • geo_bounding_box:查询geo_point值落在某个矩形范围内的所有文档

    GET /index_name/_search
    {"query":{"geo_bounding_box":{"字段名":{"top_left":{ //左上角"lat":"纬度值","lon":"经度值"},"bottom_right":{ //右下角"lat":"纬度值","lon":"经度值"}}}}
    }
    
    • top_left属性就是矩形左上角的点的经纬度
    • bottom_right属性就是矩形右下角的点的经纬度
  • geo_distance:查询到指定中心点小于某个距离值的所有文档

    GET /index_name/_search
    {"query":{"geo_distance":{"distance":"距离(直接以m、km等等为距离单位即可)""字段名":"字段值"}}
    }
    

复合查询

符合查询可以将其他简单查询组合起来,实现更复杂的搜索逻辑。

function score:算分函数查询,可以控制文档相关性算分,控制文档排名

相关性算分:当我利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果的时候按照分值降序排列。TF(词条频率)= 词条出现次数/文档中词条总数。还有一个TF-IDF算法,即也考虑逆文档频率,逆文档频率 = log(文档总数/包含词条的文档总数)。但是目前使用的是更高级的BM25算法,这种算法不会受词频影响较大,得分增长比较平缓,后面趋于水平。

Function Score Query

使用function score query,可以修改文档的相关性算分(query score),根据新得到的算分排序。

GET /index_name/_search
{"query":{"function_score":{//原始查询条件,搜索文档并根据相关性打分(query_score)"query":{"查询类型":{"字段名":"搜索内容"}},"function":[{"filter":{"查询类型":{"字段名":"字段值"}},"算分函数":……}],"boost_mode":"multiply" //加权模式}}
}

算分函数,算分函数的结果称为function score,将来会与query score运算,得到新的算分,常见的有:

  • weight:给一个常量值,作为函数结果(function score)
  • field_value_factor:用文档中的某个字段值作为函数结果
  • random_score:随机生成一个值,作为函数结果
  • script_score:自定义计算公式,公式结果作为函数结果

加权模式,定义function score与query score的运算方式,包括:

  • multiply:两者相乘。默认就是这个运算方式。
  • replace:用function score替换这个query score
  • 其他:sum、avg、max、min

Boolean Query

布尔查询是一个或者多个查询子句的组合,子查询的组合方式有:

  • must:必须匹配每个子查询,类似于“与”
  • should:选择性匹配子查询,类似于“或”
  • must_not:必须不匹配,不参与算分,类似于“非”
  • filter:必须匹配,不参与算分

语法:

GET /hotel/_search
{"query":{"bool":{"must":[{"查询方式":{"字段名":"字段值"}},{"查询方式":{"字段名":"字段值"}},……],"should":[{"查询方式":{"字段名":"字段值"}},{"查询方式":{"字段名":"字段值"}},……],"must_not":[{"查询方式":{"字段名":"字段值"}},……],"filter":[{"查询方式":{"字段名":"字段值"}},……]}}
}

搜索结果处理

排序

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序的字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

GET /index_name/_search
{//正常搜索内容部分"query":{"搜索方式":{"字段名":"搜索内容"}},"sort":[{"字段名":"排序方式(desc/asc)"},{"字段名":"排序方式(desc/asc)"},……]
}

sort数组中的一个元素代表根据一个字段排序及其排序方式,排序的优先级是按数组中的顺序,从前到后,依次递减。

如果是地理坐标类型的数据,sort数组里面的元素的写法要有些区别:

"sort":[{"_geo_distance":{"字段名":"经度值,纬度值",  //坐标"order":"asc",  //排序方式"unit":"km"  //单位}},……]

分页

elasticsearch默认情况下只返回top10的数据,如果要查询更多数据,就需要修改分页参数了。

elasticsearch中通过修改from、size参数来控制要返回的分页结果:

GET /hotel/_search
{"query":{"搜索方式":{"字段名":"搜索内容"}},"from":60,  //分页开始的位置如果想第n页,值为(n-1)*size"size":20
}

深度分页问题

es分页是选取从第一个到指定分页中最后一个的所有数据,再把前面的不要的数据丢掉,在集群的时候,就会面临问题。

es一般是分布式的,所以会面临深度分页问题。比如要搜数据中的前一千条,就是从每个集群中搜索指定的一千条数据,再汇总这些每个一千条,从汇总中取出新的前一千条。

深度分页,es提供了俩种解决方案:

search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。

scroll:将排序数据形成快照,保存在内存中。官方已经不推荐使用。

高亮

高亮就是在搜索结果中把搜索关键字突出显示。

语法:

GET /hotel/_search
{"query":{"搜索方式":{"字段名":"搜索内容"}},"highlight":{"fields":{"字段名":{"pre_tags":"<em>",  //用来标记高亮字段的前置标签"post_tags":"</em>" //用来标记高亮字段的后置标签 },"字段名2":{"pre_tags":"<em>",   //用来标记高亮字段的前置标签"post_tags":"</em>", //用来标记高亮字段的后置标签 "require_field_match":"false"}}}
}

但是默认情况下,es搜索字段必须要和高亮字段一致,如果不是一致的字段,要加上require_field_match配置项

数据聚合

聚合(aggregations)可以实现对文档数据的统计、分析、运算。常见的聚合有三类:

  • 桶(Bucket)聚合:用来对文档做分组。常见的有以下两种聚合类型

    • TermAggregation:按照文档字段值进行分组。
    • DateHistogram:按照日期阶梯进行分组,例如一周为一组,或者一个月为一组。

    DSL语法:

    GET /hotel/_search
    {"size":0, //定义size为零,结果中不包含文档,只包含聚合结果"aggs":{"聚合名":{  //给聚合自定义一个名字 "聚合类型":{"field":"字段名","size":20 //希望聚合的结果的数量}}}
    }
    

    默认情况下,Bucket聚合会统计Bucket内的文档数量,记为_count,并且按照_count降序排序。

    可以加一个order字段,进行修改结果排序方式:

    GET /hotel/_search
    {"size":0, //定义size为零,结果中不包含文档,只包含聚合结果"aggs":{"聚合名":{  //给聚合自定义一个名字 "聚合类型(同查询类型)":{"field":"字段名","order":{"字段名":"asc/desc"  //排序方式},"size":20 //希望聚合的结果的数量}}}
    }
    

    默认情况下,Bucket聚合是对索引库中的所有的文档做聚合,我们可以限定文档要聚合的文档范围,只要添加query条件即可:

    GET /hotel/_search
    {"query":{"搜索方式":{"字段名":"搜索内容"}},"size":0, //定义size为零,结果中不包含文档,只包含聚合结果"aggs":{"聚合名":{  //给聚合自定义一个名字 "聚合类型(同查询类型)":{"field":"字段名","order":{"字段名":"asc/desc"  //排序方式},"size":20 //希望聚合的结果的数量}}}
    }
    
  • 度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等。

    • Avg:求平均值
    • Max:求最大值
    • Min:求最小值
    • Stats:同时求Max、Min、Avg、Sum等。
    GET /hotel/_search
    {"size":0, //定义size为零,结果中不包含文档,只包含聚合结果"aggs":{"聚合名":{  //给聚合自定义一个名字 "聚合类型(同查询类型)":{"field":"字段名","size":20 //希望聚合的结果的数量}"aggs":{  //是上一层聚合的子聚合,也就是分组后对每组进行计算"聚合名称":{  //子聚合名称"stats":{  //metric聚合类型"field":"字段名"  //要聚合的字段}}}}}
    }
    

    排序的时候,就可以使用子聚合中的结果来排序,直接将子聚合的聚合名作为字段名即可,使用stats的时候,就使用 子聚合名.avg 、 子聚合名.min 等等作为字段名即可。

  • 管道(pipeline)聚合:其他聚合的结果为基础再做聚合。

自动补全

自定义分词器

elasticsearch中分词器(analyzer)的组成包含三部分:

  • character filter:在tokenizer之前对文本进行处理。例如删除字符、替换字符。
  • tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
  • tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等。

要实现在拼音分词的之前,能先进行ik分词,可以在创建索引的时候,通过settings来配置自定义的analyzer分词器,而且,也要设置一些拼音分词器的参数,让其能达到更好的拼音分词效果:

PUT /test
{"settings":{"analysis":{"analyzer":{ //用于创建自定义分词"自定义分词器名称":{"tokenizer":"ik_max_word","filter":"pinyin"}},"filter": { // 自定义tokenizer filter"py": { // 自定义过滤器名称"type": "pinyin", // 过滤器类型,这里是pinyin"keep_full_pinyin": false,"keep_joined_full_pinyin": true,"keep_original": true,"limit_first_letter_length": 16,"remove_duplicated_term": true,"none_chinese_pinyin_tokenize": false}}}},"mappings":{"properties":{"字段名":{"type":"字段类型","analyzer":"my_analyzer","search_analyzer":"ik_smart"}}}
}

因为不同的同音中文对应的拼音会一致,为了防止搜出同音词,应该要在倒排索引的时候使用拼音分词器,而搜索的时候不应该使用拼音分词器。要再添加一个search_analyzer,设置为ik_smart,如此搜索的时候,就会使用search_analyzer配置的分词器而不会走analyzer的分词器。

自动补全查询

completion suggester查询

这个查询会匹配用户输入内容开头的词条并返回。为了提高补全查询的效率,对于文档中的字段的类型有一些约束:

  • 参与补全查询的字段必须是completion类型。
  • 字段的内容一般是用来补全的多个词条形成的数组。

查询语法如下:

GET /test/_search
{"suggest":{"title_suggest":{"text":"搜索内容","completion":{"field":"title",  //补全查询的字段"skip_duplicates":true,"size":10}}}
}

可以在后端的业务里,将要进行自动补全查询的字段,加到一个list里面,再将这个list赋值给这个自动补全查询字段

数据同步

当关于某个实体的数据更新或者增加的时候,当然在es和数据库中的数据都要做相应的数据更新。

  • 方案一:同步调用
    • 即先更新数据库,数据库中更新完数据之后,再调用更新索引中的文档的接口,更新es中的数据。
    • 优点:简单
    • 缺点:耦合度高
  • 方案二:异步调用
    • 更新数据库的操作时候,发送一条消息给mq,mq监听消息之后,调用更新索引的文档的接口,更新es中的数据。
    • 优点:低耦合
    • 缺点:依赖mq的可靠性
  • 方案三:监听binlog
    • 使用一些中间件,比如canal,监听binlog,来监听mysql中的增删改操作,当相应的数据发生改变的时候,触发相应的更新es中的操作。
    • 优点:完全解除服务间的耦合
    • 缺点:增加数据库负担

SpringBoot整合ElasticSearch(Elasticsearch Java API Client操作索引库)

初始化

maven导入依赖:

<!--        elasticsearch的导入--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>

配置类配置基本的es连接信息(记得Java这里的import不要导入错了依赖)

yaml:

elasticsearch:host: xxx.xxx.xxx.xxx #主机地址port: 9200 #es端口scheme: http #协议

连接信息配置类:

@ConfigurationProperties(prefix = "elasticsearch")
@Component
@Data
public class EsProperties {private String host;private Integer port;private String scheme;
}

es操作的组件的配置类:

builder中的new的HttpHost对象就是在配置好client的连接信息。

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import jakarta.annotation.Resource;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ElasticSearchConfig {@Resourceprivate EsProperties esProperties;@Beanpublic ElasticsearchClient esClient() {RestClient restClient = RestClient.builder(new HttpHost(esProperties.getHost(),esProperties.getPort(),esProperties.getScheme())).build();  ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());return new ElasticsearchClient(transport);  }
}

索引操作

由于我感觉,索引操作好像一般不会放在springboot中进行,一般进行的应该都是文档操作之类的,就简单展示一下索引操作的Java代码:

// 索引名字  
String indexName = "student"; // 索引是否存在  
BooleanResponse books = esClient.indices().exists(e -> e.index(indexName));  
System.out.println("索引是否存在:" + books.value());  // 创建索引  
esClient.indices().create(c -> c  .index(indexName)  .mappings(mappings -> mappings  // 映射  .properties("name", p -> p  .text(t -> t // text类型,index=false  .index(false)  )  )  .properties("age", p -> p  .long_(t -> t) // long类型 )  )  
); // 删除索引  
esClient.indices().delete(d -> d.index(indexName));

esClient就是上面的配置文件中配置的ioc容器中的ElasticSearchClient类型的组件,直接使用autowired或者resource注解注入即可。

文档操作

新增:

// 新增
CreateResponse createResponse = esClient.create(c -> c  .index(indexName) // 索引名字  .id(account.getId()) // id  .document(account) // 实体类  
);

我使用的是es8的方式,使用ElasticsearchClient进行操作,这种方式中有着较多的lambda的写法。且会有多层调用,可以像我这种写法,能将参数分析清楚。

删除:

DeleteResponse deleteResp = esClient.delete(d -> d.index(indexName).id("1"));

批量新增:

List<Account> accountList = ...
BulkRequest.Builder br = new BulkRequest.Builder();  
for (Account acc : accountList) {  br.operations(op -> op  .create(c -> c  .index(indexName)  .id(acc.getId())  .document(acc)  )  );  
}  
BulkResponse bulkResp = esClient.bulk(br.build());

批量新增操作要使用到Bulk。

根据id查找:

      	GetResponse<ArticleVo> getResp = esClient.get(g ->g.index("article_index1").id("17770146669-1732611392477"), ArticleVo.class);if (getResp.found()) {ArticleVo source = getResp.source();  // 这就是得到的实体类source.setArticleId(getResp.id());System.out.println(source);}

高亮、分页、排序查找:

三个知识点我就放在一起了,其实和普通的DSL的写法大致,只要DSL掌握的好,再按这个结果写,写起来会很简单。

    public ResultData<List<ArticleESVo>> searchArticlePage(String message, Integer pageSize, Integer pageNum) throws IOException {SearchResponse<ArticleESVo> search = esClient.search(s -> s.index("article_index1")  //指明索引.from((pageNum-1) * pageSize)  //分页开始数.size(pageSize)  //每页的页大小.sort(so ->so  //排序配置.field(f -> f.field("publicTimeView") //排序字段.order(SortOrder.Desc).field("likes")  //排序字段.order(SortOrder.Desc))).query(q -> q  //查询配置.match(t -> t.field("all")  //查询字段,这里直接查all.query(message)      //用户的查询内容)).highlight(h ->h  //高光配置.preTags("<span color='red'>")  //高光部分的前置标签.postTags("</span>")  //高光部分的后置标签.fields("title",hi ->hi)    //要高光的字段.fields("mainContent",hi2->hi2)   //要高光的字段.requireFieldMatch(false)   //设置为不需要匹配查询字段也行), ArticleESVo.class);List<ArticleESVo> articleESVoList = new ArrayList<>();System.out.println(search + "es给回来的数据数据是这样子的");List<Hit<ArticleESVo>> hits = search.hits().hits();  //目标实体的数据都在这里面,但是还是存在一个Hit对象里面,高光和普通数据,都要从这个hit里面获取。for (Hit<ArticleESVo> hit : hits) {List<String> listTitleHighLight = hit.highlight().get("title");  //获取高光部分的内容List<String> listMainContentHighLight = hit.highlight().get("mainContent");  //获取高光部分的内容ArticleESVo articleESVo = hit.source();if (listTitleHighLight != null){articleESVo.setTitle(listTitleHighLight.get(0));  //如果标题有高光,就替换掉}if (listMainContentHighLight != null){articleESVo.setMainContent(listMainContentHighLight.get(0));  //如果文章摘要有高光,就替换掉}articleESVoList.add(articleESVo);  //将目标数据加到要返回的集合中}System.out.println(articleESVoList + " 最终结果");return ResultData.success(articleESVoList);}

高光字段是存在hit参数里面的highlight参数里面的,通过get方法指定高光处理后的字段值,再覆盖掉原来source中获取的实体类的相应字段(source中的实体对象中的字段信息是没有经过高光处理的),如果没有指定的高光信息,就不能赋值过去的,不然会报空指针异常,所以要先进行一个判断。

自动补全:

    public ResultData<List<String>> suggestSearch(String message) throws IOException {SearchResponse<ArticleESVo> search = esClient.search(s -> s.index("article_index").suggest(sug -> sug.suggesters("suggest_article", fs -> fs.text(message).completion(te -> te.field("suggestion").skipDuplicates(true).size(10)))), ArticleESVo.class);List<String> list = new ArrayList<>();System.out.println(search + "推荐结果");Suggestion<ArticleESVo> suggest_article = search.suggest().get("suggest_article").get(0);List<CompletionSuggestOption<ArticleESVo>> options = suggest_article.completion().options();for (CompletionSuggestOption<ArticleESVo> option : options) {String text = option.text();list.add(text);}return ResultData.success(list);}

可以看出,自动补全的Elasticsearch Java API Client的写法看起来和DSL的写法非常相似,可见,掌握好DSL的语法,对使用Elasticsearch Java API Client非常有帮助。我这里的写法是用一个String类型的集合,存储所有的自动补全词条,再将其返回给前端。

相关文章:

ElasticSearch的学习

介绍 ElasticSearch&#xff08;简称ES&#xff09;是一个开源的分布式搜索和数据分析引擎&#xff0c;是用Java开发并且是当前最流行的开源的企业级搜索引擎&#xff0c;能够达到近实时搜索&#xff0c;它专门设计用于处理大规模的文本数据和实现高性能的全文检索。 Elastic…...

机器学习6-梯度下降法

梯度下降法 目的 梯度下降法(Gradient Descent)是一个算法&#xff0c;但不是像多元线性回归那样是一个具体做回归任务的算法&#xff0c;而是一个非常通用的优化算法来帮助一些机器学习算法求解出最优解的&#xff0c;所谓的通用就是很多机器学习算法都是用它&#xff0c;甚…...

算法之旅:LeetCode 拓扑排序由简入繁完全攻略

前言 欢迎来到我的算法探索博客&#xff0c;在这里&#xff0c;我将通过解析精选的LeetCode题目&#xff0c;与您分享深刻的解题思路、多元化的解决方案以及宝贵的实战经验&#xff0c;旨在帮助每一位读者提升编程技能&#xff0c;领略算法之美。 &#x1f449;更多高频有趣Lee…...

vue3项目中使用星火API

在node环境epxress中使用讯飞ai接口进行二次封装&#xff0c;通过ai对话回复提取&#xff0c;获得ai提取的文章摘要 本文章只是简单使用&#xff0c;更复杂功能比如调用星火API制作对话机器人可以查看文档&#xff0c;对于初次使用星火AI接口或许有帮助 讯飞星火大模型API-大模…...

蓝桥杯第 23 场 小白入门赛

一、前言 好久没打蓝桥杯官网上的比赛了&#xff0c;回来感受一下&#xff0c;这难度区分度还是挺大的 二、题目总览 三、具体题目 3.1 1. 三体时间【算法赛】 思路 额...签到题 我的代码 // Problem: 1. 三体时间【算法赛】 // Contest: Lanqiao - 第 23 场 小白入门赛 …...

Cause: java.sql.SQLException: No value specified for parameter 4

问题 执行更新sql时报错&#xff0c;异常栈如下 org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.SQLException: No value specified for parameter 4 ### The error may exist in com/my/mapper/MyMapper.java (best gue…...

第五课 Unity资源导入工作流效率优化(AssetGraph工具)

上期我们学习了简单的animation动画的优化&#xff0c;接下来我们继续资源导入效率的优化 工程目录 首先我们来学习一下工程目录结构及用途 Asset文件夹&#xff1a;用来储存和重用的项目资产 Library文件夹&#xff1a;用来储存项目内部资产数据信息的目录 Packages文件夹…...

create-vue创建vue3项目

create-vue是Vue官方新的脚手架工具 前提条件&#xff1a; 已安装16.0或更高版本的Node.js &#xff08;node -v查看&#xff09; 创建一个Vue应用 npm init vuelatest 这一指令会帮我们安装并执行create-vue cd vue-project npm install —— 安装依赖 npm run dev...

27 基于51单片机的方向盘模拟系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用两个MPX4115压力传感器作为两路压力到位开关电路&#xff0c; 采用滑动变阻器连接数模转换器模拟重力加速度传感器电路&#xff1b; 一个按键控制LED灯的点亮与…...

HarmonyOS

UIAbility UIAbility 组件是一种包含UI的应用组件&#xff0c;主要用于和用户交互 设计理念&#xff1a;原生支持应用组件的跨端迁移和多端协同、支持多设备和多窗口的形态 UIAbility组件是系统调度的基本单位&#xff0c;为应用提供绘制界面的窗口。 /** 为使应用能够正常使用…...

字符串处理(二)

第1题 篮球比赛 查看测评数据信息 学校举行篮球比赛&#xff0c;请设计一个计分系统统计KIN、WIN两队分数&#xff0c;并输出分数和结果&#xff01; 如果平分就输出‘GOOD’&#xff0c;否则输出获胜队名&#xff01; 输入格式 输入数据共n1行&#xff0c; 第1行n&#xf…...

达梦数据库文件故障的恢复方法

目录 1、概述 1.1 概述 1.2 环境介绍 2、使用备份集的恢复方法 2.1 实验准备 2.2 误删除“用户表空间数据文件” 2.3 误删除SYSTEM.DBF 2.4 误删除ROLL.DBF 2.5 REDO日志文件 3、无备份集的恢复方法 3.1 误删除“表空间数据文件” 3.2误删除控制文件 3.3 误删除RO…...

Redis(5):哨兵

一、作用和架构 1. 作用 在介绍哨兵之前&#xff0c;首先从宏观角度回顾一下Redis实现高可用相关的技术。它们包括&#xff1a;持久化、复制、哨兵和集群&#xff0c;其主要作用和解决的问题是&#xff1a; 1&#xff09;持久化&#xff1a;持久化是最简单的高可用方法(有时甚…...

准确--在 AlmaLinux 9.2 上快速搭建 FTP 服务器

FTP 服务器配置与验证完整步骤 以下内容是针对在 192.168.6.101 配置 FTP 服务器&#xff0c;端口为 59999 的完整详细操作步骤&#xff0c;包括配置与验证。每个步骤都附有详细注释。 配置 FTP 服务器 1. 安装 vsftpd 根据系统类型&#xff0c;执行以下命令安装 FTP 服务&a…...

Monitor 显示器软件开发设计入门二

基础篇--显示驱动方案输出接口介绍 写在前面&#xff1a;首先申明&#xff0c;这篇文章是写给那些初入显示器软件行业的入门者&#xff0c;或是对显示器没有基本知识的小白人员。如您是行业大咖大神&#xff0c;可以绕行&#xff0c;可看后期进阶文章。 上篇介绍了输入接口及相…...

MySQL 数据库学习教程一:开启数据库探索之旅

在当今数字化时代&#xff0c;数据已然成为企业和组织最为宝贵的资产之一。而数据库管理系统则是存储、管理和操作这些数据的核心工具。MySQL 作为一款广泛应用的开源关系型数据库管理系统&#xff0c;以其可靠性、高性能和易用性而备受青睐。如果你渴望踏入数据库领域&#xf…...

课程答疑微信小程序设计与实现

私信我获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 课程答疑微信小程序设计与实现 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了课程答疑微信小程序设计与实现的开发全过程。通过分析…...

基于yolov8、yolov5的铝材缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;铝材缺陷检测在现代工业生产和质量管理中具有重要意义&#xff0c;不仅能帮助企业实时监控铝材质量&#xff0c;还为智能化生产系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的铝材缺陷检测模型&#xff0c;该模型使用了大量包含…...

docker 僵尸进程问题

docker僵尸进程 子进程结束后&#xff0c;父进程没有回收该进程资源&#xff08;父进程可能没有wait&#xff09;&#xff0c;子进程残留资源存放与内核中&#xff0c;就变为僵尸进程(zombie) 场景分析&#xff1a;python脚本A中执行B应用&#xff0c;将A部署在docker中&#…...

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 2.0 用户登录功能 3.0 用户管理功能 4.0 影院管理功能 5.0 电影管理功能 6.0 影厅管理功能 7.0 电影排片管理功能 8.0 用户评论管理功能 9.0 用户购票功…...

webpack5 的五大核心配置(二)

webpack主要构成部分&#xff1a; entry 入口output 出口loaders 转化器plugins 插件mode 模式devServer 开发服务器 webpack.config.js 配置文件基本格式 module.exports{//入口文件entry:{},//出口文件output:{},//module rules loadersmodule{};//插件plugins:[],//开发…...

Python语法基础(四)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 高阶函数之map 高阶函数就是说&#xff0c;A函数作为B函数的参数&#xff0c;B函数就是高阶函数 map&#xff1a;映射 map(func,iterable) 这个是map的基本语法&#xff0c;…...

UnityShader——初级篇之开始Unity Shader学习之旅

开始Unity Shader学习之旅 一个最简单的顶点/片元着色器顶点/片元着色器的基本结构模型数据从哪里来顶点着色器和片元着色器之间如何通信如何使用属性 强大的援手&#xff1a;Unity 提供的内置文件和变量内置的包含文件内置的变量 Unity 提供的 Cg/HLSL 语义什么是语义Unity 支…...

Burp入门(6)-自动化漏洞测试理论

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技术分享&#xff0c;所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险&#xff0c;并遵循相关法律法规。 感谢泷…...

【git】git 客户端设置本地缓冲区大小

文章目录 1. 报错2. 解决&#xff1a;增加本地缓冲区 1. 报错 git -c core.quotepathfalse -c log.showSignaturefalse push --progress --porcelain origin refs/heads/master:master Enumerating objects: 17, done. Counting objects: 5% (1/17) Counting objects: 11% …...

Xcode——LLDB Debugger 与断点调试学习

Xcode——LLDB Debugger 与断点调试学习 文章目录 Xcode——LLDB Debugger 与断点调试学习前言介绍打开LLDB命令helpprintexpression 断点调试异常断点——Exception Breakpoint标志断点——Symbolic Breakpointwatchpointset 断点行为condition条件判断 最后参考文章 前言 在…...

linux安全管理-系统环境安全

1 历史命令设置 1、检查内容 检查操作系统的历史命令设置。 2、配置要求 建议操作系统的历史命令设置。 3、配置方法 编辑/etc/profile 文件&#xff0c;配置保留历史命令的条数 HISTSIZE 和保留历史命令的记录文件大小 HISTFILESIZE&#xff0c;这两个都设置为 5。 配置方法如…...

【Maven】依赖冲突如何解决?

准备工作 1、创建一个空工程 maven_dependency_conflict_demo&#xff0c;在 maven_dependency_conflict_demo 创建不同的 Maven 工程模块&#xff0c;用于演示本文的一些点。 什么是依赖冲突&#xff1f; 当引入同一个依赖的多个不同版本时&#xff0c;就会发生依赖冲突。…...

学习视频超分辨率扩散模型中的空间适应和时间相干性(原文翻译)

文章目录 摘要1. Introduction2. Related Work3. Our Approach3.1. Video Upscaler3.2. Spatial Feature Adaptation Module3.3. Temporal Feature Alignment Module3.4. Video Refiner3.5. Training Strategy 4. Experiments4.1. Experimental Settings4.2. Comparisons with …...

MFC工控项目实例三十四模拟量实时监控数字显示效果

点击监控按钮&#xff0c;对选中模拟量用数字显示效果实时显示数值。 SenSet.cpp中相关代码 UINT m_nCounterID_1[6] { IDC_STATIC0,IDC_STATIC1,IDC_STATIC2,IDC_STATIC3,IDC_STATIC4,IDC_STATIC5,};UINT m_nCounterID_2[7] { IDC_STATIC7,IDC_STATIC8,IDC_STATIC9,IDC_S…...

Z2400032基于Java+Mysql+SSM的校园在线点餐系统的设计与实现 代码 论文

在线点餐系统 1.项目描述2. 技术栈3. 项目结构后端前端 4. 功能模块5. 项目实现步骤注意事项 6.界面展示7.源码获取 1.项目描述 本项目旨在开发一个校园在线点餐系统&#xff0c;通过前后端分离的方式&#xff0c;为在校学生提供便捷的餐厅点餐服务&#xff0c;同时方便餐厅和…...

Linux Deploy安装Debian桌面

下载安装Linux Deploy 下载地址 https://github.com/lateautumn233/Linuxdeploy-Pro/releases/download/3.1.0/app-debug.apk 配置 发行版本&#xff1a;Debian架构&#xff1a;arm64发行版版本&#xff1a;bookworm源地址&#xff1a;http://mirrors.aliyun.com/debian/安装…...

C语言数据相关知识:静态数据、越界与溢出

1、静态数组 在 C 语言中&#xff0c;数组一旦被定义后&#xff0c;占用的内存空间就是固定的&#xff0c;容量就是不可改变的&#xff0c;既不能在任何位置插入元素&#xff0c;也不能在任何位置删除元素&#xff0c;只能读取和修改元素&#xff0c;我们将这样的数组称为静态…...

纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架

前言​ 开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C 库&#xff0c;如 ​​OpenCV​​ 或 ​​​dlib​​​&#xff0c;但通过 ​​​cgo​​​ 调用 C 程序会引入巨大的延迟&#xff0c;并在性能方面产生显著的权衡。…...

华为ACL应用笔记

1、基本ACL 2000-2999 基本ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种网络安全技术&#xff0c;它根据源IP地址、分片信息和生效时间段等信息来定义规则&#xff0c;对报文进行过滤。 规则&#xff1a; ACL由一系列规则组成&#xff0c;每…...

Axios:现代JavaScript HTTP客户端

在当今的Web开发中&#xff0c;与后端服务进行数据交换是必不可少的。Axios是一个基于Promise的HTTP客户端&#xff0c;用于浏览器和node.js&#xff0c;它提供了一个简单的API来执行HTTP请求。本文将介绍Axios的基本概念、优势、安装方法、基本用法以及如何使用Axios下载文件。…...

Qml-TabBar类使用

Qml-TabBar类使用 TabBar的概述 TabBar继承于Container 由TabButton进行填充&#xff0c;可以与提供currentIndex属性的任何容器或布局控件一起使用&#xff0c;如StackLayout 或 SwipeView&#xff1b;contentHeight : real:TabBar的内容高度&#xff0c;用于计算标签栏的隐…...

qt QGraphicsEllipseItem详解

1、概述 QGraphicsEllipseItem是Qt框架中QGraphicsItem的一个子类&#xff0c;它提供了一个可以添加到QGraphicsScene中的椭圆项。QGraphicsEllipseItem表示一个带有填充和轮廓的椭圆&#xff0c;也可以用于表示椭圆段&#xff08;通过startAngle()和spanAngle()方法&#xff…...

单链表---移除链表元素

对于无头单向不循环链表&#xff0c;给出头结点head与数值val&#xff0c;删除链表中数据值val的所有结点 #define ListNodeDataType val struct ListNode { struct ListNode* psll;ListNodeDataType val; } 方法一---遍历删除 移除所有数值为val的链表结点&#xff0c;…...

Kafka知识体系

一、认识Kafka 1. kafka适用场景 消息系统&#xff1a;kafka不仅具备传统的系统解耦、流量削峰、缓冲、异步通信、可扩展性、可恢复性等功能&#xff0c;还有其他消息系统难以实现的消息顺序消费及消息回溯功能。 存储系统&#xff1a;kafka把消息持久化到磁盘上&#xff0c…...

Micopython与旋转按钮(Encoder)

一、 encoder.py文件 CLK pin attached to GPIO12DT pin attached to GPIO13GND pin attached to GND 旋转编码器s1->CLK s2->DTimport time from rotary_irq_esp import RotaryIRQ r = RotaryIRQ(pin_num_clk=12, #clk引脚 pin_num_dt=13, #dat…...

联想Lenovo SR650服务器硬件监控指标解读

随着企业IT架构的复杂性和业务需求的增长&#xff0c;服务器的稳定运行变得至关重要。联想Lenovo SR650服务器以其高性能和稳定性&#xff0c;在各类应用场景中发挥着关键作用。为了保障服务器的稳定运行&#xff0c;监控易作为一款专业的IT基础设施监控软件&#xff0c;为联想…...

RAG数据拆分之PDF

引言RAG数据简介PDF解析方法及工具代码实现总结 二、正文内容 引言 本文将介绍如何将RAG数据拆分至PDF格式&#xff0c;并探讨PDF解析的方法和工具&#xff0c;最后提供代码示例。 RAG数据简介 RAG&#xff08;关系型属性图&#xff09;是一种用于表示实体及其关系的图数据…...

基于STM32的传感器数据采集系统设计:Qt、RS485、Modbus Rtu协议(代码示例)

一、项目概述 项目目标与用途 本项目旨在设计并实现一个基于STM32F103RCT6微控制器的传感器数据采集系统。该系统通过多个传感器实时监测环境参数&#xff0c;并将采集到的数据传输至上位机进行处理和分析。系统的主要应用领域包括环境监测、工业控制、智能家居等。通过该系统…...

【计网不挂科】计算机网络——<34道经典简述题>特训

前言 大家好吖&#xff0c;欢迎来到 YY 滴计算机网络 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的计算机网络题目&#xff0c;供yy应对期中考试复习。大家可以参考 本章为分章节的习题内容题库&#x…...

Spring Web开发(请求)获取JOSN对象| 获取数据(Header)

大家好&#xff0c;我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…...

算法训练营day22(二叉树08:二叉搜索树的最近公共祖先,插入,删除)

第六章 二叉树part08 今日内容&#xff1a; ● 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点 详细布置 235. 二叉搜索树的最近公共祖先 相对于 二叉树的最近公共祖先 本题就简单一些了&#xff0c;因为 可以利用二叉搜索树的…...

【论文阅读】 Learning to Upsample by Learning to Sample

论文结构目录 一、之前的上采样器二、DySample概述三、不同上采样器比较四、整体架构五、设计过程&#xff08;1&#xff09;初步设计&#xff08;2&#xff09;第一次修改&#xff08;3&#xff09;第二次修改&#xff08;4&#xff09;第三次修改 六、DySample四种变体七、复…...

Android 图形系统之五:Gralloc

Gralloc (Graphics Allocator) 是 Android 系统中的关键组件之一&#xff0c;用于管理图形缓冲区的分配、映射以及处理。在 Android 的图形架构中&#xff0c;Gralloc 充当了 HAL (Hardware Abstraction Layer) 的一部分&#xff0c;为系统和硬件提供了通用的接口&#xff0c;使…...

【大数据学习 | Spark调优篇】Spark之内存调优

1. 内存的花费 1&#xff09;每个Java对象&#xff0c;都有一个对象头&#xff0c;会占用16个字节&#xff0c;主要是包括了一些对象的元信息&#xff0c;比如指向它的类的指针。如果一个对象本身很小&#xff0c;比如就包括了一个int类型的field&#xff0c;那么它的对象头实…...