ElasticSearch学习笔记

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&nbsp;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没有结束,则超时结束

查看这个查询es执行的过程

布尔操作符

  • AND(&&) ,OR(||) ,NOT(!)
    • name:(tom NOT lee) 查询name中有tom但是没有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
  • 通配符匹配执行效率低,且占用较多内存,不建议使用
  • 如无特殊需求,不要将?/*放在最前面,这样很吃内存
  • 正则表达式匹配
    • 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类型


      term和match区别

      operator控制match匹配的方式
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
match_phrase slop

因为match_phrase查询有顺序要求,缺了一个都不行,slop就是一个定义match_phrase可以缺几个的字段。可以理解slop默认为0

slop
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字段数组

query_string

Simple Query String Query

  • 类似Query String,但是会忽略错误的查询语法,并且仅支持部分查询语法
  • 其常用的逻辑符号如下,不能使用AND、OR、NOT等关键词:
    • + 代指 AND
    • | 代指OR
    • - 代指NOT
Simple Query String Query

注意simple_query_string容错性很好

Term和Terms

match会对查询的语句进行分词处理,但是有时候我们并不想查询的词被分词处理,这时候就可以使用term和terms了

image.png

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中的条件,会影响相关性得分
bool格式
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": {}
  }
}
_source用法

分布式特性

  • 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内存 减慢索引的速度,占用额外的磁盘资源
image.png

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

推荐阅读更多精彩内容