ES

官网:https://www.elastic.co/cn/

目前最新版本: 8.2.0

本次文档内容版本:7.6.1 7.17.0

6.x 和7.x的区别十分大,6.x的API(原生API、RestFul高级)

背景

SQL:like %xxx%,如果是大数据,就十分慢!加索引也没用,以%开始时会出现索引失效!

Elasticsearch:搜索!(百度、github、淘宝电商)

==ELK==生态圈:ElasticSearch 、Logstash、Kibana

大纲

1、聊一个人
2、货比三家
3、安装
4、生态圈
5、分词器
6、RestFul操作es
7、CRUD
8、SpringBoot集成es(从原理分析)
9、爬虫爬取数据!
10、实战模拟全文检索

Doug Cutting

生活中,可能所有人都间接用过他的作品,他是Lucene、Nutch 、Hadoop等项目的发起人。是他,把高深莫测的搜索技术形成产品,贡献给普罗大众;还是他,打造了目前在云计算和大数据领域里如日中天的Hadoop。他是某种意义上的盗火者,他就是Doug Cutting。

大数据的问题:==存储+计算==

2003年,Google发表了一篇技术学术论文,公开介绍自己的谷歌文件系统GFS(Google File System),这是谷歌公司为了存储海量搜索数据而设计的专用文件系统。

第二年,Doug Cutting基于Google的GFS论文,实现了分布式文件存储系统,并命名为NDFS(Nutch Distributed File System)。

同年,Google又发表了一篇技术学术论文,介绍自己的MapReduce编程模型,这个编程模型,用于大规模数据集(1TB)的并行分析运算。

2005年,Doug Cutting又基于MapReduce在Nutch搜索引擎上实现了该功能。

2006年,加入了Yahoo公司,加盟雅虎之后,他又将NDFS和MapReduce进行了升级改造,并重新命名为Haddop(NDFS也改名为HDFS)。

这个就是大名鼎鼎的大数据框架系统——Hadoop的由来!Doug Cutting也被人们称为==Hadoop之父==。

回到主题

Elasticsearch基于Lucene(信息检索工具包)做了一些封装和增强

ES概述

Elasticsearch是一个高度可扩展性的、开源的、全文本搜索和分析引擎。它让你能够快速地存储、搜索和分析大量的数据,接近实时操作。它通常被用作底层引擎/技术,用来驱动那些"需要支持复杂的搜索功能的应用程序。

Elasticsearch,基于lucene,隐藏复杂性,提供简单易用的restful api接口、java api接口(还有其他语言的api接口)。

Elasticsearch是一个实时分布式搜索和分析引擎。它用于全文搜索、结构化搜索、分析。

全文检索:将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。

结构化检索:我想搜索商品分类为日化用品的商品都有哪些,select * from products where category_id='日化用品'

数据分析:电商网站,最近7天牙膏这种商品销量排名前10的商家有哪些;新闻网站,最近1个月访问量排名前3的新闻版块是哪些

据国际权威的数据库产品评测机构DB Engines的统计,2016年1月,Elasticsearch已经超过Solr等,成为排名第一的搜索引擎类应用。

谁在使用

1.维基百科,类似百度百科,牙膏,牙膏的维基百科,全文检索,高亮,搜索推荐。
2.The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论)+ 社交网络数据(对某某新闻的相关看法),数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好,坏,热门,垃圾,鄙视,崇拜)。
3.Stack Overflow(国外的程序异常讨论论坛),IT问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案
4.GitHub(开源代码管理),搜索上千亿行代码。
5.国内:站内搜索(电商,招聘,门户,等等),IT系统搜索(OA,CRM,ERP,等等),数据分析(ES热门的一个使用场景)。

ES和solr的区别

ES

Elasticsearch是一个基于 Lucene 的搜索服务器。 它提供了一个分布式多用户能力的 全文搜索引擎 ,基于==RESTful web==接口。

Solr

对外提供类似于==Web-service==的API接口,用户可以通过http请求向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过提出查找请求,得到返回结果

Lucene

在java开发环境里,Lucene是一个成熟的免费开源工具,就其本身而言,是当前最受欢迎的免费java信息检索程序库。

