GET /customer/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
},0
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"avg_pri": {
"avg": {
"field": "balance"
}
}
}
}
}
},
"avg_balance":{
"avg": {
"field": "balance"
}
}
}
}
GET /customer/_search?size=0
{
"aggs": {
"avg_grade": {
"avg": {
"field": "balance"
}
}
}
}
GET /customer/_search?size=0
{
"aggs": {
"avg_grade": {
"avg": {
"script": {
"source": "doc.balance.value"
}
}
}
}
}
POST /customer/_search?size=0
{
"aggs" : {
"avg_corrected_grade" : {
"avg" : {
"field" : "balance",
"script" : {
"lang": "painless",
"source": "_value * params.correction",
"params" : {
"correction" : 1
}
}
}
}
}
}
自定义分词器
# 指定analyer进行分词校验
POST _analyze
{
"analyzer": "ik_smart",
"text": "这里是江西的师范大学"
}
# 指定字段进行校验,得出检索不出预期结果的原因
POST /test_index/_analyze
{
"field": "name",
"text": "Hello world"
}
# 指定char_filter和tokenizer
POST _analyze
{
"tokenizer": "keyword",
"char_filter": [
"html_strip"
],
"text": "<p>Hello World</p>"
}
# 在创建index时指定analyer
PUT test_index_1
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyer": {
"type": "custom",
"char_filter": [
"html_strip"
],
"tokenizer": "standard",
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
}
}
# 自定义分词器
PUT test_index_2
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyer": {
"type": "custom",
"char_filter": [
"emoticons"
],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"tokenizer": {
"punctuation": {
"type": "pattern",
"pattern": "[.,!?]"
}
},
"char_filter": {
"emoticons": {
"type": "mapping",
"mappings": [
":> => happy",
":< => sad",
"cat => yjn"
]
}
},
"filter": {
"english_stop": {
"type": "stop",
"stopwords": "_english_"
}
}
}
}
}
POST test_index_2/_analyze
{
"analyzer":"my_custom_analyer",
"text": "cat.cat.cat"
}
PUT test_analyze
{
"settings": {
"analysis": {
"analyzer": {
"pinyin_test": {
"char_filter": [
"html_strip",
"mymap",
"mypattern"
],
"tokenizer": "test_tokenizer"
}
},
"tokenizer": {
"test_tokenizer": {
"type": "keyword"
}
},
"char_filter": {
"mymap": {
"type": "mapping",
"mappings": [
"* => 我",
"# => 爱",
"^ => 你"
]
},
"mypattern": {
"type": "pattern_replace",
"pattern": "\\s+",
"replacement": ""
}
}
}
}
}
GET test_analyze/_analyze
{
"text": "<p>fkj<br>s<ajf>*ld#fk^^d</p>",
"analyzer": "pinyin_test"
}
自定义Mapping
PUT test_index_3
{
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "keyword"
},
"sex" : {
"type" : "boolean"
}
}
}
}
GET /test_index_3/_mapping
自定义mapping和dynamic对新增字段的意义
dynamic值 | 结果 |
---|---|
false | 可以正常插入新字段数据,但是不会创建新字段 |
true | 可以正常插入新字段数据,会创建新字段 |
"strict" | 插入新的字段会报错 |
PUT my_index
{
"mappings" : {
"dynamic": true,
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "keyword"
},
"sex" : {
"type" : "boolean"
}
}
}
}
GET my_index/_mapping
PUT my_index/_doc/1
{
"name": "zjb",
"title": "yjn"
}
GET my_index/_search
DELETE my_index
自定义mapping之index,字段能否被检索
PUT my_index
{
"mappings": {
"properties": {
"cookie": {
"type": "text",
"index": false
}
}
}
}
PUT my_index/_doc/1
{
"cookie": "name=zjb"
}
GET my_index/_search
{
"query": {
"match": {
"cookie": "TEXT"
}
}
}
自定义mapping之copy_to
DELETE my_index
PUT my_index
{
"mappings": {
"properties": {
"first_name":{
"type": "text",
"copy_to": "full_name"
},
"last_name":{
"type":"text",
"copy_to": "full_name"
},
"full_name":{
"type": "text"
}
}
}
}
POST my_index/_doc/1
{
"first_name":"zhan",
"last_name":"jinbing"
}
GET my_index/_search
{
"query": {
"match":{
"full_name": {
"query":"zhan jinbing",
"operator": "or"
}
}
}
}
自定义Mapping--index_options
- index_options 用于控制倒排索引记录的内容,有如下4中配置
- docs只记录 doc id
- freqs 记录doc id 和 term frequencies
- positions 记录 doc id、term frequencies和term position
- offsets 记录 doc id、term frequencies、term position 和character offsets
- text类型默认配置 positions,其他默认为 docs
- 记录内容越多,占用空间越大
自定义Mapping--null_value
- 当字段遇到null值的处理策略,默认为null,即空值,此时es会忽略该值。可以通过设定该值设定字段的默认值
PUT my_index
{
"mappings": {
"properties": {
"first_name":{
"type": "text",
"null_value": "这是默认值"
}
}
}
}
自定义mapping--数据类型
多字段特性 multi-fields
- 允许对同一个字段采用不同的配置,比如分词,常见例子如对人名实现拼音搜索,只需要在人名中新增一个子字段为pinyin即可
PUT my_index
{
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"pinyin": {
"type": "text",
"analyzer": "pinyin"
}
}
}
}
}
}
# 检索方式
GET test_index/_search
{
"query": {
"match": {
"username.pinyin": "hanhan"
}
}
}
# 配置多个附属字段方便查询
PUT test_index
{
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"py_name": {
"type": "text",
"analyzer": "pinyin"
},
"ik_name": {
"type": "text",
"analyzer": "ik_smart"
},
"smartcn_name": {
"type": "text",
"analyzer": "smartcn"
}
},
"analyzer": "keyword"
}
}
}
}
这里要注意分词器
analyzer
是要具有将文字转拼音
的功能的
自定义mapping之通过numeric_detection开启字符串数值探测和dynamic_date_formats设置日期格式
Query
URI Search
- 通过url query参数来实现搜索,常用参数如下:
- q指定查询的语句,语法为Query String Syntax
- df q 中不指定字段时默认查询的字段,如果不指定,es会查询所有字段
- sort 排序
- timeout 指定超时时间,默认不超时
- from,size用于分页
GET /my_index/_search?q=alfred&df=user&sort=age:asc&from=4&size=10&timeout=1s
查询user字段包含alfred的文档,结果按照age升序排序,返回第5个-到第14个文档,如果超过1s没有结束,则超时结束
布尔操作符
- AND(&&) ,OR(||) ,NOT(!)
- name:(tom NOT lee)
查询name中有tom但是没有lee
- 注意大写,不能小写
- name:(tom NOT lee)
-
+ -
分别对应 must 和 must_not- name: (tom +lee -alfred )
- name: ((lee && !alfred) || (tom && lee && !alfred))
-
+
在 url中会被解析为空格,要使用encode后的结果才可以,为%2B
范围查询,支持数值和日期
- 区间写法,闭区间用[],开区间用{}
- age: [1 TO 10]意为1<=age<=10
- age: [1 TO 10]意为1<=age<=10
- age:[1 TO ]意为age>=1
- age:[* TO 10]意为age<=10
- 算数符号写法
- age: >= 1
- age:(>=1 && <=10)或者 age:(+>=1 +<=10)
- 通配符查询
- ?代表1个字符,* 代表0或多个字符
- name: t?m
- name: tom*
- name:t*m
- ?代表1个字符,* 代表0或多个字符
- 通配符匹配执行效率低,且占用较多内存,不建议使用
- 如无特殊需求,不要将
?/*
放在最前面,这样很吃内存 - 正则表达式匹配
- name:/[mb]oat/ ===> +name:(moat boat)
- 模糊匹配 fuzzy query
- name: roam ~ 1
- 匹配与roam 差一个 character 的词,比如foam roams等
- 近似度查询 proximity search
- "fox quick" ~ 5
- 以term为单位进行差异比较,比如"quick fox" "quick brown fox"都会被匹配
Query DSL - 字段类查询
- 全文匹配
- 针对text类型的字段进行全文检索,会对查询语句先进行分词处理,如match、match_phrase等query类型
- 单词匹配
-
不会对查询的语句做分词处理,直接去匹配字段的倒排索引,如term、terms,range等query类型
-
fuzziness 模糊一个character,(可以错一个,多一个,少一个)
GET test_index/_search
{
"_source": "name",
"profile": "true",
"query": {
"match": {
"name.py_name":{
"query": "占金兵1",
"analyzer": "keyword",
"fuzziness": 1
}
}
}
}
minimum_should_match至少包含几个
GET test/_search
{
"query": {
"match": {
"name": {
"query": "zhan jin bing1",
"minimum_should_match": 2
}
}
}
}
相关性算分
- 相关性算分的几个重要概念如下:
- Term Frequency(TF) 词频,即单词在该文档中出现的次数。词频越高,相关度越高
- Document Frequency(DF)文档频率,即单词出现的文档数
- Inverse Document Frequency(IDF) 逆向文档频率,与文档频率相反,简单理解为1/DF,即单词出现的文档数越少,相关度越高
- Field-length Norm 文档越短,相关性越高
document frequency
说明:查询的词中有一个词只在很少的文档中出现了,用户想得到的结果很可能在这些少数的文档中,所以加大权重,相关性得分高
相关性算分 - TF/IDF 模型
- 可以通过explain参数来查看具体的计算方法,但是要注意:
- es的算分是按照shard进行的,即shard的分数计算是相互独立的,所以在使用explain的时候注意分片个数
- 可以通过设置索引的分片数为1来避免这个问题
GET test/_search
{
"explain": true,
"query": {
"match": {
"name": "zhan jin"
}
}
}
match_phrase
通过测试可以发现
match_phrase
也会对查询的语句进行分词处理,在与倒排序单词匹配的时候有顺序要求
match_phrase slop
因为match_phrase查询有顺序要求,缺了一个都不行,slop就是一个定义match_phrase可以缺几个的字段。可以理解slop默认为0
GET test_index/_search
{
"query": {
"match_phrase": {
"name": {
"query": "金兵",
"analyzer": "keyword",
"slop": 1
}
}
}
}
regexp 正则查询
正则查询的语句是不会分词的,如果查询的字段是分词的,那查询就会有问题,可以设置一个子字段不分词
GET test_index/_search
{
"profile": "true",
"query": {
"regexp": {
"name.py_name": "[忆占](江南|金兵)"
}
}
}
query_string
query_string就是URI方式的q,df就是default_field,也可以指定fields字段数组
Simple Query String Query
- 类似Query String,但是会忽略错误的查询语法,并且仅支持部分查询语法
- 其常用的逻辑符号如下,不能使用AND、OR、NOT等关键词:
-
+
代指 AND -
|
代指OR -
-
代指NOT
-
注意
simple_query_string
容错性很好
Term和Terms
match会对查询的语句进行分词处理,但是有时候我们并不想查询的词被分词处理,这时候就可以使用term和terms了
Range Query - Date Math
range查询用于
数值范围
和日期范围
的查询
- 针对日期提供的一种更友好地计算方式,格式如下:
now - 1d
:这里地now
是基准日期,也可以使用具体地日期来当作基准,比如2018-01-01
,使用具体日期地时候要用||
做隔离
计算公式,主要有如下3种,
+1h
加1个小时
-1d
减1天
/d
将时间舍入到天
可以使用的单位:- y - year - 年
- M - month - 月
- w - week - 周
- d - day - 天
- h - hour - 时
- m - minute - 分
- s - second - 秒
这里举个例子:防止以后懵逼 = - =
假设now为 2018-01-02 12:00:00
,那么如下的计算结果实际为:
计算公式 | 实际结果 |
---|---|
now+1h | 2018-01-02 13:00:00 |
now-1h | 2018-01-02 11:00:00 |
now-1h/d | 2018-01-02 00:00:00 |
2016-01-01|| +1M/d |
2016-02-01 00:00:00 |
上面的公式不能有空格
GET test/_search
{
"profile": true,
"query": {
"range": {
"age": {
"gte": 25,
"lte": 29
}
}
}
}
GET test/_search
{
"query": {
"range": {
"birth": {
"gte": "01/1990/10||+4y"
}
}
}
}
Query DSL - 复合查询
- 复合查询是指包含字段类查询或复合查询的类型,主要包括以下几类:
- constant_score query
- bool query
- dis_max query
- function_score query
- boosting query
Bool Query
- 布尔查询由一个或多个布尔子句组成,主要包含如下4个:
子句key | 说明 |
---|---|
filter | 只过滤符合条件的文档,不计算相关性得分 |
must | 文档必须符合must中的所有条件,会影响相关性得分 |
must_not | 文档必须不符合must_not中的所有条件 |
should | 文档可以符合should中的条件,会影响相关性得分 |
GET test/_search
{
"query": {
"bool": {
"filter": {
"term": {
"name": "zhan"
}
}
}
}
}
GET test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "zjb"
}
},
{
"match": {
"age": 22
}
}
]
}
}
}
GET test/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "zjb"
}
},
{
"match": {
"age": 22
}
}
]
}
}
}
GET test/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "zjb"
}
},
{
"match": {
"age": 22
}
}
]
}
}
}
注意:当只有
should
子句没有must
子句时,should
子句是必须至少要匹配一个条件的document
才会返回,当有must
子句时,只要满足了must
的条件就会返回,should
子句中的条件可以不必满足,满不满足只是对相关性得分有影响
Query Context VS Filter Context
- 当一个查询语句于Query或者Filter上下文时,es执行的结果会不同,对比如下:
上下文类型 | 执行类型 | 使用方式 |
---|---|---|
Query | 查找与查询语句最匹配的文档,对所有文档进行相关性算分并排序 | * query * bool中的must和should |
Filter | 查询与查询语句相匹配的文档 | * bool 中的 filter 与 must_not * constant_score 中的filter |
查询匹配的数量count
GET test/_count
{
"query": {
"match_all": {
}
}
}
指定返回的source
GET test/_search
{
"_source": false,
"query": {
"match_all": {}
}
}
GET test/_search
{
"_source": "name",
"query": {
"match_all": {}
}
}
GET test/_search
{
"_source": ["name","age"],
"query": {
"match_all": {}
}
}
GET test/_search
{
"_source": {
"includes": "n*"
},
"query": {
"match_all": {}
}
}
GET test/_search
{
"_source": {
"includes": "n*",
"excludes": "name"
},
"query": {
"match_all": {}
}
}
分布式特性
- es支持集群模式,是一个分布式系统,其好处主要有两个:
- 增大系统容量,如内存、磁盘、使得es集群可以支持PB级的数据
- 提高系统可用性,即使部分节点停止服务,整个集群依然可以正常服务
- es集群由多个es实例组成
- 不同集群通过集群名字来区分,可通过cluster.name进行修改,默认为elasticsearch
- 每个es实例本质上是一个JVM进程,且有自己的名字,通过node.name进行修改
安装
cerebro
进行可视化集群状态监控
启动一个节点
- 运行如下命令可以启动一个es节点实例
bin/elasticsearch -E cluster.name=集群名 -E node.name=节点名
不同的node需要不同的数据目录和端口,如果在同一台机器上部署多个实例,启动节点时可以使用
-Epath.data=数据目录 -Ehttp.port=端口
PUT /stconvert/
{
"settings": {
"analysis": {
"analyzer": {
"tsconvert": {
"tokenizer": "tsconvert"
}
},
"char_filter": {
"tsconvert": {
"type": "stconvert",
"convert_type": "t2s"
}
},
"tokenizer": {
"tsconvert": {
"type": "stconvert",
"delimiter": "#",
"keep_both": false,
"convert_type": "t2s"
}
},
"filter": {
"tsconvert": {
"type": "stconvert",
"delimiter": "#",
"keep_both": false,
"convert_type": "t2s"
}
}
}
}
}
GET stconvert/_analyze
{
"tokenizer": "keyword",
"filter": [
"lowercase"
],
"char_filter": [
"tsconvert"
],
"text": "擴大寄生蜂離開的"
}
Fielddata vs DocValues
对比 | FieldData | DocValues |
---|---|---|
创建时机 | 搜索时即时创建 | 索引 时创建,与倒排索引创建时机一致 |
创建位置 | JVM Heap | 磁盘 |
优点 | 不会占用额外的磁盘空间 | 不会占用Heap内存 |
缺点 | 文档过多时,即时创建会花过多时间,占用过多的Heap内存 | 减慢索引的速度,占用额外的磁盘资源 |
GET _analyze
{
"tokenizer": "whitespace",
"filter": [
{
"type": "stop",
"ignore_case": true,
"stopwords": [
"这",
"个",
"一",
"是"
]
},
{
"type": "synonym",
"lenient": true,
"expand": true,
"synonyms": [
"占金兵 => 易江南"
]
}
],
"text": "这 是 一 个 占金兵"
}