ElasticSearch
ElasticSearch概述
ElasticSearch,简称es,es是一个开源的高拓展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身拓展性很好,它可以拓展到上百台服务器,处理PB级别的数据。es也使用java开发并使用Lucene的复杂性,从而让全文检索变得简单 据国际权威的数据库产品评测机构DB Engines的统计,2016年1月,ElasticSearch已超过solr等成为排名第一的搜索引擎类应用
ElasticSearch和solr的差别
ElasticSearch简介
ElasticSearch是一个实时分布式搜索和分析引擎,它让你以前所未有的速度处理大数据的可能 它用于全文搜索,结构化搜索,分析以及这三者混合使用 ElasticSearch是一个基于Apache Lucene™的开源搜索引擎。无论是在开源还是专有邻域,Lucene可以被认为是迄今为止最先进、性能最好的,功能最全的搜索引擎库。 但是。Lucene只是一个库。想要使用它,你必须使用java作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要更深入的了解检索的相关知识来理解它是如何工作的。 ElasticSearch也使用java开发并使用Lucene作为其核心来实现所有索引和功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文检索变得简单
Solr简介
Solr是Apache下的顶级开源项目,采用java开发,它是基于Lucene的全文检索服务器。solr提供优化比Lucene跟为丰富的查询语言,同时实现了可配置、可拓展,并对索引、搜索性能进行了优化 solr可以独立运行,运行在jetty、tomcat等这些servlet容器中,Sole索引的实现方法很简单,用post方法向solr服务器发送一条可描述Filed及其内容的XML文档,Solr根据xml文档的添加、删除、更新索引、Solr搜索只需要发送HTTP GET请求,然后对solr返回xml、json等格式的查询结果进行解析,组织页面布局、solr不提供构建UI的功能,solr提供了一个管理界面,通过管理界面可以对查询的solr的配置和运行情况 Solr是一个开源搜索平台,用于构建搜索应用程序。 是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口 它建立在Lucene(全文搜索引擎)之上。 Solr是企业级的,快速的和高度可扩展的。
Lucene简介
Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能的一个开源框架。Lucene 目前是 Apache Jakarta 家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具包。
目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能。Lucene 能够为文本类型的数据建立索引,所以你只要能把你要索引的数据格式转化的文本的,Lucene 就能对你的文档进行索引和搜索。比如你要对一些 HTML 文档,PDF 文档进行索引的话你就首先需要把 HTML 文档和 PDF 文档转化成文本格式的,然后将转化后的内容交给 Lucene 进行索引,然后把创建好的索引文件保存到磁盘或者内存中,最后根据用户输入的查询条件在索引文件上进行查询。不指定要索引的文档的格式也使 Lucene 能够几乎适用于所有的搜索应用程序。
ElasticSearch与solr比较
ElasticSearch vs solr 总结
- es基本就是开箱使用,非常简单,Solr安装比较复杂一点
- Solr支持更多格式的数据,比如json,xml,csv,而ElasticSearch仅支持json文件格式
- Solr利用Zookeeper进行分布式管理,而ElasticSearch自身带有分布式协调管理功能。
- Solr官方提供的功能更多,而ElasticSearch本身更注重与核心功能,高级功能多有第三方插件提供,例如图形化页面需要kibana友好支撑
- Solr查询快,但更新索引满,用于电商等查询多的应用
- ElasticSearch建立索引快(查询慢),实时性查询快,用于facebook新浪等搜索
- Solr是传统搜索应用的有力解决方案,但ElasticSearch更适用新兴的实时搜索应用
- Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而ElasticSearch相对开发维护者较少,更新太快,学习使用成本较高
ElasticSearch安装
声明:JDK1.8,最低要求,ElasticSearch客户端,界面工具 java开发,ElasticSearch的版本和我们之后对应的java的核心jar包!版本对应,JDK环境是正常
下载 https://www.elastic.co/cn/
华为云的镜像去下载 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
链接: https://pan.baidu.com/s/1xhpIqT8Tm-jN_h2ir9xlwA 提取码: jv4h
- 下载后解压
- 目录介绍
bin 启动文件
config 配置文件
log4j2 日志配置文件
jvm.options java虚拟机相关配置
elasticsearch.yml elasticsearch的配置文件 默认9200端口! 跨域!
lib 相关jar包
modules 功能目录
plugins 插件
- 双击bin/elasticsearch.bat文件
- 访问http://127.0.0.1:9200
- 安装可视化界面 ElasticSearch head 下载:https://github.com/mobz/elasticsearch-head
- 报错:跨域问题 解决: 打开elasticsearch.yml文件进行配置
http.cors.enabled: true
http.cors.allow-origin: "*"
- 重新启动elasticSearch和elasticsearch-head
索引:相当于一个数据库
新建一个索引
安装kibana
了解ELK
ELK是elasticSearch、Logstash、kibana三大开源框架首字母大写的简称。市面上也被称为Elastic Stack。其中elasticSearch是一个基于Lucene、分布式、通过RESTful方式进行交互的近实时搜索平台框架。像类似于百度、谷歌这种大数据全文搜索引擎的场景都可以使用elasticSearch作为底层支持框架、可见elasticSearch提供的搜索能力确实强大,elasticSearch也被市面上简称为es。 Logstash是ELK的中央数据引流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出带不同的目的地(文件/MQ/redis/elasticSearch/kafka)等。kibana可以将elasticSearch的数据通过友好的页面展示出来,提供实时分析的功能
下载地址:https://www.elastic.co/cn/downloads/kibana 注意:es版本和kibana版本尽量保持一致
下载完解压
启动测试:bin/kibana.bat
端口:5601 访问:http://localhost:5601
英文看不懂咋办?使用汉化插件
打开/config/kibana.yml文件 设置i18n.locale: “zh-CN”
重新启动kibana
ES核心概念
1、索引 2、字段类型(mapping) 3、文档(documents)
概述 elasticsearch是面向文档,关系型数据库和elasticsearch客观的对比 集群,节点所以,类型,文档,分片,映射是什么?
Relational DB | elasticsearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | type |
行(rows) | documents |
字段(colums) | fieds |
elasticsearch(集群)中可以包含多个索引(数据库),每个索引可以包含多个类型(表),每个类型可以包含多个文档(行),每个文档中有保安多个字段(列)。
物理设计: elasticsearch在后台吧每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
逻辑设计: 一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引>类型>文档id>,通过这个组合我们就能索引带某个具体的文档。注意:ID不必是整数,实际上是一个字符串
文档
之前说 elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档, elasticsearch中,文档有几个重要属性:
- 自我包含,一文档同时包含字段和对应的值,也就是同时包含 key: value!name: guangshen
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的
- 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在 elasticsearch中,对于字 段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。
类型
类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么 elasticsearch是怎么做的呢? elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型, elasticsearch就开始猜,如果这个值是18,那elasticsearch会认为它是整形。但是 elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系 型数据库殊途同归了,先定义好字段,然后再使用,别整什么蛾子。
索引
就是数据库! 索引是映射类型的容器, elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。我们来研究下分片是如何工作的
物理设计:节点和分片如何工作 一个集群至少有一个节点,而一个节点就是一个 elasricsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会 有个5个分片( primary shard,又称主分片)构成的,毎一个主分片会有一个副本( replica shard,又称复制分片)
上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不于丢失。 实际上,一个分片是一个 Lucene索引,一个包含倒排索引的文件目录,倒排素引的结构使得 elasticsearchi在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。不过,等等,倒排索引是什么鬼?
倒排索引
elasticsearch使用的是一种称为倒排索引的结构,采用 lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容
代码语言:javascript复制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 | ✕ | ✕ |
ever | ✓ | ✓ |
forever | ✓ | ✓ |
day | ✓ | ✓ |
study | ✕ | ✓ |
good | ✓ | ✓ |
every | ✓ | ✓ |
to | ✓ | ✕ |
up | ✓ | ✓ |
来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构
如果要搜索含有 python标签的文章,那相对于査找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这栏,然后获取相关的文章ID即可。完全过滤掉无关的所有数据,提高效率!
elasticsearche的索引和 Lucene的索引对比
在 elasticsearcht中,索引这个词被频繁使用,这就是术语的使用。在 elasticsearch中,索引被分为多个分片,每份分片是一个 Lucene的索引。所以一个 elasticsearch素引是由多个 Lucene索引组成的。别问为什么,谁让 elasticsearch使用 Lucene作为底层呢! 如无特指,说起索引都是指 elasticsearchi的索引 接下来的一切操作都在 kibana中 Dev Tools下的 Consale里完成。 基础操作
IK分词器插件
什么是IK分词器
分词:即把一段中文或者別的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱jd”会被分为我",“爱”,“j”,"d”,这显 然是不符合要求的,所以我们需要安装中文分词器K来解决这个问题。
如果要使用中文,建议使用ik分词器 IK提供了两个分词算法: ik smart和 ik max word,其中 ik smart为最少切分, ik max word为最细粒度划分!一会我们测试!
安装
下载地址:https://github.com/medcl/elasticsearch-analysis-ik 下载完毕之后,放入到我们的 elasticsearch插件即可!
通过命令查看插件是否加载:elasticsearch-plugin list
使用kibana测试 查看不同分词器的效果 其中 ik smart为最少切分
ik_max_word为细粒度拆分
自定义词典
- 在IK分词器config目录下新建一个*.dic文件
编写自己的词汇
配置到IKAnalyzer.cfg.xml中
重新启动es、kibana
Rest风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更筒洁,更有层次,更易于实现缓存等机制。
基本Rest命令说明
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档d) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST | locahost:9200/索引名称/类型名称/文档id/_ update | 修改文档 |
DELETE | localhost::9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称文档id | 查询文档通过文档d |
POST | localhost::9200/索引名称/类型名称/_search | 查询所有数据 |
索引的基本操作
运行es,kibana、es-head
创建一个索引
代码语言:javascript复制PUT /索引名/~类型名~/文档id
{
请求体
}
完成了自动増加了索引!数据也成功的添加了
那么name这个字段用不用指定类型呢。毕亮我们关系型数据库是需要指定类型的啊 默认类型:
- 字符串类型:text、keyword
- 数值类型:ling,integer,short,byte,double,float,half float,scaled float
- 日期类型:date
- te布尔值类型:boolean
- 二进制类型:binary
- 等等
指定字段的类型
代码语言:javascript复制PUT /test2
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"birthday":{
"type": "date"
}
}
}
}
获取索引信息
代码语言:javascript复制GET /索引名/~类型名~/文档id
如果自己的文档字段没有指定,那么es就会给我们默认配置字段类型
展:通过命令 elasticsearch索引情況!通过get_cat/可以获得es的当前的很多信息!
修改索引状态
POST /索引名称/类型名称/文档名称/_update
代码语言:javascript复制PUT /test3/_doc/1
{
"name":"joker",
"age":18,
"sex":"男"
}
POST /test3/_doc/1/_update
{
"doc":{
"name":"joker_dj"
}
}
删除索引
通过 DELETE命令实现州除、根据你的请求来判断是删除索引还是删除文档记录!
DELETE /索引名称/类型名称/文档名称
删除指定文档
代码语言:javascript复制DELETE 索引名称/类型名称/文档id
{
方法体
}
例:
DELETE test3/_doc/1
{
"name":"joker_dj"
}
文档的基本操作
基本操作
创建索引
代码语言:javascript复制PUT /joker/user/1
{
"name":"joker_dj",
"age":18,
"desc":"java小萌新",
"tags":["技术宅","直男"]
}
PUT /joker/user/1
{
"name":"joker_dj",
"age":18,
"desc":"java小萌新",
"tags":["技术宅","直男"]
}
PUT /joker/user/3
{
"name":"李四",
"age":5,
"desc":"合伙人",
"tags":["坏人"]
}
获取文档
GET /索引名称/类型名称/文档id
修改文档
重新put一边就是修改 (不推荐) version就是数据被改动的次数
POST _update,推荐使用这种更新方式
代码语言:javascript复制POST /索引名称/类型名称/文档id/_update
{
"doc":{
"fieds":"value"
}
}
POST /joker/user/3/_update
{
"doc":{
"name":"张三的哥哥李四"
}
}
文档搜索
简单的搜索
代码语言:javascript复制GET /索引名称/[类型名称]/[文档id]
高级搜索 条件查询
代码语言:javascript复制GET /索引名称/[类型名称]/_search?q=fieds:value
复杂操作搜索 select(排序,分页,高亮,模糊査询,精准查询!)
根据_score分数进行排序 查看谁最匹配
代码语言:javascript复制只要文档中包含value的就会被查出来 前提是被分词器捕获
GET /索引名称/类型名称/_search
{
"query": {
"match": {
"fileds": "value"
}
}
}
GET /joker/user/_search
{
"query": {
"match": {
"name": "张三"
}
}
}
过滤字段
代码语言:javascript复制GET /索引名称/类型名称/_search
{
"query": {
"match": {
"fileds": "value"
}
},
"_source": ["fileds","fileds"]
}
GET /joker/user/_search
{
"query": {
"match": {
"name": "张三"
}
},
"_source": ["desc","name"]
}
排序
代码语言:javascript复制GET /索引名称/类型名称/_search
{
"query": {
"match": {
"fileds": "value"
}
},
"_source": ["fileds","fileds"],
"sort": [
{
"fileds": {
"order": "desc"
}
}
]
}
例:
GET /joker/user/_search
{
"query": {
"match": {
"name": "张三"
}
},
"_source": ["desc","name","age"],
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
分页查询
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"match": {
"name": "张三"
}
},
"from": 0, # 起始值 page
"size": 20 # 查询大小 pageSize
}
数据下标还是从0开始的,和学的所有数据结构是一样的! /seach/{current}/{pageSize}
布尔查询
多条件查询 must 相当于 and
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"bool": {
"must ": [
{
"match": {
"name": "张三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
should相当于 or
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "张三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
not操作
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "张三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
过滤器 filter
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "张三"
}
}
],
"filter": {
"range": {
"age": {
"gt": 1,
}
}
}
}
}
}
筛选年龄大于1
- gt 大于
- lt 小于
- gte 大于等于
- lte 小于等于
匹配多个条件
多个条件之间使用空格隔开 只满足其中一个即可查出 通过分值基本判断
精确查询
term查询是直接通过倒排索引指定的词条进程精确的查找的! 关于分词:
- term :直接查询精确的
- match:会使用分词器解析!(先分析文档,然后在通过分析的文档进行查询!)
两个类型 text 、 keyword keyword不会被分词器解析 text 会被分词器解析
多个值匹配的精确查询
高亮查询
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"match": {
"name":"张三"
}
},
"highlight": {
"fields": {
"name":{
}
}
}
}
自定义高亮标签
代码语言:javascript复制GET /joker/user/_search
{
"query": {
"match": {
"name":"张三"
}
},
"highlight": {
"pre_tags":"<p class='key' style='color:red'>",
"post_tags":"</p>",
"fields": {
"name":{
}
}
}
}
集成SpringBoot
官方文档:
https://www.elastic.co/guide/index.html
查看文档
导入maven pom依赖
1、找到原生的依赖
代码语言:javascript复制<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
2、初始化 用完后关闭客户端
3、分析类中的方法
- 创建空的父工程
- 创建SpringBoot子模块
勾选相关依赖
下载相关依赖
注意版本问题:要和本地es版本一致,否则可能会出现问题
- 新建配置类
@Configuration
public class ElasticSearchclientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
return client;
}
}
- Api测试
创建索引
代码语言:javascript复制@SpringBootTest
class ElasticseachApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//索引的创建 Request
@Test
void testCerateIndex() throws IOException {
//1. 创建索引请求
CreateIndexRequest request = new CreateIndexRequest("joker_index");
//2. 执行创建请求 indices.create
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
}
判断索引是否存在
代码语言:javascript复制 @Test
void testGetindex() throws IOException {
GetIndexRequest request = new GetIndexRequest("joker_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
删除索引
代码语言:javascript复制//删除索引
@Test
void testDeleteindex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("test2");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
创建添加文档
添加fastjson依赖 方便json转换
代码语言:javascript复制<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
代码语言:javascript复制 //添加文档
@Test
void testAddDocument() throws IOException {
User user = new User("joker_dj", 18);
//创建请求
IndexRequest request = new IndexRequest("joker_index");
// 规则 put joker_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
//将我们的数据放入请求 json
request.source(JSON.toJSONString(user), XContentType.JSON);
//客户端发送请求
IndexResponse indexRespons = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexRespons.toString());
System.out.println(indexRespons.status());
}
获取文档 判断文档是否存在
代码语言:javascript复制 //获取文档 判断文档是否存在
@Test
void testexitdocument() throws IOException {
GetRequest request = new GetRequest("joker_index", "1");
//不获取返回的上下文 _source
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields("_none_");
boolean exists = client.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
获取文档的内容
代码语言:javascript复制//获取文档的内容
@Test
void testGetDocument() throws IOException {
GetRequest request = new GetRequest("joker_index", "1");
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
System.out.println(documentFields.toString());//打印文档的内容
}
更新文档内容
代码语言:javascript复制//更新文档信息
@Test
void testUpdateDocument() throws IOException {
UpdateRequest request = new UpdateRequest("joker_index", "1");
request.timeout("1s");
User user = new User("joker_djs", 20);
request.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
System.out.println(update.status());
}
删除文档记录
代码语言:javascript复制 //删除文档记录
@Test
void testDeleteRequst() throws IOException {
DeleteRequest request = new DeleteRequest("joker_index", "1");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
批量插入数据
代码语言:javascript复制 // 批量插入数据
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> userList=new ArrayList<>();
userList.add(new User("joker1",18));
userList.add(new User("joker2",18));
userList.add(new User("joker3",18));
userList.add(new User("joker4",18));
userList.add(new User("joker5",18));
userList.add(new User("joker6",18));
userList.add(new User("joker7",18));
userList.add(new User("joker8",18));
for (int i = 0; i < userList.size(); i ) {
bulkRequest.add(new IndexRequest("joker_index")
.id("" (i 1))
.source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures()); //false 成功
}
高级查询
代码语言:javascript复制//查询
//搜索请求 SearchRequest
//条件构造 SearchSourceBuilder
//MatchAllQueryBuilder
//TermQueryBuilder 精确查询
// xxx QueryBuilder
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("joker_index");
//构建搜索条件
SearchSourceBuilder SourceBuilder = new SearchSourceBuilder();
//高亮
SourceBuilder.highlighter();
//查询条件 我们可以使用QueryBuidler
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "joker1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
SourceBuilder.query(matchAllQueryBuilder);
SourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
//构建搜索
searchRequest.source(SourceBuilder);
/*客户端执行*/
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(search.getHits()));
System.out.println("==============================");
for (SearchHit hit : search.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}
实战
项目搭建
- 新建一个SpringBoot项目
勾选依赖
引入fastjson
代码语言:javascript复制 <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
配置文件
引入静态页面
启动访问
爬虫
数据问题?数据库获取,消息队列中获取中,都可以成为数据源,爬虫!
爬取数据:(获取请求返回的页面信息,筛选出我们想要的数据就可以了!)
jsoup
代码语言:javascript复制<!--解析网页jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
编写解析网页工具包
把爬取到的数据封装成对象
代码语言:javascript复制@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Content {
private String title;
private String img;
private String price;
}
封装工具类
代码语言:javascript复制public class HtmlParseUtil {
public static void main(String[] args) throws IOException {
new HtmlParseUtil().ParseJD(encode).forEach(System.out::println);
}
public List<Content> ParseJD(String keywords) throws IOException {
//获取请求 :https://search.jd.com/Search?keyword=java
//需要联网
String encode = URLEncoder.encode(keywords, "UTF-8");//url字符转义
String url="https://search.jd.com/Search?keyword=" keywords;
//解析网页 (Jsoup返回的Document对象就是JS的Document对象)
Document document = Jsoup.parse(new URL(url), 30000);
//所有js的方法都可以使用
Element element = document.getElementById("J_goodsList");
//获取所有的li标签
Elements elements = element.getElementsByTag("li");
ArrayList<Content> goods = new ArrayList<>();
for (Element el : elements) {
//关于图片特别多的网站 所有图片都是懒加载
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(title, img, price);
goods.add(content);
}
return goods;
}
}
编写elasticsearch配置类
ElasticSearchclientConfig
代码语言:javascript复制@Configuration
public class ElasticSearchclientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
return client;
}
}
创建index索引
创建业务层
代码语言:javascript复制//业务编写
@Service
public class ContentService {
@Autowired
private RestHighLevelClient restHighLevelClient;
//解析数据放入es中
public boolean parseContent(String keywords) throws IOException {
List<Content> contents = new HtmlParseUtil().ParseJD(keywords);
//把查询得到的数据放到es中
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for (int i = 0; i < contents.size(); i ) {
bulkRequest.add(new IndexRequest("jd_goods")
.source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !bulk.hasFailures();
}
}
编写controller
代码语言:javascript复制@RestController
public class ContentController {
@Autowired
ContentService service;
@GetMapping("/parse/{keywords}")
public boolean parse(@PathVariable("keywords") String keywords) throws IOException {
return service.parseContent(keywords);
}
}
运行:localhost:9090/parse/java
获取数据实现搜索功能
Service层
代码语言:javascript复制 //2. 获取数据实现搜索功能
public List<Map<String,Object>> searchPage(String keywords,int page,int pageSize) throws IOException {
if(page<=1){
page=1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(page);
sourceBuilder.size(pageSize);
//模糊匹配
MatchPhraseQueryBuilder title = QueryBuilders.matchPhraseQuery("title", keywords);
//精确匹配
// TermQueryBuilder title = QueryBuilders.termQuery("title", keywords);
sourceBuilder.query(title);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit hit : search.getHits().getHits()) {
list.add(hit.getSourceAsMap());
}
return list;
}
Controller
代码语言:javascript复制 @GetMapping("/search/{keywords}/{page}/{pageSize}")
public List<Map<String,Object>> search(@PathVariable("keywords") String keywords,@PathVariable("page") int page,@PathVariable("pageSize") int pageSize) throws IOException {
return service.searchPage(keywords, page, pageSize);
}
访问测试
前端渲染数据 vue
下载vue npm install vue npm install axios
引入工程中
编写vue代码
代码语言:javascript复制<script>
new Vue({
el:"#app",
data:{
keywords:"",//搜索的关键字
result:[]//搜索的结果
},
methods:{
searchkey(){
var keyword=this.keywords;
axios.get("/search/" keyword "/1/10").then(res=>{
this.result=res.data;
}).catch(err=>{
})
}
}
})
</script>
数据渲染
实现搜索高亮
代码语言:javascript复制//3. 获取数据实现高亮搜索功能
public List<Map<String,Object>> searchHighlightPage(String keywords,int page,int pageSize) throws IOException {
if(page<=1){
page=1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.requireFieldMatch(false);//是否多个字段高亮;
highlightBuilder.field("title");//设置高亮的字段
highlightBuilder.preTags("<span style='color:red'>");//设置前缀
highlightBuilder.postTags("</span>");//设置后缀
sourceBuilder.highlighter(highlightBuilder);
//分页
sourceBuilder.from(page);
sourceBuilder.size(pageSize);
//模糊匹配
MatchPhraseQueryBuilder titles = QueryBuilders.matchPhraseQuery("title", keywords);
//精确匹配
//TermQueryBuilder title = QueryBuilders.termQuery("title", keywords);
sourceBuilder.query(titles);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit hit : search.getHits().getHits()) {
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title = highlightFields.get("title");
Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
//解析高亮的字段
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);
}
return list;
}
contrller引用
访问测试
推荐视频 elasticsearch up主 遇见狂神说: bilibili链接