目录
基本概念
ElasticSearch是面向文档的
ElasticSearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),
每个类型下又包含多个文档(行),每个文档包含多个字段
和关系型数据的对比 如下:
关系型数据库 | ElasticSearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | type 注:这个概念逐渐会被淘汰 |
行(rows) | documents |
字段(columns) | fileds |
分片
在ElasticSearch中,一个索引(库)被分为多个分片,分片用于存储数据,每个分片是一个Lucene的索引。所以一个ElasticSearch索引由多个Lucene索引组成。
倒排索引
https://www.zhihu.com/question/23202010
倒排索引创建索引的流程:
1) 首先把所有的原始数据进行编号,形成文档列表
2) 把文档数据进行分词,得到很多的词条,以词条为key。包含这些词条的文档的编号为value 。
搜索的过程:
1)当用户输入任意的词条时,首先对用户输入的数据进行分词,得到用户要搜索的所有词条。
2)然后拿着这些词条去倒排索引列表中进行匹配。找到这些词条就能找到包含这些词条的所有文档的编号。
3)最后根据这些编号去文档列表中找到文档
IK分词器
两种分词模式:
- ik_smart:将文本做最粗粒度的拆分。如会将“中华人民共和国国歌”切分为“中华人民共和国”、“国歌”
- ik_max_word:将文本做最细粒度的拆分。如会将“中华人民共和国国歌”切分为“中华人民共和国”、“中华人民”、“中华”、“华人”、“人民共和国”、“人民”、“共和国”、“共和”、“国”、“国歌”
1. 如果不自定义分词,则无法切分出我们想要的词
2. 通过自定义分词,可以切分出我们想要的词
# 1. 进入es容器
[root@izuf6crq4k1mozjbsp7p09z config]# docker exec -it f2a7 /bin/bash
# 2. config/analysis-ik目录下有IKAnalyzer.cfg.xml配置文件和dic文件,dic文件中记录的是一个个默认的词
[root@f2a7e75092d4 elasticsearch]# cd config/analysis-ik/
[root@f2a7e75092d4 analysis-ik]# ls
IKAnalyzer.cfg.xml extra_single_word.dic extra_single_word_low_freq.dic preposition.dic stopword.dic surname.dic
extra_main.dic extra_single_word_full.dic extra_stopword.dic main.dic quantifier.dic suffix.dic
# 3. 新建自己的dic文件,里面写自定义的词
[root@f2a7e75092d4 analysis-ik]# touch jarvis.dic
[root@f2a7e75092d4 analysis-ik]# echo 姚一斌>>jarvis.dic # 将姚一斌作为一个可搜索的词
# 4. 在IKAnalyzer.cfg.xml中配置自定义的dic文件
[root@f2a7e75092d4 analysis-ik]# cat 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">jarvis.dic</entry> # 在这里加上自定义的dic文件
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
# 5. 退出es容器,重启es容器和kibana容器
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 | 查询所有数据 |
1. _cat
GET /_cat/nodes :查看所有节点
GET /_cat/health :查看es健康状况
GET /_cat/master : 查看主节点
GET /_cat/indices : 查看所有索引
GET /_cat/indices?v :查看所有索引(带表头)
2. PUT/POST新建文档
# 创建指定id的文档
# 注: 1. 如果索引不存在,则也会创建索引
# 2. 如果创建的索引没有显式地指定字段类型,则会根据添加的值确定字段默认的类型
PUT /jarvis_indice/jarvis_type/1 # type默认为_doc,且type逐渐被弃用 会有提示:use the /{index}/_doc/{id}
{
"name":"jarvis",
"age":24
}
PUT /jarvis_indice/_doc/2 # 将type改为_doc,则此时不会有提示
{
"name":"jarvis",
"age":24,
"birthday":"1996-05-01"
}
# 获取文档
GET /jarvis_indice/_doc/2
===output:
{
"_index" : "jarvis_indice", //该条数据所在的索引
"_type" : "_doc", //数据类型
"_id" : "2", //id
"_version" : 1, //数据的版本
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "jarvis",
"age" : 24,
"birthday" : "1996-05-01"
}
}
==============================================
# POST创建不指定id的文档
# 注:当POST方式且不指定id,多次重复创建相同数据的文档,id也是不同的,即每次POST,都是创建不同的文档
POST /jarvis_indice/_doc
{
"name":"robin",
"age":23,
"birthday":"1996-06-17"
}
===output:
{
"_index" : "jarvis_indice",
"_type" : "_doc",
"_id" : "HR_JS3cB9kPBMfNjArcI", //自动生成唯一id
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1
}
=========================================
# POST创建指定id的文档 效果和PUT一样
3. PUT/POST修改文档
# 修改文档--方式1
# 注: 1. 相当于覆盖原有的文档。弊端在于,如果现有的文档少了某个字段如birthday,则修改后该字段就会没有
# 2. 第一次PUT,即新增一个文档;多次PUT,_version会变,即数据版本会改变
PUT /jarvis_indice/_doc/2
{
"name":"jarvisss",
"age":25
}
===output:
{
"_index" : "jarvis_indice",
"_type" : "_doc",
"_id" : "2",
"_version" : 2, //版本为2
"result" : "updated", //表示该数据被修改
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 7,
"_primary_term" : 1
}
GET /jarvis_indice/_doc/2
===output:
{
"_index" : "jarvis_indice",
"_type" : "_doc",
"_id" : "2",
"_version" : 2,
"_seq_no" : 2,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "jarvisss",
"age" : 25 //少了birthday字段
}
}
========================================================
# 修改文档--方式2
# 这种方式不是覆盖更新,而是更新写出来的字段数据
# 如果要更新的数据和原有数据一致,则不会进行更新,version和_seq_no不变,result为noop(无操作)
POST /jarvis_indice/_doc/2/_update
{
"doc":{
"name":"jarviaaa",
"age":26,
"birthday":"1996-07-01"
}
}
GET /jarvis_indice/_doc/2
===output:
{
"_index" : "jarvis_indice",
"_type" : "_doc",
"_id" : "2",
"_version" : 3,
"_seq_no" : 2,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "jarviaaa",
"age" : 26,
"birthday" : "1996-07-01"
}
}
数据不变,多次更新 POST /jarvis_indice/_doc/2/_update
{
"_index" : "jarvis_indice",
"_type" : "_doc",
"_id" : "2",
"_version" : 3, //版本不变
"result" : "noop", //noop为无操作
"_shards" : {
"total" : 0,
"successful" : 0,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
PUT和POST总结:
PUT:
1.必须带id
2.第一次PUT则新增一个文档
3.如果该id的文档已有,则覆盖更新(如果现有文档字段比原有文档少,则更新后该字段不存在)
POST:
1.如果不带id,每次都是新增一个文档,且自动生成唯一id
2.如果带id ,更新和修改都和PUT效果一样
3.indice/_doc/id/_update:不是覆盖更新,而是只更新写出来的字段数据;且如果数据和原有数据一致,则不作更新
4. DELETE 删除索引/文档
DELETE jarvis_indice/_doc/2 删除文档
DELETE jarvis_indice 删除索引
5. GET查询
GET /jarvis_indice/_doc/2
===output:
{
"_index" : "jarvis_indice", //在哪个索引
"_type" : "_doc", //在哪个类型
"_id" : "2", //文档id
"_version" : 3, //版本号
"_seq_no" : 2, //并发控制字段,每次更新就会+1,用来做乐观锁
"_primary_term" : 1, //并发控制字段
"found" : true,
"_source" : {
"name" : "jarviaaa",
"age" : 26,
"birthday" : "1996-07-01"
}
}
乐观锁操作,更新的时候在url后携带:?if_seq_no=X1&if_primary_term=X2
6. bulk批量API
POST /jarvis_indice/_doc/_bulk 批量增加三条数据,这三条命令都是独立的
{"index":{"_id":11}}
{"name":"jarvis01"}
{"index":{"_id":12}}
{"name":"jarvis02"}
{"index":{"_id":13}}
{"name":"jarvis03"}
批量导入数据:
https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json
7. QueryDSL
一种可以查询的Json风格的DSL(domain-specific language领域特定语言)
===常用格式:
GET /bank/_search # 格式:GET /索引名/_search {请求体}
{
"query": { //查询条件
"match_all": {}
},
"sort": [ //排序
{
"balance": {
"order": "desc"
}
}
],
"from": 5, //从第5条开始
"size": 5, //取5条
"_source": ["balance","address"] //只查这些字段
}
query中的查询方式:
1. match_all 表示查所有
2. match 表示根据条件查询:如果字段不是字符串类型,则精确匹配;如果是文本,则模糊匹配
3. match_phrase 将检索条件当成一个整体,不进行分词
4. keyword匹配 将检索条件当成一个整体,不进行分词,且为精确等值匹配
5. multi_match 多字段匹配
6. bool复合查询
7. term查询 可以使用term来查询条件字段为精确值的文档
8. aggregation聚合
===match匹配
{
"query": {
"match": {
"address": "671 Street" //会对检索条件进行分词,如这里,会被切分成671和Street,
//则address中带有671或Street的文档都可以被查出来
}
}
}
===match_phrase匹配
{
"query": {
"match_phrase": {
"address": "Bristol Street" //不会对检索条件进行分词,即要匹配出address中包含"Bristol Street"的文档,
//故此可以匹配出"671 Bristol Street"
}
}
}
===keyword匹配
{
"query": {
"match": {
"address.keyword": "Bristol Street" //不会对检索条件进行分词,且进行精确等值匹配,即要匹配出
//address等于"Bristol Street"的文档,故此无法匹配出"671 Bristol Street"
}
}
}
===multi_match匹配
{
"query": {
"multi_match": {
"query": "mill Wanamie", //会对检索条件进行分词
"fields": ["address","city"] //address或city两个字段中只要有一个包
//含"mill"或"Wanamie",则可被查出
}
}
}
===bool查询 must must_not should filter
{
"query": {
"bool": { //bool多条件组合查询
"must": [ //must表示必须满足的条件,数组中可写多条件
{"match": {
"gender": "F"
}},
{"match": {
"address": "Street"
}}
],
"must_not": [ //must_not表示必须不满足的条件,数组中可写多条件
{"match": {
"age": 23
}}
],
//应该满足的条件,不改变查询的结果集,
//但是可以提高满足这个条件的文档的相关性得分score(即可以改变结果集中文档的顺序)
"should": [
{"match": {
"city": "Jacksonburg"
}}
],
//filter可以对查询结果进行过滤。只过滤,不会改变相关性得分score的值
"filter": {
"range": {
"age": {
"gte": 20,
"lte": 50
}
}
}
}
}
}
===term查询
{
"query": {
"term": {
"age": {
"value": 32 //term不能用来检索字段为text文本的条件
}
}
}
}
最好:全文检索字段使用match,其他非text字段使用term
===aggregation聚合
1. 按照年龄聚合,并且计算每个年龄的人的平均薪资
GET /bank/_search
{
"query": {
"match_all": {}
},
"aggs": { //第一次聚合:聚合不同年龄的人
"ageAgg": {
"terms": {
"field": "age",
"size": 100
},
"aggs": { //第二次聚合:对第一次聚合的结果再次聚合,写在第一个aggs里。
"everyAgeBalanceAvg": { //对每个年龄的人群的平均工资进行计算
"avg": {
"field": "balance"
}
}
}
}
}
}
2. 查出所有年龄分布,并且这些年龄段中gender为M的平均薪资、gender为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"
}
}
}
},
"everyAgeBalanceAvg": { //聚合每个年龄总的平均薪资
"avg": {
"field": "balance"
}
}
}
}
}
}
8. Mapping
# 获取索引映射规则
GET 索引/_mapping
# 创建索引时指定字段映射规则
PUT /test_indice
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
},
"birthday":{
"type": "date"
},
"email":{
"type": "keyword" //类型设置为keyword,表示不分词且进行精确等值匹配
}
}
}
}
# 给已有索引添加新的字段映射
PUT test_indice/_mapping
{
"properties":{
"emplo_id":{
"type":"integer",
"index":false
//index默认都为true,表示都可以作为搜索条件;
//设为false,表示该字段不能作为搜索的条件
}
}
}
# 对于已存在的字段映射无法进行更新,只能重建一个索引,然后数据迁移
POST _reindex //迁移数据
{
"source": { //原索引
"index": "bank"
},
"dest": { //目标索引
"index": "new_bank"
}
}