ES和Solr的比较

1、单纯的对已有的数据进行搜索时,solr更快
2、当实时建立索引时,Solr会产生io阻塞,查询性能较差,es具有明显的优势
3、随时数据量的增加,Solr的搜索效率会变得很低,但是es没有明显的变化

总结:

  1. es基本是开箱即用,非常简单。Solr 的安装略微复杂一点。

  2. Solr利用Zookeeper进行分布式管理,而es自身带有分布式协调管理功能。

  3. Solr支持更多格式的数据,比如json、XML、CSV,而es仅支持json文件格式。

  4. Solr官方提供的功能更多,而es本身更注重核心功能,高级功能多由第三方插件提供,例如图形化界面需要kibana支撑

  5. Solr查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用;

    • es建立索引快,即==实时性查询快==,用于facebook、新浪等搜索
    • solr是传统搜索应用的有力解决方案,es更适用于新兴的实时搜索应用。
  6. Solr比较成熟,有一个更大、更成熟的用户、开发和贡献者社区,而es相对开发维护者较少,更新太快,学习使用成本较高。

Elasticsearch安装

声明:==JDK1.8==,最低要求!

下载:https://www.elastic.co/cn/downloads/elasticsearch

能否顺利下载看运气!!!!!!

==Nodejs、python环境也要安装==

alt

目录:

1. bin  启动文件
2. config  配置文件
	log4j2  日志配置文件
	jvm.options  java虚拟机相关配置
	elasticsearch.yml  elasticsearch的配置文件!  默认端口9200  ,涉及跨域问题!
3. lib  相关jar包
4. modules   功能模块
5. plugins  插件!比如IK分词器
6. logs 日志

启动:elasticsearch.bat直接点击即可

alt

安装可视化界面,head

1、下载解压 elasticsearch-head

elasticsearch-head 插件下载地址:https://github.com/mobz/elasticsearch-head/

下载后解压得到文件:elasticsearch-head-master

2、启动运行 elasticsearch-head插件

通过elasticsearch-head-master打开命令行,输入==npm install==下载依赖;

然后在输入命令 ==npm run start== 启动head插件;

访问请求:http://localhost:9100/ :发现集群健康值是灰色,访问不到ElasticSearch 服务,是因为存在跨域问题。

img

集群健康值的几种状态如下:

绿色:最健康的状态,代表所有的分片包括备份都可用

黄色:基本的分片可用,但是备份不可用(也可能是没有备份)

红色:部分的分片可用,表明分片有一部分损坏。此时执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好

灰色:未连接到elasticsearch服务

解决跨域问题:

打开ElasticSearch文件的config目录下的 elasticsearch.yml 配置文件:

#开启跨域支持
http.cors.enabled: true
#允许所有人跨域访问
http.cors.allow-origin: "*"

重新启动ElasticSearch,再次访问请求:http://localhost:9100/ ,测试head是否能连接上ElasticSearch

img

这个head把他当做一个可视化数据展示工具!查询还是要用kibana。

kibana安装

了解ELK

ELK Stack是软件集合ElasticsearchLogstashKibana的简称,由这三个软件及其相关的组件可以打造大规模日志实时处理系统。

Elasticsearch 是一个基于 Lucene 的、支持全文索引的分布式存储和索引引擎,主要负责将日志索引并存储起来,方便业务方检索查询。

Logstash是一个日志收集、过滤、转发的中间件,主要负责将各条业务线的各类日志统一收集、过滤后,转发给 Elasticsearch 进行下一步处理。

Kibana是一个可视化工具,主要负责查询 Elasticsearch 的数据并以可视化的方式展现给业务方,比如各类饼图、直方图、区域图等。

所谓“大规模”,指的是 ELK Stack 组成的系统以一种水平扩展的方式支持每天收集、过滤、索引和存储TB规模以上的各类日志。通常,各类文本形式的日志都在处理范围,包括但不限于 Web 访问日志,如 Nginx/Apache Access Log 。

基于对日志的实时分析,可以随时掌握服务的运行状况、统计 PV/UV、发现异常流量、分析用户行为、查看热门站内搜索关键词等。

