ElasticSearch全文检索
Elasticsearch是一个基于Lucene的搜索服务器。
它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
Sql: like %xxx% ,如果是大数据,就十分的慢(用索引提升效率但是还是比较慢)
ElasticSearch/Soir:搜索(百度,github,淘宝,电商)
1. ElasticSearch创始人
Doug Cutting :Lucene(基于Lucene封装的搜索引擎:soir,ElasticSearch),全文检索功能(开源)
大数据的两个问题:存储(GFS,NDFS),计算(MapReduce编程模型)
NDFS+MapReduce=Hadoop
Lucene是一套信息检索工具包,jar包,不包括搜索引擎系统
- 索引结构
- 读取索引的工具
- 排序
- 联测
- 搜索规则(工具类)
Lucene和ElasticSearch关系 :ElasticSearch是基于Lucene做了一些封装和增强(通过简单的Restful Api来隐藏Lucene的复杂性,从而让全文搜索变得简单)
日志数据分析(ELK,elasticsearch搜索+logstash过滤+kibana可视化分析)
2. 对比其他搜索引擎
ElasticSearch:(RestFult APi)基于Lucene,全文搜索,结构化搜索,分析(高亮,实时纠错,用户画像分析,数据清理)
Solr:(Web APi)基于Lucene,可以独立运行(用post方法向Solr服务器发送一个描述Field及其内容的XML文档,Slor根据XML文档添加,删除,更新索引),Solr不提供UI的功能,Solr提供一个管理界面,通过管理界面可以查询Solr的配置和运行情况
Lucene:Lucene是一套java信息检索工具包,jar包,不包括搜索引擎系统
ES和Solr的差别?
- 对单纯已有的数据进行搜索,Solr更快
- 当实时建立索引时,Solr会产生io阻塞,查询性能较差,ElasticSearch具有更明显的优势
- 随着数据量的增加,Solr的搜索效率会变得更低,而ES无明显变化
总结:
- es基本开箱即用,非常简单,Solr安装较为复杂
- Solr利用Zookeeper进行分布式管理,而es自身带有分布式协调管理功能
- Solr支持更多格式的数据,如json,xml,csv,而es仅支持json
- Solr提供的功能很多,但es本身更注重核心功能,高级功能多有第三方插件提供,例如图形化界面的kibana
- Solr查询快,但更新索引时慢(即插入删除慢),es为实时性查询
- Solr比较成熟,es相对开发维护者较少,更新太快,学习成本较高
3. 安装
- 安装es
java版本:jdk1.8以上
node.js
python
npm
下载解压即可
bin:启动文件
config配置文件
- log4j2.properties:日志配置文件
- jvm.options:java虚拟机相关配置
- elasticsearch.yaml:es配置文件 默认9200端口!跨域
jdk:环境
lib:相关jar包lucene
modules:功能模块
plugins:插件(ik分词器)
logs:日志
启动
双击bin/elasticsearch.bat文件
访问127.0.0.1:9200
- 安装可视化界面head
下载地址:https://github.com/mobz/elasticsearch-head
跨域问题(9100~9200)
es配置文件
重启es服务查看head
索引就相当于一个数据库(表就相当于一个文档,类型相当于属性)
这个head就当作是一个数据展示工具,我们后面所有的查询可以在kabana做
- 安装kibana(可视化平台)
ELK关系
- 收集清洗数据(Loastash)
- 搜索,存储(ES)
- 分析,展示(Kibana)
下载解压
启动
访问测试
开发工具(之后的所有操作都在这里进行)
修改语言
ES核心概念
集群,节点,索引,类型,文档,分片,映射是什么?
elasticSearch是面向文档 一切都是json
Mysql | ElasticSearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | 类型(types)【慢慢被弃用】 |
行(rows) | 文档(documents) |
字段(columns)属性 | 映射(fields) |
物理设计:elasticSearch在后台把每个索引划分成多个切片,每分分片可以在集群中的不同服务器上迁移
一个人就是一个集群,默认集群名称就是elasticsearch
文档 :索引的最小数据就是文档(就是一条条数据)
类型 :字段类型映射(数据类型)
索引 :数据库(一个elasticsearch索引是由多个Lucene倒排索引组成的)
分片:每个分片都是一个Lucene倒排索引
倒排索引:采用Lucene倒排索作为底层,这种结构用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。
4. 生态圈
5. ik分词器
分词:即把一段中文或者别的划分成一个个的关键词,我们在搜索时会把我们自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每一个字看成一个词,所以我们需要安装IK分词器来解决这个问题
ik分词器两种算法
- ik_smart,最少切分(不存在重复数据)
- ik_max_word,最细粒度划分(穷尽词库的可能--字典)
下载地址: https://github.com/medcl/elasticsearch-analysis-ik/releases
放入elasticsearch插件中
使用(查看不同的分词器)
问题:字典中不存在的自定义词,需要自己加入到分词器字典中
ik分词器增加自己的配置:
重启es
查看
以后需要我们自己配置分词只需要在自己定义的dic文件中进行即可
6. RestFul操作ElasticSearch
RestFul风格说明:一种软件架构风格,只是提供了一组设计原则和约束条件,它主要用于客户端与服务器交互类的软件,基于这个风格设计的软件可以更加简洁,更有层次,更易于实现缓存等机制
基本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
{
请求体
}
完成了自动增加索引,数据也成功的添加了
数据类型
字符串类型:text,keyword(不可分割)
数值类型:long,integer,short,byte,double,float,scaled
日期类型:date
布尔值:boolean
二进制:binary
-
指定字段类型
创建具体的索引规则
-
通过get请求获取信息
如果自己的文档字段没有指定,那么es就会自动给我们配置字段类型
-
修改提交还是可以使用Put即可,进行覆盖
版本号增加
使用POST修改
-
删除索引
通过DELETE命令实现删除
使用RestFul风格是es推荐使用的
7. 文档的基本操作(重点)
-
基本操作
添加数据
获取数据
更新数据
version代表这个数据被改动的次数(put如果不传值就会被覆盖)
推荐使用一下这种方式POST
查询数据
简单查询:通过默认的映射规则,产生基本的查询
GET /liuyunsan/user/1
GET /liuyunsan/user/_search?q=name:修仙
keyword只能整体搜索
匹配度(匹配度越高分值越高)
-
复杂操作(排序,分页,高亮,模糊查询,精准查询)
hit:索引和文档的信息,查询的结果总数,然后就是查询出来的具体文档,数据中的东西就可以遍历出来了,分数,我们可以通过score来判断谁更加符合结果
结果过滤
排序(desc,asc)
分页查询
/search/{current}/{pagesize}
boolean值查询(多条件精确查询)and or not
must(相当于and,所有条件都要符合)
should(相当于or,其中一个符合即可)
must_not(相当于not,都不符合的即可)
过滤器filter 区间
查询年龄大于10小于25的个数
gt:大于
gte:大于等于
lt:小于
lte:小于等于
匹配多个条件,通过权重来判别匹配的程度(多个条件空格隔开即可,只要满足其中一个结果就会被查出)
精确查询(term查询是通过倒排索引指定的词条精确查找的)
关于分词:
term:直接查找精确的值(效率高)
match:会使用分词器解析(先分析文档,再通过分析的文档进行查询)
两个类型
text:分词器解析
keyword:不会被分词器解析
多个值匹配的精确查询
高亮查询(搜索的结果会帮你自动增加一个html标签)自定义样式
- 匹配
- 按条件匹配
- 精确匹配
- 区间范围匹配
- 匹配字段过滤
- 多条件查询
- 高亮查询
- 倒排索引
- mysql也可以做,但是效率很低
8. SpringBoot集成ElasticSearch(从原理分析)
文档:https://www.elastic.co/guide/index.html
- 导入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.15.2</version>
</dependency>
- 初始化
- 分析类中方法
三个静态内部类
- RestClientBuilderConfiguration.class
- RestHighLevelClientConfiguration.class
- RestClientSnifferConfiguration.class
核心类只有一个 - ElasticsearchRestClientConfigurations
- api测试
@SpringBootTest
class EsApiApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//索引创建
@Test
void contextLoads() throws IOException {
//1.创建索引的请求
CreateIndexRequest request = new CreateIndexRequest("liuyunsan_index");
//2.客户端执行请求 IndexClient, 请求后获得响应
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
//获取索引,数据库只能判断存不存在
@Test
void testExist() throws IOException {
//1.创建索引的请求
GetIndexRequest request = new GetIndexRequest("liuyunsan_index");
//2.判断索引是否存在
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
//删除索引
@Test
void deeteExist() throws IOException {
//1.创建索引的请求
DeleteIndexRequest request = new DeleteIndexRequest("liuyunsan_index");
//2.客户端执行请求 IndexClient, 请求后获得响应
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
//测试添加文档
@Test
void insertDoc() throws IOException {
//创建对象
User user=new User("liuyunsan",3);
//创建请求
IndexRequest request = new IndexRequest("liuyunsan_index");
//规则 put /liuyunsan_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));//超时
//将我们的数据放入请求request json
request.source(JSON.toJSONString(user), XContentType.JSON);
//客户端发送请求 获取响应结果
IndexResponse index = client.index(request, RequestOptions.DEFAULT);
System.out.println(index.toString());
System.out.println(index.status()); //对应命令返回的状态
}
//获取文档,判断是否存在 GET /index/_doc/1
@Test
void getDoc() throws IOException {
GetRequest liuyunsan_index = new GetRequest("liuyunsan_index", "1");
liuyunsan_index.fetchSourceContext(new FetchSourceContext(false));//不获取返回的_source的上下文
liuyunsan_index.storedFields("_none_");//排序字段
boolean exists = client.exists(liuyunsan_index, RequestOptions.DEFAULT);
System.out.println(exists);
}
//获取文档的信息
@Test
void getDocInformation() throws IOException {
GetRequest liuyunsan_index = new GetRequest("liuyunsan_index", "1");
GetResponse documentFields = client.get(liuyunsan_index, RequestOptions.DEFAULT);
System.out.println(documentFields.getSourceAsString());
System.out.println(documentFields); //返回的全部内容和命令是一样的
}
//更新文档记录
@Test
void updateDocInformation() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("liuyunsan_index", "1");
updateRequest.timeout("1s");
User user = new User("一刹流云散", 77);
UpdateRequest doc = updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
UpdateResponse update = client.update(doc, RequestOptions.DEFAULT);
System.out.println(update);
}
//删除文档记录
@Test
void deleteDocInformation() throws IOException {
DeleteRequest liuyunsan_index = new DeleteRequest("liuyunsan_index", "1");
liuyunsan_index.timeout("1s");
DeleteResponse delete = client.delete(liuyunsan_index, RequestOptions.DEFAULT);
System.out.println(delete);
}
//真实项目一般会批量插入数据
@Test
void InsertDocInformation() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("3s");
ArrayList<User> users = new ArrayList<>();
users.add(new User("流云散1",3));
users.add(new User("流云散2",4));
users.add(new User("流云散3",5));
users.add(new User("流云散4",6));
users.add(new User("流云散5",7));
users.add(new User("流云散6",8));
users.add(new User("流云散7",9));
for (int i = 0; i <users.size() ; i++) {
//批量更新与删除就在这里修改请求即可
//如果不设置id就会生成一个随机id
bulkRequest.add(new IndexRequest("liuyunsan_index").id(""+(i+1)).source(JSON.toJSONString(users.get(i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk);
System.out.println(bulk.hasFailures());//是否执行成功
}
//查询
//SearchRequest 搜索请求
//SearchSourceBuilder 条件构造
// HighlightBuilder 构建高亮
//TermQueryBuilder 精确查询
//MatchALLQueryBuilder 全部查询
// xxxQueryBuilder 对应kibana中的命令
@Test
void QueryDocInformation() throws IOException {
SearchRequest liuyunsan_index = new SearchRequest("liuyunsan_index");
//构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//查询条件,可以使用QueryBuilders快速构建
//term 精确匹配
//match 模糊匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "流云散3");
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//放入请求
liuyunsan_index.source(sourceBuilder);
SearchResponse search = client.search(liuyunsan_index,RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(search.getHits())); //从hit中取得查询到的数据
for(SearchHit documentFields:search.getHits().getHits())
{
System.out.println(documentFields.getSourceAsMap());
}
}
}
9. 爬虫爬取数据存入ElasticSearch(模拟全文检索)
数据获取:数据库,消息队列中获取,都可以成为数据源,爬虫
爬取数据(获取请求返回的页面信息,筛选出我们想要的数据就可以了)
jsoup包
搜索相关的都可以使用ElasticSearch(最好大数据量的情况下使用)