ElasticSearch详细笔记
ElasticSearch详细笔记
什么是ElasticSearch
Elasticsearch(简称ES)是一个基于Apache Lucene(TM)的开源搜索引擎,无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。注意,Lucene 只是一个库。想要发挥其强大的作用,你需使用 Java 并要将其集成到你的应用中。
重要特性:
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 实时分析的分布式搜索引擎
- 可以扩展到上百台服务器,处理PB级结构化或非结构化数据
基本概念&倒排索引
需要了解ElasticSearch中的一些基本概念。
- 索引(indices)
-- Databases 数据库
- 类型(type)
-- Table 数据表
- 文档(Document)
-- Row 行
- 字段(Field)
-- Columns 列
ElasticSearch中的倒排索引
ElasticSearch在插入数据的同时还会为这些数据维护了一张倒排索引表,通过这个倒排索引可以大大的提高搜索的性能
倒排索引(Inverted Index)也叫反向索引,有反向索引必有正向索引。简单来讲,正向索引是通过key找value,反向索引则是通过value找key。
举个例子:
comment
表有id
、content
两个字段,现在向comment
表插入如下一条数据:id:1
content:今天天气很好
ElasticSearch
会把content
的内容进行分词,可以分成三个词:今天
、天气
、很好
。倒排索引表就如下:今天 [1]
天气 [1]
很好 [1]
表示
"今天"、"天气"、"很好"
这三个词在1
号记录中存在。再向
comment
表中插入一条数据:id: 2
content: 今天天气好冷
继续将
content
的内容进行分词,得到:今天
、天气
、好冷
。将这三个词添加到倒排索引表中今天 [1,2]
天气 [1,2]
很好 [1]
好冷 [2]
"今天"
、"天气"
这两个词在1号
、2号
记录中都存在。"很好"
在1号
记录存在"好冷"
在2号
记录存在现在查询记录,检索条件:今天好冷
通过
"今天好冷"
这个字符串进行检索记录,这种就属于通过value
查找key
。ElasticSearch
首先将"今天好冷"
进行分词为:"今天"
、"好冷"
两个词。然后在倒排索引表中查询,发现
"今天"
这个词命中了1号
和2号
记录,再看"好冷"
这个词命中了2号
记录。这里有个评分机制,
2号
记录经过对比发现命中2次
,1号
记录命中1次
。因此2号
记录的评分就比1号
记录高。查询出来的结果顺序就是:id: 2 content: 今天天气好冷
id: 1 content: 今天天气很好
这就是倒排索引的基本逻辑,通过 value
查找 key
。实际上,ElasticSearch引擎创建的倒排索引比这个复杂得多。
安装ElasticSearch&Kibana
Docker安装ElasticSearch
下载镜像
docker pull elasticsearch:7.4.2 #存储和检索数据
创建实例需要挂载目录
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml chmod -R 777 /mydata/elasticsearch/
创建运行实例
docker run --name es -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
-e "discovery.type=single-node"
:单实例模式-e ES_JAVA_OPTS="-Xms64m -Xmx512m"
:设置运行的初始内存和最大内存-v
:将本地文件映射到容器中对应文件浏览器访问主机
9200
端口
Docker安装Kibana
Kibana 是一个基于 Node.js 的 Elasticsearch 索引库数据统计工具,可以利用 Elasticsearch 的聚合功能,生成各种图表,如柱形图,线状图,饼图等。
而且还提供了操作 Elasticsearch 索引数据的控制台,并且提供了一定的API提示,非常有利于我们学习 Elasticsearch 的语法。
安装步骤:
下载镜像
docker pull kibana:7.4.2
查看
ElasticSearch
实例地址docker inspect es
es
:运行的elasticsearch
容器实例名创建运行实例
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://172.17.0.3:9200 -p 5601:5601
-d kibana:7.4.2
172.17.0.3
:地址填写上一步查询到的地址浏览器访问主机
5601
端口注意:kibana启动可能有点慢,需要等待一会
ES基本操作
_cat
elasticsearch
提供 _cat
API 来查看ElasticSearch
状态
#0. 查看_cat支持的命令
GET /_cat
#1. 查看所有节点
GET /_cat/nodes
#2. 查看es健康状态
GET /_cat/health
#3. 查看主节点
GET /_cat/master
#4. 查看所有索引
GET /_cat/indices
例子:
http://192.168.23.6:9200/_cat/indices
新增数据
elasticsearch
中保存的都是 json
格式的数据
现在向 es
添加一条数据 { "msg": "Hello ElasticSearch" }
访问
kibana
,选择Dev Tools
在这个界面操作
es
PUT 方式
PUT /news/comment/1
{
"msg":"Hello ElasticSearch"
}
执行结果:
POST 方式
POST /news/comment/1
{
"name":"Hello ElasticSearch"
}
执行结果:
可以理解为向
news
数据库的comment
表中添加了一条记录,不过这里叫做索引和类型
分析结果:
_index: 索引,对应就是数据库名
_type: 类型,对应就是数据表
_id: 数据的id
_version: 版本号,通过操作数据版本号会不断增加
_result: created表示创建了一条数据,如果重新put一条数据,则该状态会变为updated,并且版本号也会发生变化。
_shards: 分片信息
_seq_no: 序列号
_primary_term:
- PUT可以新增也可以修改。PUT必须指定id;
- POST添加数据的时候不指定id,会自动的生成id,并且类型是新增
- 由于PUT需要指定id,我们一般用来做修改操作,不指定id会报错。
查询数据
查看使用GET请求方式检索数据
GET /news/comment/1
可以理解为向
news
数据库的comment
表中查询一条id
为1
的记录
_source
:保存的数据
更新数据
POST 方式
POST /news/comment/1/_update
{
"doc": {
"msg": "Hello ES"
}
}
POST /customer/external/1
{
"msg": "Hello ES"
}
使用
_update
需要加doc
区别:
- 使用
_update
修改数据,版本号不会增加
- 使用
PUT 方式
PUT /news/comment/1
{
"msg": "Hello ES"
}
删除数据
删除一条数据
DELETE /news/comment/1
删除一个索引
DELETE /customer
bulk批量API
bulk相当于数据库里的bash操作, 其支持的操作类型包括:index, create, update, delete
bulk 语法:
{ action: { metadata } }
{ requstbody }
批量新增 index
POST /news/comment/_bulk
{ "index": {"_id": 2} }
{ "msg": "zhangsan" }
{ "index": {"_id": 3} }
{ "msg": "lisi" }
{ "index": {"_id": 4} }
{ "msg": "wangwu" }
执行结果:
参数解析:
{ "index": {"_id": 2} }
{ "msg": "zhangsan" }
这两行为一次操作,第一行指定了数据的id(还可以指定
index
、type
;以下划线开头)第二行是保存的数据体
结果解析:
"took": 31
:请求执行时间(毫秒)
"error": false
:请求是否出错,返回flase表示没有出错
"items"
:操作过的文档的具体信息
"static"
:响应状态码
批量新增 create
POST /news/comment/_bulk
{ "create": {"_id": 2} }
{ "msg": "zhangsan" }
{ "create": {"_id": 3} }
{ "msg": "lisi" }
{ "create": {"_id": 4} }
{ "msg": "wangwu" }
执行结果:
新增失败了,因为
id
重复问题
create
方式新增,如果id
已存在了就会报错index
方式新增,如果id
存在不会报错,并且version
增加
批量更新 update
POST /news/comment/_bulk
{ "update": {"_id": 2} }
{ "doc":{"msg": "zhangsan.cn"} }
{ "update": {"_id": 3} }
{ "doc":{"msg": "lisi.cn"} }
{ "update": {"_id": 4} }
{ "doc":{"msg": "wangwu.cn"} }
执行结果:
更新操作需要多加一层
doc
。
{ "update": {"_id": 2} }
{ "doc":{"msg": "zhangsan.cn"} }
第一行
update
为更新操作,并指定了更新数据的id 第二行
doc
里面是更新的新数据。
批量删除 delete
批量删除不需要请求体(数据体)
POST /news/comment/_bulk
{ "delete": {"_id": 2} }
{ "delete": {"_id": 3} }
{ "delete": {"_id": 4} }
执行结果:
进阶检索
学习之前先为es添加一些测试数据,这里使用官方提供的测试数据 https://gitee.com/depthch/elasticsearch/blob/master/doc/test/resourses/accounts.json
打开上面链接将里面的数据复制
在 kibana 中使用 bulk 批量添加数据
在索引为
bank
,类型account
中批量插入了数据
ES支持两种基本方式检索
- 通过REST request uri 发送搜索参数 (uri +检索参数)
- 通过REST request body 来发送它们(uri+请求体)
_search
请求方式:uri + 检索参数
1、检索 bank
下所有信息
GET /bank/_search
响应结果解析:
- took:Elasticsearch执行搜索的时间(毫秒)
- time_out:告诉我们搜索是否超时
- _shards:告诉我们多少个分片被搜索了,以及统计了成功/失败的搜索分片
- hits:搜索结果
- hits.total:搜索结果数量
- hits.hits:实际的搜索结果数组(默认为前10的文档)
- sort:结果的排序key (键) (没有则按score排序)
- score和max score:相关性得分和最高得分(全文检索用)
2、检索 bank
下所有信息,并按照 account_number
升序
GET /bank/_search?q=*&sort=account_number:asc
q=*
:* 是通配符,表示查询所有的数据sort=account_number:asc
:按照 account_number 排序,asc 是升序
queryDSL
请求方式:uri + 请求体
基本语法
QUERY_NAME:{
ARGUMENT:VALUE,
ARGUMENT:VALUE,...
}
1、检索 bank
下所有信息,并按照 account_number
降序
GET /bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"account_number": {
"order": "desc"
}
}
]
}
查询参数解析:
- match_all查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询;
- 除了query参数之外,我们可也传递其他的参数以改变查询结果,如sort,size;
- from+size限定,完成分页功能;
- sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;
_source
_source:返回指定的部分字段
GET bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"account_number": {
"order": "desc"
}
}
],
"_source": ["balance","firstname"]
}
_source:指定返回的部分字段
match匹配查询
基本类型(非字符串),精确控制
GET bank/_search
{
"query": {
"match": {
"account_number": "20"
}
}
}
字符串,全文检索
GET bank/_search
{
"query": {
"match": {
"address": "kings"
}
}
}
match_phrase
match_phrase: 短句匹配,将需要匹配的值当成一整个单词(不分词)进行检索
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}
查出
address
中包含mill road
的所有记录,并给出相关性得分
match_phrase
和match
的区别:
- match:匹配时会分词,如:mill road,会拆分成:mill、road。然后检索出包含这两个词的记录(包含其中一个词也满足条件)
- match_phrase:匹配时不会分词,如:mill road,会被当成一个整体来检索记录,必须包含整个整体的记录才会被检索出来
match.keyword
keyword
是精确匹配,就是说某条记录必须完全满足匹配条件才会被检索出来
GET bank/_search
{
"query": {
"match": {
"address.keyword": "990 Mill Road"
}
}
}
multi_math
multi_math:多字段匹配可以在多个字段中去匹配条件
GET bank/_search
{
"query": {
"multi_match": {
"query": "mill",
"fields": [
"state",
"address"
]
}
}
}
state
或者address
中包含mill
,并且在查询过程中,会对于查询条件进行分词。
bool
bool:用来做复合查询,复合语句可以合并,任何其他查询语句,包括符合语句。这也就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。
must
must:必须达到must所列举的所有条件
GET bank/_search
{
"query":{
"bool":{
"must":[
{"match":{"address":"mill"}},
{"match":{"gender":"M"}}
]
}
}
}
匹配
gender
为M
并且address
包含mill
的文档
must_not
must_not,必须不匹配must_not所列举的所有条件。
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"gender": "M"
}
},
{
"match": {
"address": "mill"
}
}
],
"must_not": [
{
"match": {
"age": "38"
}
}
]
}
}
匹配
gender
为M
并且address
包含mill
的文档,但是age
不等于38
的数据
should
should:应该达到should列举的条件,如果到达会增加相关文档的评分,并不会改变查询的结果。
如果query中只有should且只有一种匹配规则,那么should的条件就会被作为默认匹配条件二区改变查询结果。
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"gender": "M"
}
},
{
"match": {
"address": "mill"
}
}
],
"must_not": [
{
"match": {
"age": "18"
}
}
],
"should": [
{
"match": {
"lastname": "Wallace"
}
}
]
}
}
}
should
是"应该包含"
的意思,不是必须包含,也就是除去其他匹配条件即使lastName
不包含Wallace
也能匹配成功。但是如果有数据的lastName
包含Wallace
,那么这条数据的相关性得分会更高,即优先匹配。
Filter
并不是所有的查询都需要产生分数,特别是哪些仅用于filtering过滤的文档。为了不计算分数,elasticsearch会自动检查场景并且优化查询的执行。
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
}
],
"filter": {
"range": {
"balance": {
"gte": "10000",
"lte": "20000"
}
}
}
}
}
}
这里先是查询所有匹配
address
包含mill
的文档,然后再根据10000<=balance<=20000
进行过滤查询结果
filter
在使用过程中,并不会计算相关性得分,即 "_score" : 0.0
term
和match一样
。匹配某个属性的值。
全文检索字段用
match
非text
字段匹配用term
。
GET bank/_search
{
"query": {
"term": {
"address": "mill Road"
}
}
}
使用
term
匹配text
类型数据是匹配不到任何数据的。
Aggregation
聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL Group by和SQL聚合函数。在elasticsearch中,执行搜索返回this(命中结果),并且同时返回聚合结果,把以响应中的所有hits(命中结果)分隔开的能力。这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API啦避免网络往返。
聚合语法:
"aggs":{
"aggs_name这次聚合的名字,方便展示在结果集中":{
"AGG_TYPE聚合的类型(avg,terms....)":{}
}
}
常用聚合类型:
- avg:求平均值
- max:求最大值
- min:求最小值
- sum:求和
- filter:过滤聚合。基于一个条件,来对当前的文档进行过滤的聚合。
- terms:词聚合。基于某个field,该 field 内的每一个【唯一词元】为一个桶,并计算每个桶内文档个数。默认返回顺序是按照文档个数多少排序。
搜索address中包含mill的所有人的年龄分布以及平均年龄
GET bank/_search
{
"query": {
"match": {
"address": "Mill"
}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 10
}
},
"ageAvg": {
"avg": {
"field": "age"
}
}
}
}
查出所有年龄分布,并且求这些年龄段的这些人的平均薪资
GET bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 100
},
"aggs": {
"ageAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
聚合是可以嵌套聚合的。
查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资以及这个年龄段的总体平均薪资
GET bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 100
},
"aggs": {
"genderAgg": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"balanceAvg": {
"avg": {
"field": "balance"
}
}
}
},
"ageBalanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
先按年龄聚合查出了所有分布情况,再嵌套聚合 按性别聚合查出分布情况,最后再嵌套聚合 按薪资聚合查出平均薪资。
ageBalanceAvg
:根据年龄分布计算出平均工资,这个聚合跟性别无关。
mapping
maping是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的。比如:使用maping来定义:
- 哪些字符串属性应该被看做全文本属性(full text fields);
- 哪些属性包含数字,日期或地理位置;
- 文档中的所有属性是否都能被索引(all 配置);
- 日期的格式;
- 自定义映射规则来执行动态添加属性;
查看 bank 索引的 mapping
映射信息
GET bank/_mapping
执行结果:
在
properties
中可以看到每个field
的字段类型
新版本的改变
ElasticSearch7 去掉了type(表)概念
- 关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。
- 两个不同type(表)下的两个名称 user_name(字段),在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type(表)中定义相同的 filed 映射。否则,不同 type(表)中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。
- 去掉type(表)就是为了提高ES处理数据的效率。
- Elasticsearch 7.x URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
- Elasticsearch 8.x 不再支持URL中的type参数。
创建映射
PUT /student
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
执行结果:
创建
student
索引并指定了索引的mapping
映射信息。
properties
:指定映射的字段和字段类型
查看映射
GET /student
执行结果:
添加新的字段映射
为 student
索引添加一个新的字段映射
PUT /student/_mapping
{
"properties": {
"id": {
"type": "keyword",
"index": false
}
}
}
执行结果:
再次查看映射:
"index": false
:表明新增的字段不能被检索,只是一个冗余字段。
数据迁移
由于ElasticSearch
是不支持修改映射字段的,只能添加映射字段。如果必须修改就需要数据迁移。
需求:将 bank 索引的所有数据迁移到 newbank 索引下,并将 age
字段类型改为 integer
。修改 city、email、employer、gender
等字段类型改为 keyword
具体步骤:
查看 bank 的映射信息
GET /bank
创建一个跟
bank
索引字段相同mapping
映射的索引,并且改变字段类型PUT /newbank
{
"mappings": {
"properties": {
"account_number": {
"type": "long"
},
"address": {
"type": "text"
},
"age": {
"type": "integer"
},
"balance": {
"type": "long"
},
"city": {
"type": "keyword"
},
"email": {
"type": "keyword"
},
"employer": {
"type": "keyword"
},
"firstname": {
"type": "text"
},
"gender": {
"type": "keyword"
},
"lastname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"state": {
"type": "keyword"
}
}
}
}
在指定映射信息时改变了字段的类型。
可以查看到
newbank
索引的映射信息GET /newbank
执行结果:
字段类型都已经改变。
将
bank
索引中的数据迁移到newbank
索引中POST _reindex
{
"source": {
"index": "bank",
"type": "account"
},
"dest": {
"index": "newbank"
}
}
执行结果:
source
:指定旧索引信息index
:指定旧的索引名type
:指定 type,如果没有 type 可以不指定。
dest
:指定新索引信息index
:指定新的索引名
查看
newbank
中的数据GET /newbank/_search
执行结果:
检索了1000条数据,数据迁移成功。发现
type
:_doc
,我们在创建映射关系时并没有设置type
,这是因为ElasticSearch7
去掉了type
(表)概念,但是有个默认的type
就是_doc
。
分词
一个 tokenizer(分词器)
接收一个字符流,将之分割为独立的 tokens(词元,通常是独立的单词)
,然后输出tokens
流。例如:hello world
遇到空白字符时分割文本。它会将文本 "hello world"
分割为 [hello, world]
。
tokenizer(分词器
)还负责记录各个terms(词条)
的顺序或position
位置(用于phrase短语和word proximity词近邻查询),以及term(词条)
所代表的原始word(单词)
的start(起始)
和 end(结束)
的 character offsets(字符串偏移量)
(用于高亮显示搜索的内容)
。
elasticsearch
提供了很多内置的分词器,可以用来构建 custom analyzers(自定义分词器)
。
使用分词器
POST _analyze
{
"analyzer": "standard",
"text": "Nothing is impossible!"
}
执行结果:
standard
:ElasticSearch默认的分词器,默认就是按空格进行分词的
安装 ik 分词器
所有的语言分词,默认使用的都是“Standard Analyzer”,但是这些分词器针对于中文的分词,并不友好。为此需要安装中文的分词器。
具体步骤:
查看 ElasticSearch 的版本
访问es主机的 9200 端口查看es版本
下载对应版本的 ik 分词器
在前面安装的elasticsearch时,我们已经将elasticsearch容器的“/usr/share/elasticsearch/plugins”目录,映射到本地主机的“ /mydata/elasticsearch/plugins”目录下.
2.1)进入
/mydata/elasticsearch/plugins
目录cd /mydata/elasticsearch/plugins
2.2)下载 ik 分词器
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
注意
7.4.2
是自己es对应的版本。解压下载好的文件
unzip elasticsearch-analysis-ik-7.4.2.zip -d ik
重启es
docker restart es
查看安装好的ik
GET _cat/plugins
执行结果:
可以看到我们的ik分词器已经配置成功了
使用ik分词器
ik分词器有两种分词模式:ik_max_word
和 ik_smart
模式。
ik_max_word
会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
先来看看默认的分词器:
GET _analyze
{
"text":"每天都要努力"
}
执行结果:
ik 分词器:
GET _analyze
{
"analyzer": "ik_smart",
"text":"每天都要努力"
}
执行结果:
可以看到使用 ik 分词器可以把一些常用的中文词分出来了。
自定义词库
虽然使用 ik 分词器默认的词库已经可以实现常用的中文分词了,但是如果我们要分的词不常用,如:张明想学Java
GET _analyze
{
"analyzer": "ik_smart",
"text":"张明想学Java"
}
执行结果:
可以看到这里把
"张明"
拆成了"张"
和"明"
,这并我是预想的效果,"张明"
应该拆成整体。
使用自定义词库,因为要使用 "远程扩展字典"
,因此就需要一个远程的字典文件。这里可以使用 nginx
来配置远程扩展字典文件。文档最后有nginx安装和配置步骤
具体步骤:
配置好 nginx 后,创建词库文件
vi /mydata/nginx/html/fenci.txt
添加如下内容并保存:
张明
学java
修改es-plugins配置文件
vi /mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict"></entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://192.168.16.6/fenci.txt</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
注意:修改第10行需要改成
nginx
服务的地址(这行默认是注释的)。修改了配置文件需要重启 es
docker restart es
再次使用 ik 分词器
GET /_analyze
{
"analyzer": "ik_max_word",
"text":"张明想学Java"
}
//TODO 执行结果
....
SpringBoot 整合 ElasticSearch
SpringBoot
可以通过 9200
和9300
端口来调用 ElasticSearch
,它们之间的区别:
9300:TCP
SpringBoot
提供了spring-data-elasticsearch:transport-api.jar;
来对ES
调用。这种方式有些缺陷:- springboot版本不同,transport-api.jar不同,不能适配es版本
- es7.x已经不建议使用,es8以后就要废弃
9200:HTTP
jestClient
:非官方,更新慢;RestTemplate
:模拟HTTP请求,ES很多操作需要自己封装,麻烦;HttpClient
:模拟HTTP请求,ES很多操作需要自己封装,麻烦;
Elasticsearch-Rest-Client
:官方RestClient,封装了ES操作,API层次分明,上手简单;
根据上面分析,我们最终选择 Elasticsearch-Rest-Client
来进行调用es
。
具体步骤:
添加依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.2</version>
</dependency> <dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.2</version>
</dependency> <dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.4.2</version>
</dependency>
创建一个
ElasticSearch
的配置类@Configuration
public class ElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
COMMON_OPTIONS = builder.build();
} @Bean
public RestHighLevelClient esRestClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("192.168.16.6", 9200, "http")));
return client;
}
}
HttpHost("192.168.16.6", 9200, "http")
- 192.168.16.6:es服务的地址
- 9200:es服务9200端口
- http:使用http协议
配置类中创建了一个
JavaBen
,之后通过这个JavaBean
来调用ElasticSearch
的相关API
保存数据
@RunWith(SpringRunner.class)
@SpringBootTest
public class MallSearchApplicationTests {
@Autowired
RestHighLevelClient client;
/**
* 测试保存数据
*/
@Test
public void contextLoads() throws IOException {
IndexRequest request = new IndexRequest("users"); //创建索引对象
request.id("10"); //设置id
//source方法可以直接传入多个键值对值保存
//request.source("name", "lisi", "age", 24, "gender", "男");
User user = new User(); //创建一个实体类user
user.setName("java");
user.setAge(24);
user.setGender("男");
String jsonString = JSON.toJSONString(user); //解析实体转成json字符串
request.source(jsonString, XContentType.JSON); //传入json格式字符串保存
IndexResponse response = client.index(request, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println(response); //打印结果
}
/**
* 定义 user 实体类
*/
@Data
class User{
private String name;
private int age;
private String gender;
}
}
执行结果:
IndexResponse[index=users,type=_doc,id=10,version=2,result=updated,seqNo=3,primaryTerm=6,shards={"total":2,"successful":1,"failed":0}]
kibana查看:
数据测试添加成功。
检索数据
搜索address中包含mill的所有人的年龄分布以及平均年龄
QueryDSL
实现:
GET bank/_search
{
"query": {
"match": {
"address": "Mill"
}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 10
}
},
"ageAvg": {
"avg": {
"field": "age"
}
}
}
}
执行结果:
java
代码实现:
首先需要生成实体类,因为从es获取的数据在java中最终都会保存为java对象。
需要根据 _source 中的字段生成 java 类 account
测试类代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MallSearchApplicationTests {
@Autowired
RestHighLevelClient client;
/**
* 按照 bank 索引里的 _source 数据字段创建对应的实体类
*/
@Data
@ToString
static class Account{
private int account_number;
private int balance;
private String firstname;
private String lastname;
private int age;
private String gender;
private String address;
private String employer;
private String email;
private String city;
private String state;
}
/**
* 检索数据
*/
@Test
public void searchData() throws IOException {
//1、创建检索对象
SearchRequest searchRequest = new SearchRequest();
//指定索引
searchRequest.indices("bank");
//指定DSL&检索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//构建检索条件
//sourceBuilder.query();
//sourceBuilder.from();
//sourceBuilder.size();
//sourceBuilder.aggregation();
sourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));
//构建聚合条件: 按照年龄值分布进行聚合
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
sourceBuilder.aggregation(ageAgg);
//构建聚合条件: 计算平均工资
AvgAggregationBuilder ageAvgAgg = AggregationBuilders.avg("ageAvgAgg").field("age");
sourceBuilder.aggregation(ageAvgAgg);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
//结果分析
SearchHits hits = searchResponse.getHits();
SearchHit[] hitsHits = hits.getHits();
for(SearchHit hit : hitsHits){
String sourceAsString = hit.getSourceAsString();
//将结果转成 javeBean
Account account = JSON.parseObject(sourceAsString, Account.class);
System.out.println("account:" + account);
}
//获取检索到的聚合信息
Aggregations aggregations = searchResponse.getAggregations();
Terms ageAgg1 = aggregations.get("ageAgg");
// 打印聚合结果
for (Terms.Bucket bucket : ageAgg1.getBuckets()) {
String keyAsString = bucket.getKeyAsString();
System.out.println("年龄:" + keyAsString + "===> " + bucket.getDocCount());
}
Avg balanceAgg1 = aggregations.get("ageAvgAgg");
System.out.println("平均年龄:" + balanceAgg1.getValueAsString());
}
}
执行结果:
这里执行结果跟queryDSL查询是相同的。
附:Docker 安装 Nginx
随便启动一个nginx实例,只是为了复制出配置
docker run -p 80:80 --name nginx -d nginx:1.10
创建目录并且将容器内部配置文件拷贝到外部
mkdir -p /mydata/nginx/html
mkdir -p /mydata/nginx/logs
mkdir -p /mydata/nginx/conf
docker container cp nginx:/etc/nginx/* /mydata/nginx/conf/
#由于拷贝完成后会在conf中存在一个nginx文件夹,所以需要将它的内容移动到conf中
mv /mydata/nginx/conf/nginx/* /mydata/nginx/conf/
rm -rf /mydata/nginx/conf/nginx
终止容器&删除原来的容器
docker stop nginx
docker rm nginx
创建新的 nginx 容器
docker run -p 80:80 --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
ElasticSearch详细笔记的更多相关文章
- ElasticSearch学习笔记(超详细)
文章目录 初识ElasticSearch 什么是ElasticSearch ElasticSearch特点 ElasticSearch用途 ElasticSearch底层实现 ElasticSearc ...
- ElasticSearch NEST笔记
ElasticSearch NEST笔记 1. 什么是ElasticSearch? ElasticSearch is a powerful open source search and analyti ...
- (转载)Linux下安装配置MySQL+Apache+PHP+WordPress的详细笔记
Linux下安装配置MySQL+Apache+PHP+WordPress的详细笔记 Linux下配LMAP环境,花了我好几天的时间.之前没有配置过,网上的安装资料比较混乱,加上我用的版本问题,安装过程 ...
- Elasticsearch学习笔记一
Elasticsearch Elasticsearch(以下简称ES)是一款Java语言开发的基于Lucene的高效全文搜索引擎.它提供了一个分布式多用户能力的基于RESTful web接口的全文搜索 ...
- xshell远程终端操作Ubuntu server安装LAMP环境之最详细笔记之二PHP开发环境配置
前言: 昨天学会了安装server,今天试着通过远程终端xshell来安装LAMP,搭配一下开发环境,也有集成环境可以一键安装使用,还是瞎折腾一下,手动一步一步搭建一下这个开发环境. 接上一篇:ubu ...
- elasticsearch学习笔记——相关插件和使用场景
logstash-input-jdbc学习 ES(elasticsearch缩写)的一大优点就是开源,插件众多.所以扩展起来非常的方便,这也造成了它的生态系统越来越强大.这种开源分享的思想真是与天朝格 ...
- 清晰易懂!关于PS入门的超详细笔记!
给大家分享一篇关于PS入门的超详细笔记!原理讲解清晰明了,虽不是新版本解析,但都是新手学习PS必掌懂的一些知识点,灰常的实用,转走收藏学习! 编辑:千锋UI设计 来源:PS学堂
- java编写service详细笔记 - centos7.2实战笔记(windows类似就不在重复了)
java编写service详细笔记 - centos7.2实战笔记(windows类似就不在重复了) 目标效果(命令行启动服务): service xxxxd start #启动服务 servic ...
- python聚类算法实战详细笔记 (python3.6+(win10、Linux))
python聚类算法实战详细笔记 (python3.6+(win10.Linux)) 一.基本概念: 1.计算TF-DIF TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库 ...
随机推荐
- 解决vue侧边栏一级菜单问题
最近我在学习vue,然后遇到一个问题,就是跟着视频里面的代码敲,出现了一些不好解决的问题 这是两个一级目录,我遇到的问题就是点击第一个一级目录,另外一个一级目录也会展开, 前端代码是这样的,和视频里面 ...
- 我把公司 10 年老系统改造 Maven,真香!!
公司有几个老古董项目,应该是 10 年前开发的了,有一个是 JSP + Servlet,有一个还用的 SSH 框架,打包用的 Ant,是有多老啊,我想在座的各位很多都没听过吧. 为了持续集成.持续部署 ...
- 常用JVM 启动参数解析
https://www.jianshu.com/p/a17dcef57559 https://www.cnblogs.com/zkyefei/p/9334562.html
- 提权 EXP
windows: 漏洞列表 #Security Bulletin #KB #Description #Operating System CVE-2017-0213 [Windows COM Eleva ...
- C语言实现数据结构的邻接矩阵----数组生成矩阵、打印、深度优先遍历和广度优先遍历
写在前面 图的存储结构有两种:一种是基于二维数组的邻接矩阵表示法. 另一种是基于链表的的邻接表表示法. 在邻接矩阵中,可以如下表示顶点和边连接关系: 说明: 将顶点对应为下标,根据横纵坐标将矩阵中的某 ...
- 花时三月 终于Spring Boot 微信点餐开源系统! 附源码
架构 前后端分离: Nginx与Tomcat的关系在这篇文章,几分钟可以快速了解: https://www.jianshu.com/p/22dcb7ef9172 补充: set ...
- 什么是垃圾搜集(GC)?为什么要有GC呢?
GC的全称是Gabage Collection,翻译过来就是"垃圾收集"的意思.那么我们为什么用GC呢? 那么我们接下来就来聊一聊GC的创造背景.在C和C++那个年代的程序员界的长 ...
- 超详细的 Vagrant 上手指南
搭建 Linux 虚拟机,别再用 VirtualBox 从 .iso 文件安装了. 概述 2020 年了,也许你已经习惯了 docker,习惯了在 XX 云上快速创建云主机,但是如果你想在个人电脑上安 ...
- PHP判断是否是微信浏览器访问的方法
PHP判断是否是微信浏览器访问的方法 PHP判断是否是微信浏览器访问的方法 都是干货,微信开发可能需要用到,留着日后COPY. public function isWeichatBrowser() { ...
- django 的初始项目结构
2.创建Django项目 root@dev:shiyanlou_project# workon syl (syl) root@dev:shiyanlou_project# cd /aaa/shiy ...