==kibana版本要跟es的版本保持一致==,下载之后解压即可

alt

开发工具:Postman、curl、head、谷歌浏览器插件测试

注意:

8版本es有内置密码,第一次启动日志中会出现elastic用户的密码 以及kibana需要的key,这个最好复制到本地;

非第一次启动es,日志中不会出现用户密码和kibana的key:

1.重置密码:
PS F:\developTool\elasticsearch-env\elasticsearch-8.2.0\bin> ./elasticsearch-reset-password -u elastic
This tool will reset the password of the [elastic] user to an autogenerated value.
The password will be printed in the console.
Please confirm that you would like to continue [y/N]y


Password for the [elastic] user successfully reset.
New value: FI+j83p+ZAWlF9DAeuPR


2.bin\elasticsearch-create-enrollment-token.bat --scope kibana   #获取kibana的key

F:\developTool\elasticsearch-env\elasticsearch-8.2.0\bin> .\elasticsearch-create-enrollment-token.bat --scope kibana
eyJ2ZXIiOiI4LjIuMCIsImFkciI6WyIxOTIuMTY4LjAuMTAzOjkyMDAiXSwiZmdyIjoiOWY1MzFlNzI3ODA5ODBhNDllYzgxNDNiZjVjZjM1Y2E2NzM1ODQ5NmQzMzY4Yzk0MDFlZDRjZWVjMmQ0YmY5YyIsImtleSI6IjczZTk1b0FCUHJYcjA4ZEFwSzJ1OnFOYmNuNXZZU2ZDdWt4YlVRdWwwQ0EifQ==

输入完kibana的key之后,又有弹框,按提示做即可

alt

此时可以在kibana.yml文件中发现新增配置:

# This section was automatically generated during setup.
elasticsearch.hosts: ['https://192.168.0.103:9200']
elasticsearch.serviceAccountToken: AAEAAWVsYXN0aWMva2liYW5hL2Vucm9sbC1wcm9jZXNzLXRva2VuLTE2NTMxMzg3OTQ0ODg6aGpOWkwwTFZRVTZ1NmpDSHhkQTdqUQ
elasticsearch.ssl.certificateAuthorities: ['F:\developTool\elasticsearch-env\kibana-8.2.0\data\ca_1653138795230.crt']
xpack.fleet.outputs: [{id: fleet-default-output, name: default, is_default: true, is_default_monitoring: true, type: elasticsearch, hosts: ['https://192.168.0.103:9200'], ca_trusted_fingerprint: 9f531e72780980a49ec8143bf5cf35ca67358496d3368c9401ed4ceec2d4bf9c}]

然后输入elastic的用户名和密码就可以登录进去了,:

alt

汉化:在config/kibana.yml文件中将#i18n.locale: "en"修改"zh-CN",然后重启。

ES核心概念

###集群(cluster)、节点(node)、索引(index)、类型(type)、文档(document)、分片(shard)、映射(mapping)

elasticsearch是面向文档,与关系型数据库的对比:

Relational DataBase Elasticsearch
数据库(database) 索引(indix)
表(table) types(7之后就没了)
行(row) 文档(document)
字段(columns) field

==elasticsearch中可以 包含多个索引,每个索引中可以包含多个类型,每个类型下又包含了多个文档,每个文档下又有多个字段==。

物理设计

elasticsearch在后台把每个索引划分成多个分片,包括主分片(primary shard)或者是复制分片(replica shard),每个分片可以在集群中的不同服务器间迁移。

逻辑设计

一个索引类型中,包含多个文档,当我们索引一篇文档时,可以通过这样的顺序找到它:索引>类型>文档id,通过这个组合我们就能索引到某个具体的文档。注意:==文档id不必是整数,实际上是个字符串==。

节点和分片如何工作

一个集群至少有一个节点,而一个节点就是一个elasticsearch进程,每个节点都可以有多个索引,如果创建了索引,那么索引将会有5个分片(主分片)构成,每一个主分片会有一个副本(复制分片)。

alt

