1、什么是搜索?
百度,淘宝,拉钩,腾讯新闻,管理系统,各种app都有搜索的功能,我想得到我要的信息,输入一个关键字,找出这个关键字相关的信息。
2、如果用数据库做搜索会怎么样?
比如影片表里面有100万条数据(100万部电影),其中有一部电影的描述是"变形金刚是非常好看的一部电影....省略1000字",这时候我们使用"变金刚"关键字去搜索的话
问题1:全表去扫100万条数据并依次匹配每条数据的1000字电影描述里面有没有包含"变金刚",性能会很差。
问题2:电影描述里包含的是“变形金刚”,而用"变金刚"关键字产生的sql条件是"like %变金刚%",因此查不出任何数据,所以说这个搜索是不靠谱的。
全文检索,同义词处理,相关度排名,复杂数据分析,海量数据的近实时处理,这些都是数据库的缺陷。es作为传统数据库的补充,提供了数据库所不能提供的很多功能。
3、什么是全文检索?
(1)什么是倒排索引?
当用户在主页上搜索关键词“华为手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“华为手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。
所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。
得到倒排索引的结构如下:
“关键词1”:“文档1”的ID,“文档2”的ID,…………。
“关键词2”:带有此关键词的文档ID列表。
从词的关键字,去找文档。
4、什么是Elasticsearch?
(1)什么是lucene?
lucene就是一个jar包,里面包含了封装好的各种建立倒排索引,以及进行搜索的代码,包括各种算法。我们就用java开发的时候,引入lucene jar,然后基于lucene的api进行去进行开发就可以了。用lucene,我们就可以去将已有的数据建立索引,lucene会在本地磁盘上面,给我们组织索引的数据结构。另外的话,我们也可以用lucene提供的一些功能和api。
lucene是一款先进、功能最强大的搜索库,直接基于lucene开发,非常复杂,api复杂(实现一些简单的功能,写大量的java代码),需要深入理解原理(各种索引结构)。
而Elasticsearch是基于lucene,隐藏其复杂性,提供简单易用的restful api接口的分布式,高性能,高可用,可伸缩的搜索和分析系统。
1)分布式的文档存储引擎
2)分布式的搜索引擎和分析引擎
3)分布式,支持PB级数据
5、Elasticsearch基本概念
(1)集群(cluster)
一个Elasticsearch集群由一个或多个Elasticsearch节点组成,并提供集群内所有节点的联合索引和搜索能力(所有节点共同存储数据)。一个集群被命名为唯一的名字(默认为elasticsearch),集群名称非常重要,因为节点需要通过集群的名称加入集群。请确保在不同的集群中使用不同的集群名称,否则会导致节点添加到错误的集群中。
(2)节点(node)
一个节点是集群中的一个服务器,用来存储数据并参与集群的索引和搜索。和集群类似,节点由一个名称来标识,默认情况下,该名称是在节点启动时分配给节点的随机通用唯一标识符(UUID)。您也可以自定义任意节点的名称,节点名称对于管理工作很重要,因为通过节点名称可以确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以被添加到指定名称的集群中。默认情况下,每个节点会被设置加入到名称为elasticsearch的集群中,这意味着,如果在您在网络中启动了某些节点(假设这些节点可以发现彼此),它们会自动加入名称为elasticsearch的集群中。
一个集群可以拥有任意多的节点。此外,如果在您的网络中没有运行任何Elasticsearch节点,此时启动一个节点会创建一个名称为elasticsearch的单节点集群。
(3)索引(index)
一个索引是一个拥有一些相似特征的文档的集合,相当于关系型数据库中的一个数据库。例如,您可以拥有一个客户数据的索引,一个商品目录的索引,以及一个订单数据的索引。一个索引通常使用一个名称(所有字母必须小写)来标识,当针对这个索引的文档执行索引、搜索、更新和删除操作的时候,这个名称被用来指向索引。
Elasticsearch与关系型数据库的对应关系:
Elasticsearch 关系型数据库
索引(index) 数据库(Database)
文档类型(type) 表(Table)
文档(document) 一行数据(Row)
字段(field) 一列数据(Column)
映射(mapping) 数据库的组织和结构(Schema)
(4)类型(type)
一个类型通常是一个索引的一个逻辑分类或分区,允许在一个索引下存储不同类型的文档,相当于关系型数据库中的一张表,例如用户类型、博客类型等。目前已经不支持在一个索引下创建多个类型,并且类型概念已经在后续版本中删除,详情请参见Elasticsearch官方文档。
(5)文档(document)
一个文档是可以被索引的基本信息单元,相当于关系型数据库中的一行数据。例如,您可以为一个客户创建一个文档,或者为一个商品创建一个文档。文档可以用JSON格式来表示。在一个索引中,您可以存储任意多的文档,且文档必须被索引。
(6)字段(field)
组成文档的最小单位,相当于关系型数据库中的一列数据。
(7)映射(mapping)
用来定义一个文档以及其所包含的字段如何被存储和索引,相当于关系型数据库中的Schema,例如在mapping中定义字段的名称和类型,以及所使用的分词器。
(8)分片(shards)
代表索引分片,Elasticsearch可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上,构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。
一个分片可以是主分片或副本分片。Elasticsearch 7.0以下版本默认为一个索引创建5个主分片,并分别为每个主分片创建1个副本分片,7.0及以上版本默认创建1个主分片和1个副本分片。两者区别如下:
分片类型支持处理的请求数量是否可修改其他说明
主分片支持处理查询和索引请求。在创建索引时设定,设定后不可更改。索引内任意一个文档都存储在一个主分片中,所以主分片的数量和大小决定着索引能够保存的最大数据量。
注意: 主分片不是越多越好,因为主分片越多,Elasticsearch性能开销也会越大。
副本分片支持处理查询请求,不支持处理索引请求。可在任何时候添加或删除副本分片。副本分片对搜索性能非常重要,主要体现在以下两个方面:
提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。
提高Elasticsearch的查询效率,Elasticsearch会自动对搜索请求进行负载均衡。
(9)recovery
代表数据恢复或数据重新分布,Elasticsearch在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。
(10)gateway
代表Elasticsearch索引快照的存储方式,Elasticsearch默认优先将索引存放到内存中,当内存满时再将这些索引持久化存储至本地硬盘。gateway对索引快照进行存储,当这个Elasticsearch集群关闭再重新启动时就会从gateway中读取索引备份数据。Elasticsearch支持多种类型的gateway,有本地文件系统(默认)、分布式文件系统、Hadoop的HDFS和阿里云的OSS云存储服务。
(11)discovery.zen
代表Elasticsearch的自动发现节点机制,Elasticsearch是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播协议进行节点之间的通信,同时也支持点对点的交互。
(12)Transport
Transport代表Elasticsearch内部节点或集群与客户端的交互方式,默认使用TCP协议进行交互。同时,通过插件的方式集成,也支持使用HTTP协议(JSON格式)、thrift、servlet、memcached、zeroMQ等传输协议进行交互。
6、Elasticsearch应用:
(1)安装
声明:JDK1.8,最低要求!ElasticSearch 客户端,界面工具!
Java开发,ElasticSearch 的版本和我们之后对应的Java 的核心jar包!版本对应!JDK 环境是正常的。
官网:https://www.elastic.co/
下载地址:https://www.elastic.co/cn/downloads/elasticsearch
官网下载巨慢,翻墙,或从网上找已下载好的资源
ELK三剑客,解压即用!
window 下安装!
1.解压就可以使用了
2.熟悉目录
bin 启动文件config 配置文件 log4j2 日志配置文件 jvm.options java虚拟机相关的配置 elasticsearch.yml elasticsearch的配置文件,默认 9200 端口,跨域问题!lib 相关jar包logs 日志modules 功能模块plugins 插件 ik分词器
3.启动,访问 9200(注意,第一次在本机上尝试启动报错,需要在yaml文件中加一行配置:xpack.ml.enabled: false)
4.访问测试!
安装可视化界面 es head的插件
此处必须要有 VUE 基础,并且需要有 node.js 的基本环境已安装
1.下载地址:https://github.com/mobz/elasticsearch-head/
2.启动
npm installnpm run start
3.连接测试发现,存在跨域问题,配置es的yaml配置文件
# 解决跨域http.cors.enabled:truehttp.cors.allow-origin:"*"
4.重启es服务器,然后再次连接
我们初学时,就把es当做一个数据库~(可以建立索引(库),文档(库中的数据!))
这个head我们就把它当做数据展示工具,我们后面所有的查询,Kibana
(2)了解ELK
ELK是Elasticsearch、Logstash、Kibana三大开源框架首字母大写简称。市面上也被称为Elastic Stack。其中Elasticsearch 是一个基于Luncene、分布式、通过Restful方式进行交互的近实时搜索平台框架。像类似百度,谷歌这种大数据全文搜索引擎的场景都可以使用Elasticsearch作为底层支持框架,可见Elasticsearch提供的搜索能力确实强大,市面上很多时候我们简称Elasticsearch为es。Logstash是ELK的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana可以将elasticsearch的数据通过友好的页面展示出来,提供实时分析功能。
收集清洗数据 -- 》 搜索,存储 --》 展示Kibana
市面上很多开发只要提到ELK能够一致说出他是一个日志分析架构技术总称,但实际上ELK不仅仅适用于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。
(3)安装 Kibana
Kibana 是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示Elasticsearch查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引监测。
官网:https://www.elastic.co/cn/kibana
Kibana版本要和es版本一致!
下载完毕后,解压需要一些时间。
好处:ELK基本上都是拆箱即用!
启动测试
1.解压后的目录
2.启动
3.访问测试
4.开发工具(Post,curl,head,谷歌浏览器插件测试)
我们之后的所有操作都在这里进行编写
5.汉化,修改Kibana的配置文件yaml即可,修改完成后重启项目
(4)IK分词器插件
什么是IK分词器?
分词:即把一段中文或别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“大程子”会被分为“大”,“程”,“子”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
如果要使用中文,建议选择使用ik分词器!
IK提供了两个分词算法:ik_smart 和 ik_max_word , 其中 ik_smart 为最少切分,ik_max_word 为最细粒度划分,后面进行测试。
安装
1.https://github.com/medcl/elasticsearch-analysis-ik
2.下载完毕之后,放入到我们的elasticsearch插件中即可
3.重启观察ES,可以看到ik分词器被加载了!
4.elasticsearch-plugin 可以通过这个命令来查看加载进来的插件
5.使用kibana测试!
查看不同的分词器效果
ik_smart 为最少切分
ik_max_word 为最细粒度划分,穷尽词库的可能,字典
我们输入“超级喜欢大程子学Java”
发现问题:大程子被拆开了!
这种自己需要的词,需要自己加到我们的分词器的字典中!
ik 分词器增加自己的配置
重启es,看细节
再次测试以下大程子,看下效果
以后的话,我们需要自己配置分词就在自已定义的dic文件中进行配置即可!
Rest风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基于Rest命令说明:
methodurl地址描述
PUTlocalhost:9200/索引名称/类型名称/文档id创建文档(指定文档id)
POSTlocalhost:9200/索引名称/类型名称创建文档(随机文档id)
POSTlocalhost:9200/索引名称/类型名称/文档id/_update修改文档
DELETElocalhost:9200/索引名称/类型名称/文档id删除文档
GETlocalhost:9200/索引名称/类型名称/文档id查询文档通过文档id
POSTlocalhost:9200/索引名称/类型名称/_search查询所有数据
关于索引的基本操作
1.创建一个索引
put/索引名/~类型名~/文档id{请求体}
完成了自动增加索引!数据也成功的添加了,这就是为什么在初期可以把它当做数据库学习的原因!
3.那么name这个字段用不用指定类型呢。毕竟我们关系型数据库 是需要指定类型的
字符串类型
text、keyword
数值类型
long、integer、short、byte、double、float、half_float、scaled_float
日期类型
date
布尔值类型
boolean
二进制类型
binary
等等……
4.指定字段的类型
获得这个规则,可以通过GET请求获取具体的信息!
5.查看默认的信息
如果自己的文档字段没有指定,那么es就会给我们默认配置字段类型!
扩展:通过命令 elasticsearch 索引情况!通过 get _cat/ 可以获得es的当前的很多信息!
修改 提交还是使用PUT 即可! 然后覆盖! 最新办法
曾经
现在的方法
删除索引
通过 DELETE 命令实现删除、根据你的请求来判断是删除索引还是删除文档记录!
使用 RESTFULL 风格是我们ES推荐大家使用的!
关于文档的基本操作(es的重点)
基本操作
1.添加数据
PUT/wangcp/user/3{"name":"李四","age":30,"desc":"emm,不知道如何形容","tags":["靓女","旅游","唱歌"]}
2.查询获取数据 GET
3.更新数据 PUT
Post _update,推荐使用这种更新方式!
简单的搜索
GET wangcp/user/1
简单的条件查询,可以根据默认的映射规则,产生基本的查询!
复杂操作搜索 select(排序,分页,高亮,模糊查询,精准查询!)
输出结果过滤,不想要那么多,select name,desc
我们之后使用Java操作es,所有的方法和对象就是这里面的key!
排序
分页查询
数据索引下标还是从0开始的,和学的所有数据结构还是一样的。
/search/{current}/{pagesize}
布尔值查询
must(and),所有的条件都要符合 where id=1 and name=xxx
should(or),所有的条件都要符合 where id=1 or name=xxx
must_not( not )
过滤器 filter
gt 大于
gte 大于等于
lt 小于
lte 小于等于
匹配多个条件
精确查询
term 查询时直接通过倒排索引指定的词条进行精确查找的!
关于分词:
term:直接查询精确的
match:会使用分词器解析!(先分析文档,然后在通过分析的文档进行查询!)
两个类型 text keyword
多个值匹配的精确查询
高亮查询
这些其实 MySQL 也可以做,只是 MySQL 效率较低
匹配
按照条件匹配
精确匹配
区间范围匹配
匹配字段过滤
多条件查询
高亮查询
倒排索引
集成SpringBoot
找官方文档
1.找到原生的依赖
2.找对象
3.分析这个类中的方法
配置基本的项目
==问题:一定保证 我们导入导入的依赖和我们的es版本一致==
源码中提供的对象
虽然这里导入3个类,静态内部类,核心类就一个。
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/packageorg.springframework.boot.autoconfigure.elasticsearch.rest;importjava.time.Duration;importorg.apache.http.HttpHost;importorg.apache.http.auth.AuthScope;importorg.apache.http.auth.Credentials;importorg.apache.http.auth.UsernamePasswordCredentials;importorg.apache.http.client.CredentialsProvider;importorg.apache.http.impl.client.BasicCredentialsProvider;importorg.elasticsearch.client.RestClient;importorg.elasticsearch.client.RestClientBuilder;importorg.elasticsearch.client.RestHighLevelClient;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;importorg.springframework.boot.context.properties.PropertyMapper;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/**
* Elasticsearch rest client infrastructure configurations.
*
* @author Brian Clozel
* @author Stephane Nicoll
*/classRestClientConfigurations{@Configuration(proxyBeanMethods=false)staticclassRestClientBuilderConfiguration{// RestClientBuilder@Bean@ConditionalOnMissingBeanRestClientBuilderelasticsearchRestClientBuilder(RestClientPropertiesproperties,ObjectProvider
具体的API测试!
1.创建索引
@TestvoidtestCreateIndex()throwsIOException{// 1.创建索引请求 相当于 PUT wang_indexCreateIndexRequestrequest=newCreateIndexRequest("wang_index");// 2.客户端执行请求CreateIndexResponsecreateIndexResponse=client.indices().create(request,RequestOptions.DEFAULT);System.out.println(createIndexResponse);}
2.判断索引是否存在
@TestvoidtextExistIndex()throwsIOException{GetIndexRequestrequest=newGetIndexRequest("wang_index");booleanexists=client.indices().exists(request,RequestOptions.DEFAULT);System.out.println(exists);}
3.删除索引
@TestvoidtextDeleteIndex()throwsIOException{DeleteIndexRequestrequest=newDeleteIndexRequest("wang_index");AcknowledgedResponsedelete=client.indices().delete(request,RequestOptions.DEFAULT);System.out.println(delete.isAcknowledged());}
4.创建文档
// 测试添加文档@TestvoidtestAddDocument()throwsIOException{//创建对象Useruser=newUser("大程子",3);//创建请求IndexRequestrequest=newIndexRequest("wang_index");// 规则 put /wang_index/_doc/1request.id("1");request.timeout(TimeValue.timeValueSeconds(1));// 将我们的数据放入请求IndexRequestsource=request.source(JSON.toJSONString(user),XContentType.JSON);// 客户端发送请求,获取响应的结果IndexResponseindexResponse=client.index(source,RequestOptions.DEFAULT);System.out.println(indexResponse);System.out.println(indexResponse.status());//对应我们命令返回的状态 CREATED}
5.添加文档
@TestvoidtestAddDocument()throwsIOException{//创建对象Useruser=newUser("大程子",3);//创建请求IndexRequestrequest=newIndexRequest("wang_index");// 规则 put /wang_index/_doc/1request.id("1");request.timeout(TimeValue.timeValueSeconds(1));// 将我们的数据放入请求IndexRequestsource=request.source(JSON.toJSONString(user),XContentType.JSON);// 客户端发送请求,获取响应的结果IndexResponseindexResponse=client.index(source,RequestOptions.DEFAULT);System.out.println(indexResponse);System.out.println(indexResponse.status());//对应我们命令返回的状态 CREATED}
6.获取文档判断是否存在
// 获取文档判断是否存在@TestvoidtestIsExists()throwsIOException{GetRequestgetRequest=newGetRequest("wang_index","1");// 不获取返回的 _source 的上下文了getRequest.fetchSourceContext(newFetchSourceContext(false));getRequest.storedFields("_none_");booleanexists=client.exists(getRequest,RequestOptions.DEFAULT);System.out.println(exists);}
7.获得文档信息
// 获得文档的信息@TestvoidtestGetDocument()throwsIOException{GetRequestgetRequest=newGetRequest("wang_index","1");GetResponsegetResponse=client.get(getRequest,RequestOptions.DEFAULT);System.out.println(getResponse.getSourceAsString());// 打印文档内容System.out.println(getResponse);//获得的全部内容和使用命令是一致的}
8.更新文档信息
// 更新文档的信息@TestvoidtestUpdateDocument()throwsIOException{UpdateRequestupdateRequest=newUpdateRequest("wang_index","1");updateRequest.timeout("1s");Useruser=newUser("大程子的技术成长路",18);updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);UpdateResponseupdateResponse=client.update(updateRequest,RequestOptions.DEFAULT);System.out.println(updateResponse.status());}
9.删除文档信息
// 删除文档信息@TestvoidtestDeleteDocument()throwsIOException{DeleteRequestrequest=newDeleteRequest("wang_index","1");request.timeout("1s");DeleteResponsedelete=client.delete(request,RequestOptions.DEFAULT);System.out.println(delete.status());}
10.批量插入数据
// 特殊的,真实项目一般都会批量插入数据@TestvoidtestBulkRequest()throwsIOException{BulkRequestbulkRequest=newBulkRequest();bulkRequest.timeout("10s");ArrayList 11.搜索查询 // 查询// SearchRequest 搜索请求// SearchSourceBuilder 条件构造// HighlightBuilder 高亮构建// TermQueryBuilder 构建精确查询@TestvoidtestSearch()throwsIOException{SearchRequestsearchRequest=newSearchRequest("wang_index");//构建搜索条件SearchSourceBuildersourceBuilder=newSearchSourceBuilder();sourceBuilder.highlighter();// 查询条件,我们可以使用 QueryBuilders 工具来实现// QueryBuilders.termQuery 精确查找// QueryBuilders.matchAllQuery() 匹配所有TermQueryBuildertermQueryBuilder=QueryBuilders.termQuery("name","wangcp1");// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();sourceBuilder.query(termQueryBuilder);// 设置查询最大时间sourceBuilder.timeout(newTimeValue(60,TimeUnit.SECONDS));searchRequest.source(sourceBuilder);SearchResponsesearchResponse=client.search(searchRequest,RequestOptions.DEFAULT);System.out.println(JSON.toJSONString(searchResponse.getHits()));System.out.println("=======================================");for(SearchHitdocumentFields:searchResponse.getHits().getHits()){System.out.println(documentFields.getSourceAsMap());}}