ElasticSearch(基础)

1.1 倒排索引 倒排索引原理??

ElasticSearch使用一种称为 ==倒排索引== 的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表够成,对于其中每个词,有一个包含它的文档列表

1.1.2 分词器介绍及内置分词器

分词器:从一串文本中切分出一个一个的词条,并对每个词条进行标准化
包括三部分:

  • character filter : 分词之前的预处理,过滤掉HTML标签,特殊符号转换等
  • tokenizer : 分词
  • token filter : 标准化

内置分词器:

  • standard分词器: (默认)将词汇单词转为小写形式,并去除停用词和标点符号,支持中文采用的方法为单字切分
  • simple分词器: 首先会通过非字母字符来分割文本信息,然后将词汇单元统一为小写形式。该分析器会去掉数字类型的字符。
  • Whitespace分词器: 仅仅是去除空格,对字符没有lowcase化,不支持中文;并且不对生成的词汇单元进行其他的标准化处理。
  • language分词器: 特定语言的分词器,不支持中文

1.1.3 安装中文(IK)分词器
#使用ik_smart进行分词
GET _analyze?pretty
{
  "analyzer": "ik_smart",
  "text": "湖南省邵阳市双清区"
}

#使用ik_max_word进行分词
GET _analyze?pretty
{
  "analyzer": "ik_max_word",
  "text": "湖南省邵阳市双清区"
}

#新词
GET _analyze?pretty
{
  "analyzer": "ik_smart",
  "text": "斗罗大陆"
}
  • 查看已有词典 打开目录cd /usr/local/software/elasticsearch-6.2.2/config/analysis-ik/
  • !!自定义词典 在已有词典目录下使用命令 mkdir custom 新建自定义词典目录;
    使用命令 vi custom/new_word.dic 新建编辑自定义词典
  • 更新配置(将自定义词典配置进去) 使用命令 vim IKAnalyzer.cfg.xml 编辑配置文件
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">custom/new_word.dic</entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
  • 重启es

1.1.4 使用ElasticSearch API 实现CRUD
  • 添加索引
# 添加索引
PUT lib
{
  "settings": {
    "index": {
      # 分片 每个索引包含多个分片 分布在不同的node上
      "number_of_shards": 3,
      # 备份
      "number_of_replicas": 0
    }
  }
}
  • 查看索引信息
# 查看索引信息
GET lib/_settings
# 查看所有所以的配置
GET _all/_settings
  • 添加文档(数据) ==指定Id时使用PUT 不指定时使用POST==
# 添加文档(PUT POST)
PUT lib/user/1
{
  "username": "伍磊",
  "age": 23,
  "sex":"男"
}

# 结果
{
  "_index": "lib",
  "_type": "user",
  "_id": "1",
  "_version": 3,
  "result": "updated",
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 2,
  "_primary_term": 1
}
  • 查看
# 查看某一文档记录
GET lib/user/1
# 查看具体字段
GET lib/user/1?_source=username,age
  • 更新文档
# 覆盖(PUT)全局替换,相当于先删除再增加
PUT lib/user/1
{
  "username": "伍磊",
  "age": 24,
  "sex":"男"
}
# 直接修改(POST)更新某几个字段 修改哪个字段填写哪个字段
POST lib/user/1/_update
{
  "doc": {
    "age": 23
  }
}
  • 删除
# 删除文档
DELETE lib/user/1
# 删除索引
DELETE lib

1.1.5 批量获取文档

使用es提供的 Multi Get API:
可以通过索引名、类型名、文档ID一次得到一个文档集合,文档可以来自于同一个索引库,也可以来自不同索引库

# 批量获取 通过此命令获取多个文档信息
GET /_mget
{
  "docs": [
    {
      "_index": "lib",
      "_type": "user",
      "_id": 1,
      "_source": "username"
    },
    {
      "_index": "lib",
      "_type": "user",
      "_id": 2
    }
  ]
}

# 获取同索引同类型下不同的文档(指定获取的id集合)
GET lib/user/_mget
{
  "ids": [1,2]
}

1.1.6 使用Bulk API 实现批量操作
bulk的格式:

{action:{metadata}}\n
{requestbody}\n

action:(行为)
  • create: 文档不存在时创建
  • update: 更新文档
  • index : 创建新文档或替换已有文档
  • delete:删除一个文档