上图是一个有三个节点的集群,可以看到主分片对应的复制分片都不会在同一个节点,这样的话,如果某个节点挂了,数据也不至于丢失。实际上,一个分片就是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得es在不扫描全部文档的情况下,就能告诉你哪些文档包含搜索词!

倒排索引

ES使用的是一种成为倒排索引的结构,采用Lucene倒排索引作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。

alt

如果要搜索含有python标签的文档,那相对于查找所有的原始数据表,查找倒排索引后的数据将会快很多。只需要查看标签这一栏,然后获取相关的文档id即可。

es索引和Lucene索引对比

在es中,索引这个词被频繁使用,这就是术语的作用。在es中,索引被分为多个分片,每个分片是一个Lucene索引。所以es的索引是由多个Lucene索引组成。

IK分词器插件

什么是Ik分词器

分词:即把一段文字划分成一个个的关键字,我们搜索时会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每一个字看成一个词,比如“我喜欢学习”被拆成“我”、“喜”、“欢”、“学”、“习”,这显然是不满足要求的,所以我们需要安装中文分词器IK分词器来解决这个问题。

IK分词器提供了两种分词算法:ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最小粒度划分!

安装

下载地址:https://github.com/medcl/elasticsearch-analysis-ik

alt

点击最新的,

alt

下载,然后解压,将解压后的文件夹放到es的plugin目录中

alt

重启es,日志中就可以看到IK分词器生效:

alt

也可以使用es的bin目录下的工具

alt

可以通过这个命令查看所有的插件: .\elasticsearch-plugin list

分词测试

打开kibana,测试分词器效果

ik_smart效果

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "中国***永远滴神"
}
{
  "tokens" : [
    {
      "token" : "中国***",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "永远",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "滴",
      "start_offset" : 7,
      "end_offset" : 8,
      "type" : "CN_CHAR",
      "position" : 2
    },
    {
      "token" : "神",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "CN_CHAR",
      "position" : 3
    }
  ]
}

ik_max_word效果

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "中国***永远滴神"
}
{
  "tokens" : [
    {
      "token" : "中国***",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "中国",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "国共",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "***",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 3
    },
    {
      "token" : "***",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "党",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "CN_CHAR",
      "position" : 5
    },
    {
      "token" : "永远",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 6
    },
    {
      "token" : "滴",
      "start_offset" : 7,
      "end_offset" : 8,
      "type" : "CN_CHAR",
      "position" : 7
    },
    {
      "token" : "神",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "CN_CHAR",
      "position" : 8
    }
  ]
}

问题:永远滴神被拆开了!

这种自己需要的词我们需要自己加入到分词器的词库中

ik分词器加配置!

alt

添加自己的.dic文件,然后配置到xml中。重启es,再次搜索:

{
  "tokens" : [
    {
      "token" : "中国***",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "永远滴神",
      "start_offset" : 5,
      "end_offset" : 9,
      "type" : "CN_WORD",
      "position" : 1
    }
  ]
}

Rest风格说明

一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。他主要用于客户端和服务器交互类软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

es8版本的时候,会有出入,下面这个只做参考

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 查询所有数据

基础测试

创建索引

#es8之后type就没有了,自动创建索引与文档,默认就是_doc
PUT /test1/_doc/1
{
  "name":"张志敏",
  "age":29
}

