该文档使用的ElasticSearch版本为7.4.2
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/index.html
快速开始:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/getting-started-search.html
Docker安装
需要给挂载的目录给777权限 chmod -R 777 文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 version: '3.8' services: elasticsearch_742: container_name: elasticsearch_742 image: elasticsearch:7.4.2 restart: always ports: - "9200:9200" - "9300:9300" environment: - "discovery.type=single-node" - "ES_JAVA_OPTS=-Xms64m -Xmx512m" networks: - xiaofei-net volumes: - ./elasticsearch_742/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - ./elasticsearch_742/data:/usr/share/elasticsearch/data - ./elasticsearch_742/plugins:/usr/share/elasticsearch/plugins kibana_742: image: kibana:7.4.2 container_name: kibana_742 restart: always ports: - "5601:5601" environment: SERVER_NAME: kibana742 ELASTICSEARCH_HOSTS: http://elasticsearch_742:9200 volumes: - ./kibana_742/config/kibana.yml:/usr/share/kibana/config/kibana.yml networks: - xiaofei-net depends_on: - elasticsearch_742 links: - elasticsearch_742 networks: xiaofei-net:
elasticsearch.yml
kibana.yml 1 2 3 4 5 server.name: kibana server.host: "0" elasticsearch.hosts: [ "http://elasticsearch:9200" ]xpack.monitoring.ui.container.elasticsearch.enabled: true i18n.locale: "zh-CN"
基本概念
索引(index):相当于MySQL数据库
类型(_type):相当于MySQL的表,7.x建议使用_doc
,如果使用其他的type,则该索引下面的的type都应该一致,不能出现一个索引下面指定多个type
_ type是早期版本的设计缺陷。在5.x以前的版本里边,一个所以下面是支持多个type的。6版本以后改为只支持一个type, type可以自定义。7以后所有的typr就默认为_doc,7.。8版本后移除type
文档(Document):相当于MySQL表里面的内容,类型全是JSON格式
文档存在某个索引下面的某个类型下面
索引增删改查
下列操作都是在kibana
里面进行的操作,如果使用接口调试工具进行调试,需要在地址前面加上ElasticSearch的地址
新增 / 修改【索引、类型、文档】
PUT:必须指定id
POST:如果指定id,则使用指定的id,执行新增 / 修改操作;如果未指定id,则会自动生成id且添加数据
1 2 3 4 5 6 7 8 9 10 11 12 13 # 使用put # 格式:【/索引/_doc/id】 或 【/索引/_create/id】 PUT /test/_doc/1 { "username":"xiaofei" } # 使用post # 格式:【/索引/_doc/id】 或 【/索引/_doc】 或 【/索引/_create/id】 POST /test/_doc/1 { "username":"测试" }
删除【索引、类型、文档】 1 2 3 4 5 6 7 # 删除文档 # 格式:DELETE /索引/_doc/id DELETE /test/_doc/1 # 删除索引 # 格式:DELETE /索引 DELETE /test
修改
批量添加的数据不能换行,必须在一行显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 修改一条数据,如果存在则修改,不存在则添加 # 格式:/索引/_doc/id POST /test/_doc/1 { "username":"测试" } # 批量修改 / 添加,如果存在则添加,如果不存在则创建 # 格式 : # POST /索引/_bulk # {"index" :{_id:"id值" }} # 数据 # {"index" :{_id:"id值" }} # 数据 POST /test/_bulk {"index":{"_id":"1"}} {"username":"测试","password":"123456"} {"index":{"_id":"2"}} {"username":"测试2","password":"123456"} {"index":{"_id":"3"}} {"username":"测试3"}
查询 查询DSL文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl.html
测试数据:https://github.com/elastic/elasticsearch/blob/7.4/docs/src/test/resources/accounts.json
1 2 3 # 添加测试数据 POST /bank/_bulk 上面下载的测试数据
一个是通过使用REST request URI发送搜索参数(uri + 检索参数)
另一个是通过使用REST request body来发送它们(uri + 请求体)
基本DSL查询使用
该文档查询根据MySQL查询方式来进行对照编写,因为ES分页有默认值,所以MySQL语句都会加上分页,文档中对应的SQL语句只能用于参考
1 2 3 4 5 6 7 8 9 10 SELECT ...... , ...... , ......FROM ......WHERE 不包含组函数的过滤条件 AND / OR 不包含组函数的过滤条件 GROUP BY ...... , ...... , ......HAVING 包含组函数的过滤条件 ORDER BY ...... ASC / DESC LIMIT ...... , ......
match_all & match【匹配查询】 1 2 3 4 5 6 7 8 9 10 11 12 # MySQL,查询全部字段、分页 SELECT *FORM bank LIMIT 0,2 # ES GET /bank/_search { "query": { "match_all": {} }, "from": 0, "size": 2 }
1 2 3 4 5 6 7 8 9 10 11 12 13 # MySQL:查询部分字段、分页 SELECT account_number,balance,firstname FORM bank LIMIT 0,2 # ES GET /bank/_search { "_source": ["account_number","balance","firstname"], "query": { "match_all": {} }, "from": 0, "size": 2 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # MySQL:查询部分字段、分页、排序、排序(排序只能是数字和日期类型) SELECT account_number,balance,firstname FORM bank ORDER BY account_number DESC LIMIT 0,2 # ES GET /bank/_search { "_source": ["account_number","balance","firstname"], "query": { "match_all": {} }, "sort": [ { "account_number": { "order": "desc" } } ], "from": 0, "size": 2 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # MySQL:查询全部字段、分词查询,等值查询,ES还会做分词 SELECT * FORM bank WHERE address LIKE '%mill Road%' #ES:match ( # 字符串:分词匹配,mill Road会分为mill和Road去进行匹配 # 数字:等值匹配,因为数字不能进行分词了 # ) GET / bank/ _search{ "query": { "match": { "address": "mill Road" } } } # MySQL:查询全部字段、分词查询,等值查询 SELECT * FORM bank WHERE account_number = 1 # ES GET / bank/ _search{ "query": { "match": { "account_number": 1 } } }
match_phrase【短词匹配】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # MySQL:查询全部字段、分页、分词查询,等值查询 SELECT *FORM bank WHERE address LIKE '%mill Road%' # ES:match_phrase( # 字符串:全词匹配,mill Road不会再进行分词去进行匹配,必须分词中有mill Road才能匹配成功 # 数字:等值匹配,因为数字不能进行分词了 # ) GET /bank/_search { "query": { "match_phrase": { "address": "mill Road" } } }
multi_match【多字段匹配】 1 2 3 4 5 6 7 8 9 10 11 12 13 # MySQL:查询全部字段、分页、分词查询,等值查询 SELECT *FORM bank WHERE address LIKE '%mill Road%' OR `state` LIKE '%mill Road%' # ES:multi_match(mill Road会再进行分词,在state或address中只要分词匹配上就能命中) GET /bank/_search { "query": { "multi_match": { "query": "mill Road" , "fields": ["state","address"] } } }
bool【复合查询】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 # MySQL SELECT *FROM bank WHERE address LIKE '%mill%' AND gender = 'M' AND email NOT LIKE '%baluba.com%' # address 包含mill,并且 gender 是M,如果 address 里面有 lane 最好不过,但是 email 必 须不包含 baluba.com # must:必须达到列举的条件 # should:应该达到 should 列举的条件,如果达到会增加相关文档的评分,并不会改变 查询的结果。如果 query 中只有 should 且只有一种匹配规则,那么 should 的条件就会被作为默认匹配条件而去改变查询结果 # must_not:必须不是指定的情况 GET bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "gender": "M" } } ], "should": [ { "match": { "address": "lane" } } ], "must_not": [ { "match": { "email": "baluba.com" } } ] } } }
filter【过滤查询】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # MySQL SELECT *FROM bank WHERE address LIKE '%mill%' AND gender='%M%' AND balance >= 10000 AND balance <=20000 # filter结果过滤:仅仅过滤结果,不会影响相关性得分 GET bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "gender": "M" } } ], "filter": { "range": { "balance": { "gte": 10000, "lte": 20000 } } } } } }
term【非text精确匹配】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # MySQL SELECT *FROM table WHERE address LIKE '%mill%' AND age = 28 # term:对于非文本匹配用term精确匹配 GET bank/_search { "query": { "bool": { "must": [ { "term": { "age": { "value": "28" } } }, { "match": { "address": "Mill" } } ] } } }
aggs【聚合查询】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # MySQL分组 SELECT age , COUNT(*) FROM `user1` GROUP BY age; SELECT AVG(balance) FROM `user1`; # aggs分组统计,size:0,只做聚合查询,不做数据查询 # 统计age的各个年龄段的人数 # 统计所有人的平均薪资 GET bank/_search { "aggs": { "group_by_age": { "terms": { "field": "age" } }, "group_by_balance": { "avg": { "field": "balance" } } }, "size": 0 } # MySQL分组 SELECT age , COUNT(*) , AVG(balance) FROM `user1` GROUP BY age; # 查询每个年龄段的平均人的薪资 GET bank/_search { "aggs": { "group_by_age": { "terms": { "field": "age" }, "aggs": { "group_by_balance": { "avg": { "field": "balance" } } } } }, "size": 0 }
Mapping
ElasticSearch中新建Mapping映射相当于MySQL中的建表和修改表,如果没有指定映射,则会在第一次添加数据的时候,ES会自动猜测每一个字段的类型,并且自动创建映射。新增和修改新增,相当于MySQL的添加字段和修改字段,建立Mapping有利于更精确的管理Elastic Search中的类型
字段数据类型 Elasticsearch 支持文档中字段的多种不同数据类型
参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping-types.html
类型
细分
核心数据类型
String
text、keyword
Numeric
long、integer、short、byte、double、float、half_float、scaled_float
Date
date
Date nanoseconds
date_nanos
Boolean
boolean
Binary
binary
Range
integer_range、float_range、long_range、double_range、date_range
复杂数据类型
Object
object:对于单个JSON对象
Nested
nested:对于JSON对象数组
地理数据类型
Geo-point
geo_point:对于纬度/经度点
Geo-shape
geo_shape:对于多边形等复杂形状
专用数据类型
IP
ip:IPv4 和 IPv6
Completion datatype
completion to:provide auto-complete suggestions
Token count
token_count:计算字符串中标记的数量
mapper-murmur3
murmur3:在索引时计算值的哈希值并将它们存储在索引中
mapper-annotated-text
annotated-text:索引包含特殊标记的文本(通常用于识别命名实体)
Percolator
接受来自 query-dsl 的查询
Join
定义同一索引中文档的父/子关系
Rank feature
记录数字特征以提高查询时的命中率。
Rank features
记录数字特征以提高查询时的命中率。
Dense vector
记录浮点值的密集向量。
Sparse vector
记录浮点值的稀疏向量。
Search-as-you-type
为查询优化的类似文本的字段,以实现键入时完成
Alias
定义现有字段的别名。
Flattened
允许将整个 JSON 对象作为单个字段进行索引。
Shape
shape:对于任意笛卡尔几何。
Mapping增删改查 增 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 格式 PUT /索引名称 { "mappings": { "properties": { "属性名称": { "type": "属性类型" }, "属性名称": { "type": "属性类型" }, ...... } } } # 案例 PUT /my-index/_mapping { "mappings": { "properties": { "age": { "type": "integer" }, "email": { "type": "keyword" }, "name": { "type": "text" } } } }
删 1 2 3 # mapping的删除就是索引的删除 # 格式:DELETE /索引 DELETE /test
改
修改映射只能添加属性,不能对原有的属性进行修改和删除,如果需要修改原有字段,只能创建新的映射然后进行数据迁移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # 格式 PUT /索引名称/_mapping { "mappings": { "properties": { "属性名称": { "type": "属性类型" }, "属性名称": { "type": "属性类型" }, ...... } } } PUT /my-index/_mapping { "mappings": { "properties": { "age": { "type": "integer" }, "email": { "type": "keyword" }, "name": { "type": "text" }, "gender": { "type": "keyword" } } } }
数据迁移 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 创建一个新的mapping PUT /new_bank { "mappings": { "properties": { "属性名称": { "type": "属性类型" } ...... } } } # 数据迁移格式,迁移到新的索引的type ,默认是 _doc POST _reindex { "source": { "index": "老索引名称", "type" : "老索引的type" }, "dest": { "index": "新索引名称" } } # 使用:有一个老的索引 bank,需要迁移到新的索引 new_bank POST _reindex { "source": { "index": "bank", "type" : "_doc" }, "dest": { "index": "new_bank" } }
查 1 2 3 4 5 # 查看索引,格式 GET /索引名称/_mapping # 使用 GET /bank/_mapping
ES插件安装 IK分词器
ES默认的分词只能对英文进行分词,对于中文的分词会将中文全部拆分成一个子,所以需要安装IK分词器对中文进行分词
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # 未安装IK分词器进行分词 POST _analyze { "text": "我是中国人" } # 分词结果 { "tokens" : [ { "token" : "我", "start_offset" : 0, "end_offset" : 1, "type" : "<IDEOGRAPHIC>", "position" : 0 }, { "token" : "是", "start_offset" : 1, "end_offset" : 2, "type" : "<IDEOGRAPHIC>", "position" : 1 }, { "token" : "中", "start_offset" : 2, "end_offset" : 3, "type" : "<IDEOGRAPHIC>", "position" : 2 }, { "token" : "国", "start_offset" : 3, "end_offset" : 4, "type" : "<IDEOGRAPHIC>", "position" : 3 }, { "token" : "人", "start_offset" : 4, "end_offset" : 5, "type" : "<IDEOGRAPHIC>", "position" : 4 } ] }
下载 & 安装 7.4.2:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
将压缩包内的文件上传到映射的plugins下面的ik文件夹下后,对ik文件夹加权限chmod -R 777 /xiaofei/elasticsearch_742/plugins/ik
使用IK分词器 使用analyzer
指定IK分词器ik_smart
或ik_max_word
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 使用 analyzer 指定ik分词器 ik_smart POST _analyze { "analyzer": "ik_smart", "text": "我是中国人" } # 结果 { "tokens" : [ { "token" : "我", "start_offset" : 0, "end_offset" : 1, "type" : "CN_CHAR", "position" : 0 }, { "token" : "是", "start_offset" : 1, "end_offset" : 2, "type" : "CN_CHAR", "position" : 1 }, { "token" : "中国人", "start_offset" : 2, "end_offset" : 5, "type" : "CN_WORD", "position" : 2 } ] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # 使用 analyzer 指定ik分词器 ik_max_word POST _analyze { "analyzer": "ik_max_word", "text": "我是中国人" } # 结果 { "tokens" : [ { "token" : "我", "start_offset" : 0, "end_offset" : 1, "type" : "CN_CHAR", "position" : 0 }, { "token" : "是", "start_offset" : 1, "end_offset" : 2, "type" : "CN_CHAR", "position" : 1 }, { "token" : "中国人", "start_offset" : 2, "end_offset" : 5, "type" : "CN_WORD", "position" : 2 }, { "token" : "中国", "start_offset" : 2, "end_offset" : 4, "type" : "CN_WORD", "position" : 3 }, { "token" : "国人", "start_offset" : 3, "end_offset" : 5, "type" : "CN_WORD", "position" : 4 } ] }
自定义词库
IK分词器的分词对于新出的词语不能进行分词,这时可以使用自定义词库,让IK来进行分词
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 POST _analyze { "analyzer": "ik_smart", "text": "奥里给" } # 结果,对于新出的网络用词不能完成进行分词,需要自定义分词 { "tokens" : [ { "token" : "奥", "start_offset" : 0, "end_offset" : 1, "type" : "CN_CHAR", "position" : 0 }, { "token" : "里", "start_offset" : 1, "end_offset" : 2, "type" : "CN_CHAR", "position" : 1 }, { "token" : "给", "start_offset" : 2, "end_offset" : 3, "type" : "CN_CHAR", "position" : 2 } ] }
修改ik分词器挂载目录下面的conf下面的IKAnalyzer.cfg.xml
文件,并且搭建nginx用于设置远程词库地址,词库为txt
文件,每一行一个词
词库内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 POST _analyze { "analyzer": "ik_smart", "text": "奥里给" } # 结果,使用了自定义分词 { "tokens" : [ { "token" : "奥里给", "start_offset" : 0, "end_offset" : 3, "type" : "CN_WORD", "position" : 0 } ] }
ES和Kibana设置用户名和密码 ElasticSearch配置文件 1 2 3 4 http.host: 0.0 .0 .0 xpack.security.enabled: true xpack.license.self_generated.type: basic xpack.security.transport.ssl.enabled: true
ES密码设置 1 2 3 4 5 6 7 8 9 10 11 # 进入容器 docker exec -it elasticsearch_742 /bin/bash # 进入ES bin目录下,执行 ./elasticsearch-setup-passwords interactive # 用户名为[]中的值,例如 Enter password for [elastic]的用户名就是elastic # 输入完密码,退出容器,重启容器 exit docker restart elasticsearch_742
Kibana配置文件 1 2 3 4 5 6 7 8 9 10 server.name: kibana server.host: "0" elasticsearch.hosts: [ "http://elasticsearch:9200" ]xpack.monitoring.ui.container.elasticsearch.enabled: true i18n.locale: "zh-CN" elasticsearch.username: "elastic" elasticsearch.password: "ES设置的密码" xpack.security.encryptionKey: "something_at_least_32_characters"
Elasticsearch-Rest-Client
使用ES官方提供的API,在Java中操作ES
官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high.html
1 2 3 4 5 6 7 8 <properties > <elasticsearch.version > 7.4.2</elasticsearch.version > </properties > <dependency > <groupId > org.elasticsearch.client</groupId > <artifactId > elasticsearch-rest-high-level-client</artifactId > </dependency >
配置 1 2 3 4 5 6 es: host: 主机 port: 端口 username: 用户名 password: 密码 scheme: http / https
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ElasticSearchConfig { @Value("${es.host}") private String host; @Value("${es.port}") private Integer port; @Value("${es.username}") private String username; @Value("${es.password}") private String password; /** * http / https */ @Value("${es.scheme}") private String scheme; public static final RequestOptions COMMON_OPTIONS; static { RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); COMMON_OPTIONS = builder.build(); } @Bean public RestHighLevelClient esRestClient() { //设置ES用户名和密码 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password)); return new RestHighLevelClient( RestClient.builder(new HttpHost(host, port, scheme)) .setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.disableAuthCaching(); return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); }) ); } }