ElasticSearch
ElasticSearch是一个基于Lucene的搜索服务器,是一个分布式、高扩展、高实时的搜索与数据分析引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。官网:https://www.elastic.co/
应用场景:
搜索:海量数据的查询
日志数据分析
实时数据分析
在使用关系型数据库查询数据时存在的弊端:
例如我们在操作关系型数据库实现模糊查询时:
-
性能低:使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低
-
功能弱:如果以”华为手机“作为条件,查询不出来数据
1.1倒排索引
将文档进行分词,形成词条和id的对应的关系即为反向索引
1.2ElasticSearch存储和查询的原理
在ElasticSearch中的:
index(索引):相当于mysql的库
映射:相当于mysql 的表结构
document(文档):相当于mysql的表中的数据
ElasticSearch使用倒排索引,对文档中title进行分词:
-
使用“手机”作为关键字查询
生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度
-
使用“华为手机”作为关键字查询
华为:1,3
手机:1,2,3
1.3ElasticSearch核心概念
索引(index)
ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。
映射(mapping)
mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。
文档(document)
Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。
倒排索引
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表。
类型(type)
一种type就像一类表。如用户表、角色表等。在Elasticsearch7.X默认type为_doc
-
ES 5.x中一个index可以有多种type。
-
ES 6.x中一个index只能有一种type。
-
ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc
1.4操作ElasticSearch
#----------操作索引------------
# 创建person索引
PUT person
# 查询指定名称的索引信息
GET person
# 查询所有索引
GET _all
# 关闭索引
POST person
# 打开索引
POST person
# 删除索引
DELETE person
1.5ElasticSearch数据类型
简单数据类型
-
字符串
text:会分词,不支持聚合
keyword:不会分词,将全部内容作为一个词条,支持聚合
-
数值 integer、long、float、double
-
布尔:boolean
-
二进制:binary
-
范围类型
integer_range, float_range, long_range, double_range, date_range
-
日期:date
复杂数据类型
-
数组:[ ] Nested:
nested
(for arrays of JSON objects 数组类型的JSON对象) -
对象:{ } Object: object(for single JSON objects 单个JSON对象)
1.6映射操作
# 创建索引
PUT person
# 查询索引
GET person
# 给person创建映射
PUT person/_mapping
{
"properties":{
"name":{
"type":"text"
},
"age":{
"type":"integer"
}
}
}
# 只查询person索引的映射信息
GET person/_mapping
# 删除索引,不允许直接删除映射
DELETE person
# 创建索引的同时创建映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type":"text"
},
"age":{
"type":"integer"
}
}
}
}
# 给映射添加字段,和创建映射一样
PUT person/_mapping
{
"properties":{
"address":{
"type":"text"
}
}
}
1.7文档操作
# 添加文档,指定id
POST person/_doc/1
{
"name":"张益达",
"age":30,
"address":"武汉"
}
# 添加文档,不指定id会自动生成一个id
POST person/_doc
{
"name":"张大炮",
"age":33,
"address":"武汉"
}
# 查询所有文档
GET person/_search
# 查询id=1的文档
GET person/_doc/1
# 修改文档,和添加文档一样,id存在就是修改
POST person/_doc/1
{
"name":"snake",
"age":33,
"address":"武汉"
}
# 删除指定id的文档
DELETE person/_doc/1
分词器
2.1分词器介绍
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包
是一个基于Maven构建的项目
具有60万字/秒的高速处理能力
支持用户词典扩展定义
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/archive/v7.4.0.zip
2.2分词器的使用
IK分词器有两种分词模式:ik_max_word
和ik_smart
模式
ik_max_word : 会将文本做最细粒度的拆分,比如会将 “湖北省武汉市” 拆分为 “湖北省、湖北、省、武汉市、武汉、市。
#方式一ik_max_word
GET _analyze
{
"tokenizer": "ik_max_word",
"text": ["湖北省武汉市"]
}
ik_max_word : 分词器执行如下:
{
"tokens" : [
{
"token" : "湖北省",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "湖北",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "省",
"start_offset" : 2,
"end_offset" : 3,
"type" : "CN_CHAR",
"position" : 2
},
{
"token" : "武汉市",
"start_offset" : 3,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "武汉",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "市",
"start_offset" : 5,
"end_offset" : 6,
"type" : "CN_CHAR",
"position" : 5
}
]
}
ik_smart 模式:会做最粗粒度的拆分,比如会将“湖北省武汉市”拆分为湖北省、武汉市。
#方式二ik_smart
GET _analyze
{
"tokenizer": "ik_smart",
"text": ["湖北省武汉市"]
}
ik_smart分词器执行如下:
{
"tokens" : [
{
"token" : "湖北省",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "武汉市",
"start_offset" : 3,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 1
}
]
}
2.3使用分词器查询文档
关键词查询:term
关键词查询不会分析查询条件,只有当关键词和索引完全匹配时才匹配搜索
全文查询:match
全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集
数据准备
创建索引,添加映射,并指定分词器为ik分词器
# 创建新的索引和映射
PUT user
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"age":{
"type": "integer"
},
"address":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
注意:在创建映射的时候,对于text类型的字段要指定分词器,否则默认使用standard分词器,一个中文一个词。
添加文档
#如果映射中没有age自动,此处会自动创建age自动
PUT person/_doc/1
{
"name":"张三",
"age":20,
"address":"北京海淀区"
}
PUT person/_doc/2
{
"name":"李四",
"age":21,
"address":"北京朝阳区"
}
PUT person/_doc/3
{
"name":"王五",
"age":22,
"address":"北京昌平区"
}
PUT person/_doc/4
{
"name":"赵六",
"age":23,
"address":"华为5G手机"
}
查询映射
GET user/_mapping
{
"user" : {
"mappings" : {
"properties" : {
"address" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"age" : {
"type" : "long"
},
"name" : {
"type" : "keyword"
}
}
}
}
}
词条查询:term
查询user中匹配到"北京"两字的词条
GET user/_search
{
"query": {
"term": {
"address": {
"value": "北京"
}
}
}
}
结果:可以查到“北京朝阳区”、“北京昌平区”、“北京海淀区”。如果value=“北京天安门”,那么就一个都查不到了,因为查询条件不会被分词,需要完全匹配。
全文查询:match
全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集
GET /user/_search
{
"query": {
"match": {
"address":"北京天安门"
}
}
}
结果:可以查到“北京朝阳区”、“北京昌平区”、“北京海淀区”
springboot整合ElasticSearch见下篇