ES
目前最新版本: 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没有明显的变化
总结:
-
es基本是开箱即用,非常简单。Solr 的安装略微复杂一点。
-
Solr利用Zookeeper进行分布式管理,而es自身带有分布式协调管理功能。
-
Solr支持更多格式的数据,比如json、XML、CSV,而es仅支持json文件格式。
-
Solr官方提供的功能更多,而es本身更注重核心功能,高级功能多由第三方插件提供,例如图形化界面需要kibana支撑
-
Solr查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用;
- es建立索引快,即==实时性查询快==,用于facebook、新浪等搜索
- solr是传统搜索应用的有力解决方案,es更适用于新兴的实时搜索应用。
-
Solr比较成熟,有一个更大、更成熟的用户、开发和贡献者社区,而es相对开发维护者较少,更新太快,学习使用成本较高。
Elasticsearch安装
声明:==JDK1.8==,最低要求!
下载:https://www.elastic.co/cn/downloads/elasticsearch
能否顺利下载看运气!!!!!!
==Nodejs、python环境也要安装==
目录:
1. bin 启动文件
2. config 配置文件
log4j2 日志配置文件
jvm.options java虚拟机相关配置
elasticsearch.yml elasticsearch的配置文件! 默认端口9200 ,涉及跨域问题!
3. lib 相关jar包
4. modules 功能模块
5. plugins 插件!比如IK分词器
6. logs 日志
启动:elasticsearch.bat直接点击即可
安装可视化界面,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 服务,是因为存在跨域问题。
集群健康值的几种状态如下:
绿色:最健康的状态,代表所有的分片包括备份都可用
黄色:基本的分片可用,但是备份不可用(也可能是没有备份)
红色:部分的分片可用,表明分片有一部分损坏。此时执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好
灰色:未连接到elasticsearch服务
解决跨域问题:
打开ElasticSearch文件的config目录下的 elasticsearch.yml 配置文件:
#开启跨域支持
http.cors.enabled: true
#允许所有人跨域访问
http.cors.allow-origin: "*"
重新启动ElasticSearch,再次访问请求:http://localhost:9100/ ,测试head是否能连接上ElasticSearch
这个head把他当做一个可视化数据展示工具!查询还是要用kibana。
kibana安装
了解ELK
ELK Stack是软件集合Elasticsearch、Logstash、Kibana的简称,由这三个软件及其相关的组件可以打造大规模日志实时处理系统。
Elasticsearch 是一个基于 Lucene 的、支持全文索引的分布式存储和索引引擎,主要负责将日志索引并存储起来,方便业务方检索查询。
Logstash是一个日志收集、过滤、转发的中间件,主要负责将各条业务线的各类日志统一收集、过滤后,转发给 Elasticsearch 进行下一步处理。
Kibana是一个可视化工具,主要负责查询 Elasticsearch 的数据并以可视化的方式展现给业务方,比如各类饼图、直方图、区域图等。
所谓“大规模”,指的是 ELK Stack 组成的系统以一种水平扩展的方式支持每天收集、过滤、索引和存储TB规模以上的各类日志。通常,各类文本形式的日志都在处理范围,包括但不限于 Web 访问日志,如 Nginx/Apache Access Log 。
基于对日志的实时分析,可以随时掌握服务的运行状况、统计 PV/UV、发现异常流量、分析用户行为、查看热门站内搜索关键词等。
==kibana版本要跟es的版本保持一致==,下载之后解压即可
开发工具: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之后,又有弹框,按提示做即可
此时可以在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的用户名和密码就可以登录进去了,:
汉化:在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个分片(主分片)构成,每一个主分片会有一个副本(复制分片)。
上图是一个有三个节点的集群,可以看到主分片对应的复制分片都不会在同一个节点,这样的话,如果某个节点挂了,数据也不至于丢失。实际上,一个分片就是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得es在不扫描全部文档的情况下,就能告诉你哪些文档包含搜索词!
倒排索引
ES使用的是一种成为倒排索引的结构,采用Lucene倒排索引作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。
如果要搜索含有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
点击最新的,
下载,然后解压,将解压后的文件夹放到es的plugin目录中
重启es,日志中就可以看到IK分词器生效:
也可以使用es的bin目录下的工具
可以通过这个命令查看所有的插件: .\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分词器加配置!
添加自己的.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 #返回数据个数
}
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
找官方文档
==找到client==
推荐使用的是Java REST Client(2022年这部分客户端都已经弃用deprecated),低版本会有两个,高版本(7.15之后)只有一个
最新版的Java Client(8.2)也是只有一个
找到原生依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>8.2.0</version>
</dependency>
找对象
分析这个API中的方法
配置基本的项目
- SpringBoot的自带ES版本太低,需要重置es版本(pom.xml文件)
idea新项目配置jdk8:
==修改pom.xml文件保证es的版本和客户端版本(8.2.0)一致==
<properties>
<elasticsearch.version>8.2.0</elasticsearch.version> 由于没有找到最新版本的,就写为7.17.0
</properties>
- 创建es配置类
@Configuration
public class ElaticSearchClientConfig{
@Bean
public RestClient restClient(){
RestClient restClient = RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")).build();
return restClient;
}
}
- 查看xxxAutoConfiguration.class和xxxproperties.class
##默认的配置
#org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration
#关键的配置类:
#org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientConfigurations
- 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
以上内容如有错误之处,多多见谅!!!!!!