#结果,显示创建成功
{
  "_index" : "myinfo",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

#如果关闭自动创建索引,在配置文件config/elasticsearch.yml中加
action.auto_create_index: false

#若跨域没开
{"statusCode":502,"error":"Bad Gateway","message":"Client request timeout"}

疑问:name需不需要指定类型?

  • 字符串:keyword text
  • 数值类型:long、integer、short、byte、double、float、half_float、scaled_float
  • 日期类型:date
  • 布尔类型:boolean
  • 二进制类型:binary
  • 。。。。。。

指定字段类型

#新建索引,只添加映射
put /test2
{
  "mappings":{
    "properties":{
      "name":{
        "type":"text"
      },
      "age":{
        "type":"long"
      },
      "birthday":{
        "type":"date"
      }
    }
  }
}

#查看索引信息
get test2

如果自己的文档字段没有指定,那么es就会给我们默认配置字段类型。

修改索引

#第一种方式
put /test3/_doc/1
{
  "name":"zzm1",
  "age":30,
  "birthday":"1993-12-29"
}
#第二种方式  es8版本
post /test3/_update/1
{
  "doc":{
    "name":"卡卡罗特"
  }
}



{
  "_index" : "test3",
  "_id" : "1",
  "_version" : 4, #修改版本
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

删除索引

#删除索引
DELETE test2

##################
{
  "acknowledged" : true
}

关于文档的基本搜索操作(==重点==)

简单的条件搜索

GET kaka/_search?q=desc:传说

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.9672978,
    "hits" : [
      {
        "_index" : "kaka",
        "_id" : "3",
        "_score" : 0.9672978,
        "_source" : {
          "name" : "布罗利",
          "age" : "12",
          "desc" : "传说中的超级赛亚人",
          "tags" : [
            "无敌",
            "迷失"
          ]
        }
      },
      {
        "_index" : "kaka",
        "_id" : "2",
        "_score" : 0.8237976,
        "_source" : {
          "name" : "贝吉塔",
          "age" : "12",
          "desc" : "传说中的超级赛亚人远古血脉",
          "tags" : [
            "无敌",
            "迷失"
          ]
        }
      }
    ]
  }
}

复杂的查询(高亮、排序、分页、模糊查询、精准查询)

GET kaka/_search
{
  "query": {
    "match": {
      "name": "罗"			#搜索条件
    }
  },
  "_source": ["name","tags"],   #字段过滤
  "sort": [
    {
      "age.keyword": {   #如果直接用age,不设置类型的话,默认都是text,有报错"reason" : "Text fields are not optimised for 
      #operations that require per-document field data like aggregations and sorting, so
      #these operations are disabled by default. Please use a keyword field instead. 
      #Alternatively, set fielddata=true on [age] in order to load field data by uninverting
      #the inverted index. Note that this can use significant memory.##
        "order": "asc"
      }
    }
  ],
  "from": 0,    #从第几个数据开始,从0算起
  "size": 20    #返回数据个数
}

alt

GET kaka/_search
{
  "query": {
    "bool": {
      "should": [			#布尔类型条件查询(or),还有must(and),must_not(not)
        {
          "match": {
            "name": "贝 卡卡"   #多个条件用空格隔开,只要满足其中一个结果就可以被查出
          }
        },
        {
          "match": {
            "age": 12
          }
        }
      ],
      "filter": [			#过滤条件
        {"range": {			#范围
          "age": {
            "gte": 10,			#大于等于
            "lte": 20			#小鱼等于 
          }
        }}
      ]
    }
  }
}

term查询-精准查询

term查询是直接通过倒排索引指定的词条进行精确查找。

关于分词

keyword类型的数据不会被分词

  • term 直接查询精确的
  • match 使用分词器解析!(先分析文档,然后通过分析的文档进行查询)

高亮查询

GET kaka/_search
{
  "query": {"match": {
    "name": "卡卡罗特"
  }},
  "highlight": {
    "pre_tags": "<p class=key' style='color:red'>",  #自定义标签前缀
    "post_tags": "</p>", 							#自定义标签后缀,这个是全局的
    "fields": {
      "name": {
      		####这个地方也可以设置每个字段的自定义标签
      }
    }
  }
}


##############################
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 3.894744,
    "hits" : [
      {
        "_index" : "kaka",
        "_id" : "1",
        "_score" : 3.894744,
        "_source" : {
          "name" : "卡卡罗特",
          "age" : "12",
          "desc" : "赛亚人低级战士",
          "tags" : [
            "自大",
            "好战"
          ]
        },
        "highlight" : {
          "name" : [
            "<p class=key' style='color:red'>卡</p><p class=key' style='color:red'>卡</p><p class=key' style='color:red'>罗</p><p class=key' style='color:red'>特</p>"
          ]
        }
      },
      {
        "_index" : "kaka",
        "_id" : "3",
        "_score" : 0.49005115,
        "_source" : {
          "name" : "布罗利",
          "age" : "12",
          "desc" : "传说中的超级赛亚人",
          "tags" : [
            "无敌",
            "迷失"
          ]
        },
        "highlight" : {
          "name" : [
            "布<p class=key' style='color:red'>罗</p>利"
          ]
        }
      }
    ]
  }
}