metadata: _index,_type,_id
create和index的区别
  • 如果数据存在,使用create操作失败,会提示文档已经存在,使用index则可以成功执行。
示例:
# 批量添加 index:文档存在则替换否则添加
POST lib2/books/_bulk
{"index": {"_id":1}}
{"title": "java", "price": 55}
{"index": {"_id":2}}
{"title": "c++", "price": 48}
{"index": {"_id":3}}
{"title": "php", "price": 36}
{"index": {"_id":4}}
{"title": "Python", "price": 50}

# 查看刚刚批量添加的数据
GET lib2/books/_mget
{
  "ids": [1,2,3,4]
}

# 同时执行增删改 删除时不需要请求体
POST lib2/books/_bulk
{"delete": {"_index": "lib2", "_type": "books", "_id": 4}}
{"create": {"_index": "temp", "_type": "tt", "_id": 100}}
{"name": "测试-create"}
{"index": {"_index": "temp", "_type": "tt"}}
{"name": "测试-index"}
{"update": {"_index": "lib2", "_type": "books", "_id": 3}}
{"doc": {"price": 58}}
bulk一次最大处理多少数据量

bulk会把将要处理的数据载入内存中,所以数据量是有限制的,最佳的数据量不是一个确定的数值,它取决于你的硬件,你的文档大小以及复杂性,你的索引以及搜索的负载。一般建议是1000-5000个文档,大小建议是5-15MB,默认不能超过100M,可以在es的配置文件中进行配置


1.1.7 版本控制

