ElasticSearch
01-ElasticSearch概述
ElasticSearch 是基于 Lucene 做了一些封装和增强
Elasticsearch是一个基于Apache Lucene™的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进性能最好的、功能最全的搜索引擎库。
但是,Lucene只是一个库。想要使用它,你必须使用ava来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
Elasticsearch也使用/ava开发并使用Luene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Elaticsearch,简称为es,es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es也使用ava开发并使用Luene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
ELK技术:elasticsearch+logstash+kibana
ElasticSearch vs Solr 总结
1、es基本是开箱即用( 解压就可以用!),非常简单。Solr安装略微复杂一丢丢!
2、Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能。
3、Solr 支持更多格式的数据,比如SON、XML、CSV,而 Elasticsearch 仅支持json文件格式.
4、Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑
5、Solr 查询快,但更新索引时慢( 即插入删除慢 ),用于电商等查询多的应用 ;
- ES建立索引快( 即查询慢),即实时性查询快,用于facebook新浪等搜索
- Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。
6、Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而 Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。、
02-ElasticSearch安装
下载地址:
ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
elasticsearch-analysis-ik: https://github.com/medcl/elasticsearch-analysis-ik/releases
cerebro: https://github.com/lmenezes/cerebro/releases
head插件: https://github.com/mobz/elasticsearch-head/archive/master.zip
目录组成
- bin 启动文件
- config 配置文件
- 1og4j2:日志配置文件
- jvm.options:java虚拟机相关的配置
- elasticsearch.yml:elasticsearch 的配置文件:默认 9200 端口,跨域
- lib 相关jar包
- logs 日志
- modules功能模块
- plugins插件
可视化界面 es head插件,需要把前端基本的环境安装完毕
1、下载地址: https://github.com/mobz/elasticsearch-head/
2、启动:
npm install
npm run start
3、连接测试发现,存在跨域问题:配置es
http.cors.enabled: true
http.cors.a11ow-origin: "*"
4、重启es服务器,然后再次连接
03-Kibana
Kibana /kiːˈbɑːnə/ 是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交与存储在Elasticsearch索引中的数据。
使用Kibana可以通过各种图表进行高级数据分析及展示。
Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard )实时显示Elasticsearch查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引监测。
官网:https://www.elastic.co/cn/kibana
注意点:Kibana 版本要和 Es 一致
04-ElasticSearch基础
elasticsearch是面向文档的:索引和搜索数据的最小单位是文档
关系行数据库和elasticsearch 客观的对比
RelationalDB | Elasticsearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | types |
行(rows) | documents |
字段(columns) | fields |
elasticsearch(集群)中可以包含多个索引[数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档[行),每个文档中又包含多个字段(列)。
elasticsearch在后台把每个索引划分成多个分片,每个分片可以在集群中的不同服务器间迁移
一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引->类型文档->ID,通过这个组合就能索引到某个具体的文档。 注意:ID不必是整数,实际上它是个字符串。
文档的属性
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含 key:value
- 层次型的,一个文档中包含自文档,复杂的逻辑实体是一个json对象,fastison进行自动转换
- 灵活的结构,文档不依赖预先定义的模式,对于字段是非常灵活的,有时候,可以忽略该字段,或者动态的添加一个新的字段
类型
- 类型是文档的逻辑容器。
- 类型中对于字段的定义称为映射,比如 name 映射为字符串类型。文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段。elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那elasticsearch会认为它是整型。但是elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射
索引
- 索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。
- 索引存储了映射类型的字段和其他设置,被存储到了各个分片上。
一个集群至少有一个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的。
如果创建索引,那么索引将会有个5个分片(primary shard,又称主分片)构成的,每一个主分片会有一个副本(replica shard ,又称复制分片)
一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。
倒排索引
elasticsearch使用的是一种称为倒排索引的结构,采用Luene倒排索引作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。
例如,现在有两个文档,每个文档包含如下内容:
study every day,good good up to forever # 文档1包含的内容
To forever,study every day,good good up # 文档2包含的内容
为了创建倒排索引,首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:
term | doc_1 | doc_2 |
---|---|---|
Study | √ | × |
To | × | × |
every | √ | √ |
forever | √ | √ |
day | √ | √ |
study | × | √ |
good | √ | √ |
every | √ | √ |
to | √ | × |
up | √ | √ |
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回
在elasticsearch中,索引被分为多个分片,每个分片是一个Lucene /'lusi:n/ 的索引。所以一个elasticsearch索引是由多个Lucene索引组成的。
05-IK分词器插件
分词概念:
把一段中文或者别的划分成一个个的关键字,在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作
默认的中文分词是将每个字看成一个词,在不符合使用场景时可以安装中文分词器ik来解决这个问题。
IK提供了两个分词算法:
- ik smart:最少切分
- ik max word:最细度划分
IK的安装:
- 地址:https://github.com/medcl/elasticsearch-analysis-ik
- 下载完毕之后,放入到elasticsearch 插件即可,目录:…/…/plugins/ik
- 重启观察ES,输入cmd命令:elasticsearch-plugin list,查看ik是否被加载
查看不同的分词效果:
设置分词词典:IKAnalyzer.cfg.xml
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext dict">kuang.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote ext dict">words location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote ext stopwords">words location</entry> -->
</properties>
06-ElasticSearch操作
Rest风格说明:
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。
它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基本Rest命令说明:
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档( 指定文档id ) |
POST | localhost:9200/索引名称/类型名称 | 创建文档( 随机文档id ) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档id |
POST | localhost:9200/索引名称/类型名称/ search | 查询所有数据 |
关于索引的基本操作:
- 创建一个索引: PUT /索引名/类型名/文档id {请求体}
- 指定字段的类型:PUT/索引名 {“mappings”:{“properties”:{“key字段”:{“type”:“字段類型”}}}
- 获取具体規則/信息:GET/索引名
- 查看默认的信息:PUT/索引名/_doc/文档id {请求体}
- 扩展: get _cat/ 可以获得es的当前的很多信息
- 删除索引:通过DELETE 命令实现删除,索引/文档记录
如果文档字段没有指定,那么es 就会默认配置字段类型,最好自定义!
关于文档的基本操作
1. 添加数据
PUT /kuangshen/user/1
{
"name":"狂神说"
"age" : 23 ,
"desc":“一顿操作猛如虎,一看工资2500”
"tags": ["技术宅","温暖","直男”]
}
2. 获取数据 GET
GET kuangshen/user/1
3. 更新数据 PUT(如果不传递值,就会被覆盖原数据为空)
PUT /kuangshen/user/3
{
"name":“李四233",
"age": 30,
"desc":"mmp,不知道如何形容",
"tags": ["靓女","旅游","唱歌"]
}
返回——"version":2,result:"updated",
4. 更新数据 POST _update(推荐的更新方式)
POST kuangshen/user/1/_update
{
"doc":{
"name":"狂神说Java"
}
}
5. 通过文档id查询
GET kuangshen/user/1
6. 简单的条件查询,可以根据默认的映射规则,产生基本的查询
GET kuangshen/user/_search?q=name:狂神说
7. 复杂操作搜索select(排序,分页,高亮,模糊查询,精准查询),查询的参数体使用Json构建
GET kuangshen/user/_search
{
"query":{
"match":{
"name":"狂神"
}
}
}
返回的hit对象:
1.索引和文栏的信息
2.查询的结果总数
3.查询出来的具体的文档(遍历数据)
4.分数: 可以通过分数来判断谁更加符合结果
8. 过滤输出的结果字段
GET kuangshen/user/_search
{
"query":{
"match":{
"name":"狂神"
}
},
"source": ["name" ,"desc"]
}
返回——
"source":
{
"name”:"狂神说Java",
"desc”:"-顿操作墅如虎,一看工资2500"
}
9. 排序
GET kuangshen/user/_search
{
"query":{
"match":{
"name":"狂神"
}
},
"sort": [
{
"age":{
"order":"asc"
}
}
]
}
10. 分页(from:从第几个数据开始,size:返回多少条数据( 单页面的数据 ))
GET kuangshen/user/_search
{
"query":{
"match":{
"name":"狂神"
}
},
"sort": [
{
"age":{
"order":"asc"
}
}
]
"from":0,
"size":1
}
11. 多条件查询(bool)
GET kuangshen/user/_search
{
"query":{
"bool":{
"must":[ ---相当于and
{
"match":{
"name":"狂神说"
}
},
{
"match":{
"age": 23
}
}
],
"should":[ ---相当于or
{
"match":{
"name":"狂神说"
}
},
{
"match":{
"age": 23
}
}
],
"must_not":[ ---相当于<>
{
"match":{
"name":"狂神说"
}
},
{
"match":{
"age": 23
}
}
],
"filter":{ --可以使用filter 进行数据过滤
"range":{
"age":{
"lt": 10,
"gte": 1 --可以使用多个条件进行过滤
}
}
}
}
}
}
gt | 大于 |
---|---|
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
term 查询是直接通过倒排索引指定的词条进程精确查找的
关于分词 :
- term,直接查询精确的
- match,会使用分词器解析( 先分析文档,然后在通过分析的文档进行查询)
PUT testdb/_doc/2
{
"name":"狂神说Java name",
"desc":"狂神说Java desc2"
}
GET_analyze
{
"analyzer":"keyword", --keyword字段类型不会被分词器解析
"text":"狂神说Java name"
}
GET _analyze
{
"analyzer":"standard",
"text":"狂神说Java name"
}
GET testdb/_search
{
"query":{
"term":{
"name":“狂"
}
}
}
GET testdb/_search
{
"query":{
"term":{
"desc":“狂神说Java desc"
}
}
}
多个值匹配精确查询
GET testdb/_search
{
"query":
"bool":
"should":[
"term":
"t1":"22"
"term":(
"t1":"33"
高亮查询(搜索相关的结果可以高亮显示)
GET kuangshen/user/ search
{
"query":{
"match":{
"name":"狂神说"
}
}
"highlight":{
"pre tags": "<p class='key' style='color:red'>", -- 自定义搜索高亮条件
"post tags":"</p>", -- 自定义搜索高亮条件
"fields":{
"name":{}
}
}
}
原生依赖
<dependency>
<groupId>org.easticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-leve1-client</artifactId>
<version>7.6.2</version>
</dependency>
pom文件修改es版本
<properties>
<java.version>1.8</java.version>
<!-- 自己定义es 版本依赖,保证和本地一致 -->
<elasticsearch,version>7.6.1</elasticsearch.version>
</properties>
springboot增加elasticSearch依赖
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("127.0.0.1", 9299, "http")));
return client;
}
}
// 面向对象来操作
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client:
// 测试索引的创建
Request PUT kuang_index
@Test
void testCreateIndex() throws IOException {
// 1、创建索引请求
CreateIndexRequest request = new CreateIndexRequest("kuang index");
// 2、客户端执行请 IndicesCLient,请求后获得啊应
CreateIndexResponse createIndexResponse =client.indices().create(request, RequestOptions.DEFAULT);
System.out.printIn(createIndexResponse);
}
// 测试获取索引,判断其是否存在
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("kuang index2");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.printIn(exists);
}
// 测试删除索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest();
//删除
AcknowledgedResponse delete = client.indices().delete(request, ReguestOptions.DEFAULT);
System.out.printIn(delete.isAcknowledged());
}
// 测试添加文档
@SneakyThrows
void testAddDocument() throws IOException {
// 创建对象
User user = new User("狂神说" ,3);
// 创建请求
IndexRequest request = new IndexRequest("kuang index");
// 规则 put /kuang_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
// 将数据放入请求Json
request.source(JSON.toJSONString(user), XContentType.JSON);
// 客户端发送请求 ,获取响应的结果
IndexResponse indexResponse = client.index(request, RequestOptions,DEFAULT);
System.out.printIn(indexResponse.toString());
System.out.printIn(indexResponse.status());
}
// 获取文档,判断是否存在 get /index/doc/1
void testIsExists() throws IOException {
GetRequest getRequest = new GetRequest("kuang index", "1");
// 不获取返回的_source 的上下文
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 获得文档的信息
@Test
void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest("kuang index","1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
// 打印文档的内容
System.out.printIn(getResponse.getSourceAsString());
// 返回的全部内容和命令是一样的
System.out.printIn(getResponse);
}
// 更新文档的信息
@Test
void testUpdateRequest() throws IOException {
UpdateRequest updateRequest = new UpdateRequest( index: "kuang index", id: "1");
updateRequest.timeout("1s");
User user = new User("狂神说Java",18);
updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions ,DEFAULT);
System.out.println(updateResponse.status());
}
// 删除文档记录
@Test
void testDeleteRequest() throws IOException {
DeleteRequest request = new DeleteRequest("kuang index", "3");
request.timeout("1s");
DeleteResponse deleteResponse = client,delete(request, RequestOptions,DEFAULT);
System.out.printIn(deleteResponse.status());
}
// 特殊的,真的项目一般都会批量插入数据
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> userList = new ArrayList<>();
userList.add(new User("kuangshen1",3));
userList.add(new User("kuangshen2",3);
userList.add(new User("kuangshen3",3));
userList.add(new User("ginjiang1", 3));
userList.add(new User("qinjiang2",3));
userList.add(new User("ginjiang3",3)) ;
// 批处理请求
for (int i = 0; i < userList.size() i++) {
// 批量更新和批量删除,修改对应的请求即可
bulkRequest.add(
new IndexRequest("kuang_index").id(""+(i+1)).source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk(bulkRequest, ReguestOptions,DEFAULT);
System.out.printIn(bulkResponse.hasFailures());
}
//查询
//SearchRequest 搜索请求
//SearchSourceBuilder 条件构造
//HighlightBuilder 构建高亮
//TermQueryBuilder 精确查询
//MatchAllOueryBuilder
//XXX QueryBuilder 对应命令集
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("kuang index");
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 查询条件,可以使用 QueryBuilders 工具实现
// QueryBuilders.termQuery 精确
// QueryBuilders.matchALLQuery() 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name","qinjiang1");
//MatchAllOueryBuilder matchAllOueryBuilder = QueryBuilders,matchAllOuery();
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions,DEFAULT);
System.out.println(JSON.toJSONString(searchResponse.getHits()));
System.out.println("=================================") ;
for (SearchHit documentFields : searchResponse.getHits().getHits()){
System.out.println(documentFields.getSourceAsMap());
}
}
爬虫依赖及工具类
爬虫依赖:
<!-- jSoup解析网页 -->
<!-- 解析网页jSoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
爬虫工具类
public class HtmlParseUtil{
public static void main(String[] args) throws IOException {
// 获取请求 https://search.jd.com/Search?keyword=java
// 前提,需要联网
String url = "https://search,jd.com/Search?keyword=java";
// 解析网页。(Jsoup返回Document就是浏觉器Document对象)
Document document = Jsoup.parse(new URL(ur1), 3000);
// 所有在JS 中可以使用的方法,都能用
Element element = document.getElementById("J_goodsList");
// 获取所有的元素
Elements elements = element,getElementsByTag("li");
// 获取元素中的内容,这里el 是指每一个Li标签
for (Element el : elements){
// 关于这种图片特别多的网站,所有的图片都是延迟加裁的!
// source-data-Lazy-img
String img = el.getElementsByTag("img").eg(0),attr("source-data-lazy-img");
//String img = el.getElementsByTag("img"),eq(0),attr("src");
String price = el.getElementsByClass("p-price").eq(0).text();
String title = el.getElementsByClass("p-name").eq(0).text();
System,out,println("=============================");
System.out.println(img);
System.out.println(price);
System.out.printIn(title);
}
}
}
public class HtmlParseUtil {
public static void main(String[] args) throws Exception {
new HtmlParseUtil().parseJD( keywords: "java").forEach(System.out::println);
}
}
public List<Content> parseJD(String keywords) throws Exception {
String url ="https://search,jd,com/Search?keyword="+keywords;
Document document = Jsoup.parse(new URL(ur1), 3000);
Element element = document.getElementById("J_goodsList");
Elements elements = element.getElementsByTag("li");
ArrayList<Content> goodsList = new ArrayList<>();
// 取元素中的内容,这里el 就是每一个Li标签
for (Element el : elements) {
// 关于这种图片特别多的网站,所有的图片都是延迟加裁的
// source-data-Lazy-img
String img = el.getElementsByTag("img").eg(0),attr("source-data-lazy-img");
//String img = el.getElementsByTag("img"),eq(0),attr("src");
String price = el.getElementsByClass("p-price").eq(0).text();
String title = el.getElementsByClass("p-name").eq(0).text();
Content content = new Content();
content.setTitle(title);
content.setPrice(price);
content.setImg(img);
goodsList.add(content);
}
return goodslist;
}
前后端分离项目
// 业务编写
@Service
public class ContentService{
@Autowired
private RestHighLevelClient restHighLevelclient;
public static void main(String[] args) throws Exception {
new ContentService().parseContent("java");
}
// 1、解析数据放入 es 索引中
public Boolean parseContent(String keywords) throws Exception{
List<Content> contents = new HtmlparseUtil().parseJD(keywords);
// 把查询到的数据放入 es 中
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for(int i = @; i <contents.size() ; i++) {
bulkRequest.add(new IndexRequest("jd_goods").source(JSON.toJsONString(contents.get(i)),XContentType.JsoN));
}
BulkResponse bulk = restHighLevelclientbulk(bulkRequest, Requestoptions.DEFAULT);
return bulk.hasFailures();
}
// 2、获取这些数据实现搜索功能
public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize){
if(pageNo<=1){
pageNo = 1;
}
// 条件搜索
SearchRequest searchRequest = new SearchRequest("jd goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 分页
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
// 精准匹配
TermQueryBuilder termOueryBuilder = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termOueryBuilder);
sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
// 高亮
// 执行搜索
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighlevelclient,search(searchRequest, RequestOptions,DEFAULT);
// 解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
list.add(documentFields.getSourceAsMap());
}
return list;
}
}
// 编写
@RestController
public class ContentController{
@Autowired
private ContentService contentService;
@GetMapping("/parse/{keyword}")
public Boolean parse(@PathVariable("keyword") String keywords) throws Exception {
return contentService.parseContent(keywords);
}
@GetMapping("/search/(keyword}/(pageNo}/{pageSize}")
public List<Map<String,0bject>> search(@PathVariable("keyword") String keyword,@PathVariable("pageNo") int pageNo,@PathVariable("pageSize") int pageSize) throws IOException{
return contentService.searchPage(keyword,pageNo,pageSize);
}
}
//vue
<script>
new Vue({
el:'#app',
data:{
keyword:,// 搜索的关键字
results:[] // 搜索的结果
},
methods: {
searchKey(){
var keyword = this.keyword;
console.log(keyword);
//对接后端的接口
axios.get('search/'+keyword+"/1/10").then(response=>{
console.log(response);
this.results = response.data; // 绑定数据
})
}
}
})
</script>
关键字高亮
// 高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false); // 多个高亮显示
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
sourceBuilder.highlighter(highlightBuilder);
// 执行搜索
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighlevelclient,search(searchRequest, RequestOptions,DEFAULT);
// 解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for(SearchHit hit : searchResponse.getHits().getHits()) {
Map<String,HighlightField> highlightFields = hit,getHighlightFields();
HighlightField title = highlightFields.get("title");
// 原来的结果
Map<String,Object> sourceAsMap = hitgetSourceAsMap();
// 解析高亮的字段,将原来的字段换为高亮的字即可
if (title!=null){
Text[] fragments = title.fragments();
String n_title = "";
for (Text text : fragments) {
n_title += text;
}
sourceAsMap.put("title",n_title); // 高亮字段替换原来的内容即可
}
list.add(sourceAsMap);
}
<!--标题-->
<p class="productTitle">
<a v-html="result.title">
</a>
</p>