集成SpringBoot

找官方文档

alt

==找到client==

alt

alt

推荐使用的是Java REST Client(2022年这部分客户端都已经弃用deprecated),低版本会有两个,高版本(7.15之后)只有一个

alt

最新版的Java Client(8.2)也是只有一个

alt

找到原生依赖

alt

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>8.2.0</version>
</dependency>

找对象

alt

分析这个API中的方法

配置基本的项目

  1. SpringBoot的自带ES版本太低,需要重置es版本(pom.xml文件)

alt alt alt

idea新项目配置jdk8: alt alt alt

==修改pom.xml文件保证es的版本和客户端版本(8.2.0)一致==

<properties>
    <elasticsearch.version>8.2.0</elasticsearch.version>       由于没有找到最新版本的,就写为7.17.0
</properties>

alt

  1. 创建es配置类

@Configuration
public class ElaticSearchClientConfig{
    
    @Bean
    public RestClient restClient(){
        RestClient restClient = RestClient.builder(
                new HttpHost("127.0.0.1", 9200, "http")).build();
        return restClient;
    }  
}
  1. 查看xxxAutoConfiguration.class和xxxproperties.class

alt

##默认的配置
#org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration

#关键的配置类:
#org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientConfigurations

  1. request对象
//注意7.17的RestClient没有下列方法,应该用其他方法,参考官网相应版本的API使用
//下面的是7低版本API  RestHighLevelClient 

//创建索引请求
CreateIndexRequest request = new CreateIndexRequest("karotte");
//客户端执行请求,请求后获得响应
CreateIndexResponse response = client.indices().create(request,RequestOptions.DEFAULT);

//判断索引是否存在
GetIndexRequest request = new GetIndexRequest("karotte");
Boolean exists = client.indices().exists(request,RequestOptions.DEFAULT);
    
//删除索引
DeleteIndexRequest request = new DeleteIndexRequest("karotte");
AcknowledgeResponse response = client.indices().delete(request,RequestOptions.DEFAULT);
Boolean isok = response.isAcknowledged();

//添加文档
IndexRequest request = new IndexRequest("karotte");
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.source(JSON.toJSONString(new User("kaka",14L)), XContentType.JSON);
IndexResponse response = client.index(request,RequestOptions.DEFAULT);

//获得文档信息
GetRequest request = new GetRequest("karotte","1");//索引和文档id
request.fetchSourceContext(new FetchSourceContext(false));//不获取_source上下文
//判断文档是否存在
Boolean isExists = client.exists(request,RequestOptions.DEFAULT);
//获取文档信息
GetResponse response = client.get(request,RequestOptions.DEFAULT);
String source = response.getSourceAsString();
//更新文档
UpdateRequest request = new UpdateRequest("karotte","1");
request.setTimeout("1s");
request.doc(JSON.toJSONString(new User("kaka",14L)),XContentType.JSON);
UpdateResponse response = client.update(request,RequestOptions.DEFAULT);
//删除文档
DeleteRequest request = new DeleteRequest("karotte","1");
DeleteResponse response = client.delete(request,RequestOptions.DEFAULT);

//实际场景都是批量操作,批量插入如下,其他的类似
BulkRequest request = new BulkRequest();
request.setTimeout("20s");
ArrayList<User> list = new ArrayList<>();
...//添加数据
for(int i=0; i<list.size(); i++){
    request.add(
        new IndexRequest("karotte")
        .id(list.get(i).getId)
        .source(JSON.toJSONString(list.get(i)),XContentType.JSON));
}
BulkResponse response = client.bulk(request,RequestOptions.DEFAULT);
Boolean isFail = response.hasFailures();

//查询
SearchRequest request = newSearchRequest("karotte");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//查询条件要使用  QueryBuilders 工具类实现
TermQueryBulder termQueryBulder = TermQueryBulder.termQuery("name","卡卡");
sourceBuilder.query(termQueryBulder);
sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
sourceBuilder.from(0);
sourceBuilder.size(50);//默认最大100000
request.source(sourceBuilder);