ElasticSearch采用==乐观锁==来保证数据的一致性,也就是说,当用户对document进行操作时,并不需要对该document作加锁和解锁的操作,只需要==指定操作的版本==即可。当版本号一致时,ElasticSearch会允许该操作顺利执行,而当版本号存在冲突时,ElasticSearch会提示冲突并抛出异常(version_conflict_engine_exception

# 修改时指定版本与es中版本不一致那么就会出现异常
PUT lib2/books/2?version=1
{
  "doc":{
    "price": 49
  }
}

ElasticSearch版本号取值为1~2^63-1

内部版本控制:使用的是_version

外部版本控制:ElasticSearch处理外部版本号时会对内部版本号的处理有些不同。不再是检查_version是否与请求中指定的数值相同,而是检查当前的_version是否比指定的数小。如果请求成功,那么外部的版本号就会被存储到文档中的_version中,为了保持_version与外部版本控制的数据一致,使用version_type=external

# 使用外部版本控制,在指定的版本号后加上version_type=external,若此指定# 的版本号>es中的版本号,那么添加成功,否则异常
PUT lib2/books/2?version=99&version_type=external
{
  "doc":{
    "price": 49
  }
}

1.1.8 Mapping

查看es自动创建的mapping

# 查看lib2索引下books类型的mapping
GET lib2/books/_mapping

# 结果 mapping(dynamic mapping) ——> 映射
{
  "lib2": {
    "mappings": {
      "books": {
        "properties": {
          "doc": {
            "properties": {
              "price": {
                "type": "long"
              }
            }
          },
          "price": {
            "type": "long"
          },
          "title": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

mapping定义了type中的每个字段的数据类型以及这些字段如何分词等相关属性
Elasticsearch 映射参数 fields
创建索引的时候,可以预先定义字段的类型以及相关属性,这样就能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理字符串等支持的数据类型

核心数据类型(Core datatypes)
字符型:String 
String类型包括 text 和 keyword
text类型被用来索引长文本,在建立索引前会将这些文本进行分词,转化为词的组合,建立索引。允许es来检索这些词语。text类型不能用来排序和聚合。
Keyword类型不需要进行分词,可以被用来检索过滤,排序和聚合。Keyword类型字段只能用本身来进行检索。

数字型:long,integer,short,byte,double,float
日期型:date
布尔型:boolean
二进制型:binary
复杂数据类型(Complex datatypes)
数组类型(Array datatypes): 数组类型不需要专门指定数组元素的type。例如:
    字符型数组: ["one", "two"]
    整形数组: [1, 2]
    数组型数组: [1, [2, 3]] 等价于 [1, 2, 3]
    对象数组: [{"name": "leiwu", "age": 23}, {"name": "wulei", "age": 23}]
对象类型(Object datatype): _object_ 用于单个json对象
嵌套类型(Nested datatype): _nested_ 用于json数组
地理位置类型(Geo datatypes)
地理坐标类型(Geo-point datatypes): _geo_point_ 用于经纬度坐标
地理形状类型(Geo-shape datatypes): _geo_shape_ 用于类似余多边形的复杂形状
特定类型(Specialised datatypes)
IPv4类型(IPv4 datatype): _ip_ 用于IPv4地址
Completion类型(Completion datatype): _completion_提供补全建议
Token count类型(Token count datatype): _token_count_ 用于统计做了标记的字段的index数目,该值会一直增加,不会因为过滤条件而减少
手动创建mapping
# 创建索引 并为类型books设置mapping 可以设置字段的类型,分词器的选择,以及此字段查询时分词器的选择
PUT lib4
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 0
  },
  
  "mappings": {
    "books": {
      "properties": {
        "name": {"type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart"},
        "price": {"type": "double"}
      }
    }
  }
}

# 向索引中添加一条数据
POST lib4/books/2
{
  "name": "伍磊的java",
  "price": 19
}

# 指定分词器进行查询 (一般使用ik_max_word进行分词 使用ik_smart进行分词查询 这样能最大限度查询自己想要的结果)
GET lib4/books/_search
{
  "query": {
    "match": {"name": {"query": "伍磊", "analyzer": "ik_smart"}}
  }
}

2.1 查询相关

2.1.1 基本查询
term查询和terms查询

term query 会去倒排索引中寻找准确的term,它并不知道分词器的存在,这种查询适合keyword、numeric、date (倒排索引中有则能够查到)

term: 查询某个字段里面含有某个关键词的文档

# term/terms 关键词查询-倒排索引中有的才能查询出来 terms查询只要符合其中一个关键词就能查询出来
GET lib4/books/_search
{
  "query": {
    "term": {"name": "伍磊的"}
  }
}

# 查询从第"from"个开始 一共查询"size"个 按照"sort"进行排序 "version"表示同时将版本号查询出来
GET lib4/books/_search
{
  "from": 0,
  "size": 2,
  "version": true, 
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ], 
  "query": {
    "term": {"name": "java"}
  }
}


match查询

match query 知道分词器的存在,会对filed进行分词操作,然后再查询

相关链接

analyzed字段无法使用term精确查询 这是因为analyzed字段会默认进行分词之后再建立倒排索引,而此时使用term查找这个短语,因为这个短语分词后才建立的倒排索引,对于这个短语来说是没有对应的倒排索引的,所以就查询不到。
match_phrase主要用来精确查询可以分词的字段 查询的短语与想要匹配到的字段要完全一致,且分词之后也要完全一致,这就必须在精确查询的同时指定分词器,此分词器与此索引mapping中指定的分词器要一致才能查询出来

基本查询代码
# 通过match查询和指定分词器 可以将需要查询的字段分词之后再进行查询
GET lib4/books/_search
{
  "query": {
    "match": {
      "name": {"query": "伍磊很厉就说是吧", "analyzer": "ik_smart"}
    }
  }
}

# match_all 查询所有文档
GET lib4/books/_search
{
  "query": {
    "match_all": {}
  }
}

# multi_match: 可以指定多个字段 "query"表示需要寻找的关键字 "fileds":表示从这些字段数组中匹配关键字 
GET lib4/books/_search
{
  "query": {
    "multi_match": {
      "query": "伍磊",
      "fields": ["name"]
    }
  }
}

# match_phrase:短语匹配查询
# ES引擎首先分析查询字符串,从分析后的文本中构建短语查询,这意味着必须匹配短语中的!!所有分词!!且保证各个分词的相对位置不变(查询时指定与此索引此字段mapping中同样的分词器才能实现精确查询)
GET lib4/books/_search
{
  "query": {
    "match_phrase": {
      "name": {"query": "伍磊的java", "analyzer": "ik_max_word"}
    }
  }
}

# "_source": "name" 表明返回的字段
GET lib4/books/_search
{
  "_source": "name", 
  "query": {
    "match_phrase": {
      "name": {"query": "伍磊的java", "analyzer": "ik_max_word"}
    }
  }
}

# "_source"下"includes" 表示返回需要包含哪些字段 "excludes"表示不包含哪些字段 上面两个均可用通配符表示
GET lib4/books/_search
{
  "_source": {
    "includes": ["nam*", "price"],
    "excludes": "pric*"
  },
  "query": {
    "match_phrase": {
      "name": {"query": "伍磊的java", "analyzer": "ik_max_word"}
    }
  }
}

# "match_phrase_prefix"前缀匹配查询
GET lib4/books/_search
{
  "_source": {
    "includes": ["nam*", "price"],
    "excludes": "pric*"
  },
  "query": {
    "match_phrase_prefix": {
      "name": {"query": "伍磊的jav", "analyzer": "ik_max_word"}
    }
  }
}

# 范围查询 表示查询价格从19到88的数据 其中include_lower(true)表示包含下界  include_upper(false)表示不包含上届
GET lib4/books/_search
{
  "query": {
    "range": {
      "price": {
        "from": 19,
        "to": 88,
        "include_lower": true,
        "include_upper": false
      }
    }
  }
}

# wildcard查询 允许使用通配符 "*" "?" 进行查询
# "*" 代表0或多个字符
# "?" 代表任意一个字符
# !!还是根据倒排索引进行查询 所以这个查询条件必须得是符合某个倒排索引才能查询出来
GET lib4/books/_search
{
  "query": {
    "wildcard": {
      "name": "伍*"
    }
  }
}

# fuzzy实现模糊查询

# 高亮显示字段 "highlight"
GET lib4/books/_search
{
  "query": {
    "wildcard": {
      "name": "伍?"
    }
  },
  "highlight": {"fields": {"name": {}}}
}


2.1.2 Filter查询

filter是不计算相关性的,同时可以cache。因此,filter速度要快于query

# 通过filetr查询价格为19的书
GET lib4/books/_search
{
  "query": {
    "bool": {
      "filter": [
          {"term": {"price": 19}}
        ]
    }
  }
}

# 通过filetr查询价格为19或88的书
GET lib4/books/_search
{
  "query": {
    "bool": {
      "filter": [
          {"terms": {"price": [19, 88]}}
        ]
    }
  }
}


bool过滤查询

可以实现组合过滤查询
格式 {"bool": {"must": [], "should": [], "must_not": []}}
must:必须满足的条件 ==> and
should: 可以满足也可以不满足的条件 ==> or
must_not: 不需要满足的条件 ==> not

# 通过bool过滤器匹配名字为java的书或价格为19的书
GET lib4/books/_search
{
  "query": {
    "bool": {
      "should": [
        {"match": {
          "name": "java"
        }},
        {"term": {
          "price": {
            "value": 19
          }
        }}
      ]
    }
  }
}

# 通过bool过滤器匹配名字为伍磊的书或名字包含java且价格为19的书(可以嵌套)
GET lib4/books/_search
{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "name": "伍磊"
                    }
                }, 
                {
                    "bool": {
                        "must": [
                            {
                                "term": {
                                    "name": {
                                        "value": "java"
                                    }
                                }
                            }, 
                            {
                                "term": {
                                    "price": {
                                        "value": "19"
                                    }
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}
范围过滤

gt : > lt : < gte : 大于等于 lte : 小于等于

# 通过filetr查询价格为19到88区间的书
GET lib4/books/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "price": {
            "gte": 19,
            "lte": 88
          }
        }
      }
    }
  }
}

# 通过filetr查询价格存在的书
GET lib4/books/_search
{
  "query": {
    "bool": {
      "filter": {
        "exists": {
          "field": "price"
        }
      }
    }
  }
}
聚合查询
# 聚合查询 size表示只要获取聚合查询的结果 "aggs"表示聚合查询  "price_of_sum"自定义名称  "sum"表示总和 "min"表示最小值 
# "avg"表示平均数 "cardinality"表示基数(字段上互不相同的数的个数) "terms"表示分组
GET lib4/books/_search 
{
  "size": 0, 
  "aggs": {
    "price_of_sum": {
      "sum": {
        "field": "price"
      }
    }
  }
}

GET lib4/books/_search
{
  "size": 0, 
  "aggs": {
    "price_of_min": {
      "min": {
        "field": "price"
      }
    }
  }
}

# 对名字为java的书按价格分组,并计算每组中价格的平均值,按平均价格倒序排序
GET lib4/books/_search
{
  "size": 0, 
  "query": {
    "match": {
      "name": "java"
    }
  },
  "aggs": {
    "price_of_group": {
      "terms": {
        "field": "price",
        "order": {
          "price_of_avg": "desc"
        }
      },
      "aggs": {
      "price_of_avg": {
        "avg": {
          "field": "price"
          }
        }
      }
    }
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容