SearchResponse response = client.search(request,RequestOptions.DEFAULT);
response.getHits()

实战

爬虫

/**
     * 爬取京东官网的产品信息
     * @param keyword
     * @return
     */
    public static List<Product> parseJD(String keyword){
        List<Product> list = new ArrayList<>();
        try {
            //获取请求, https://search.jd.com/Search?keyword=java    需要联网
            String url = "https://search.jd.com/Search?keyword="+keyword;
            //解析网页。
            Document document = Jsoup.parse(new URL(url), 5000);
            //所有在js的方法这里都可以使用
            Element element = document.getElementById("J_goodsList");
            //获取所有的li元素
            Elements li = element.getElementsByTag("li");
            for (Element el : li) {
                //关于图片比较多的网站,所有的图片都是延迟加载的,所以src找不到
                //String img = el.getElementsByTag("img").eq(0).attr("src");
                String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
                String price = el.getElementsByClass("p-price").eq(0).text();
                String title = el.getElementsByClass("p-name").eq(0).text();
                ProductJd pd = new ProductJd();
                pd.setImg(img);
                pd.setPrice(price);
                pd.setTitle(title);
                list.add(pd);
            }
        }catch (Exception e){
            e.printStackTrace();
            return list;
        }
        return list;
    }

service层代码

package com.kaka.esapi.service.Impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kaka.esapi.pojo.Product;
import com.kaka.esapi.pojo.ProductJd;
import com.kaka.esapi.service.ContentService;
import com.kaka.esapi.utils.HtmlParseUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class ContentServiceImpl implements ContentService {

    @Autowired
    @Qualifier("restClient")
    private RestClient restClient;

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private HtmlParseUtil htmlParseUtil;
    /**
     * 将爬取出的京东商城数据存储到ES
     * @param word  关键字
     * @return
     */
    public Boolean parseContent(String word){
        try {
            List<Product> list = htmlParseUtil.parseJD(word);
            BulkRequest request = new BulkRequest();
            request.timeout("2m");
            for (int i = 0; i < list.size(); i++) {
                request.add(
                        new IndexRequest("jd_goods_info")
                                .source(JSON.toJSONString(list.get(i)), XContentType.JSON)
                );
            }
            BulkResponse rep = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            return !rep.hasFailures();
        }catch (Exception e){
            return false;
        }
    }
    /**
     * 获取商品数据
     * @param query  搜索词
     * @param pageNo  页码
     * @param pageSize  每页返回数
     * @return
     */
    public List<Product> searchPage(String query, int pageNo, int pageSize) throws Exception{
        if(pageNo<=1){
            pageNo = 1;
        }
        List<Product> result = new ArrayList<>();
        //条件搜索
        SearchRequest searchRequest = new SearchRequest("jd_goods_info");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //精准匹配
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title",query);
        searchSourceBuilder.query(termQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        //高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style='color:red' >");
        highlightBuilder.postTags("</span>");
        highlightBuilder.requireFieldMatch(false); //多个高亮显示
        searchSourceBuilder.highlighter(highlightBuilder);
        //分页
        searchSourceBuilder.from(pageNo);
        searchSourceBuilder.size(pageSize);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        for (SearchHit hit : search.getHits().getHits()) {
            String productStr = hit.getSourceAsString();//获取json字符串
            //ProductJd productJd = JSON.parseObject(productStr, ProductJd.class);//转换成对应的实体类
            ObjectMapper objectMapper = new ObjectMapper();
            ProductJd productJd = objectMapper.readValue(productStr,ProductJd.class);
            //解析高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField title =  highlightFields.get("title");
            if (title != null) {
                Text[] texts = title.fragments();
                String new_title = "";
                for (Text text : texts) {
                    new_title += text;
                }
                productJd.setTitle(new_title);
            }
            result.add(productJd);
        }
        return result;
    }
}

本文内容参考:https://www.bilibili.com/video/BV17a4y1x7zq?spm_id_from=333.999.0.0

以上内容如有错误之处,多多见谅!!!!!!