ElasticSearch入门到入土手册请查阅

发布于:2022-12-26 ⋅ 阅读:(571) ⋅ 点赞:(0)

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

ElasticSearch来源

伦敦的公寓内,Shay Banon 正在忙着寻找工作,而他的妻子正在蓝带 (Le Cordon Bleu) 烹饪学校学习厨艺。在空闲时间,他开始编写搜索引擎来帮助妻子管理越来越丰富的菜谱。
他的首个迭代版本叫做 Compass。第二个迭代版本就是 Elasticsearch(基于 Apache Lucene 开发)。他将 Elasticsearch 作为开源产品发布给公众,并创建了 #elasticsearch IRC 通道,剩下来就是静待用户出现了。
公众反响十分强烈。用户自然而然地就喜欢上了这一软件。由于使用量急速攀升,此软件开始有了自己的社区,并引起了人们的高度关注,尤其引发了 Steven Schuurman、Uri Boness 和 Simon Willnauer 的浓厚兴趣。他们四人最终共同组建了一家搜索公司
在这里插入图片描述

流行程度

这里我们看看github的star数就可以看到喜欢的人到底有多少

在这里插入图片描述

用途

  • 日志处理和分析(ELK, elasticsearch+logstash+kibana)
  • 电商网站,检索商品

Elasticsearch特点

  1. 性能高,在全文索引方面的搜索非常快
  2. 分布式,可以分成多分片存储和搜索海量数据
  3. 存储数据是非结构化的,可以简单理解为存储的是json数据,可以随意添加字段
  4. 不支持事务
  5. 支持中文分词、相关读排名搜索
  6. 近实时,数据写入到被可以被读取到有小延迟,大概在一秒左右

核心概念

为了方便我们理解,我们与数据库中的一些概念做一些简单的比对

index

索引,相当于数据库中的一个数据库(Database)
我们可以简单看看elasticsearch中的所有索引

语法

查看索引
GET _cat/indices?v
创建索引
PUT /test_index
删除索引
DELETE /test_index

Type

可以理解为数据库中的表。在elasticsearch 7.0.0之后就不建议使用了,创建文档后面的会有一个type为_doc
8.0.0版本后的elasticsearch将完全废弃
include_type_name 参数可控制type相关api

为什么废弃
其实原因很简单

  1. 我们知道数据库中的表在数据库中是独立存储的,而同一个index下不同的type是存储在同一个索引下面的,所以不同type下相同名字的字段定义的mapping也必须一致
  2. 影响性能
    在这里插入图片描述

Document

索引(Index)中的每条记录就是Document

我们这里简单查看一下商品索引的里面的一些数据

GET test_index/_search

等价于

GET test_index/_doc/_search

在这里插入图片描述

  • _index: 文档所属索引
  • _type: 后续将废弃
  • _id: Doc的主键。在写入的时候,可以指定该Doc的ID值,如果不指定,则系统自动生成一个唯一的UUID值
  • _source: 相关度评分

Document的CRUD

插入数据
指定id
PUT /test_index/doc/1
{
  "post_date": "2022-09-03",
  "name": "小奏技术",
  "content": "这是测试小奏技术数据",
  "age": 88
}
使用自动生成id方式
POST /test_index/_doc
{
  "post_date": "2022-09-03",
  "name": "小奏技术1",
  "content": "这是测试小奏技术1数据",
  "age": 882
}
新增字段
PUT /test_index/doc/1
{
  "post_date": "2022-09-03",
  "name": "小奏技术",
  "content": "这是测试小奏技术数据",
  "age": 88,
  "label": "开心"
}

删除文档

DELETE test_index/_doc/oo0cAoABFI-iZ3BAcffx

这里的oo0cAoABFI-iZ3BAcffx是_id

Mapping

Mapping 可以简单理解为数据库中表中字段的类型,与数据库不同的是在es中我们可以不用手动指定Mapping,在插入数据的时候es可以为我们自动生成Mapping

我们来简单看看我们之前自动生成的Mapping

查看Mapping

查询语法

GET /test_index/_mapping

在这里插入图片描述

指定Mapping

我们也可以在创建索引的时候同时指定_mapping

PUT /test_index
{
  "mappings" : {
      "properties" : {
        "author_id" : {
          "type" : "long"
        },
        "content" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "post_date" : {
          "type" : "date"
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
}

mapping只能创建index手动指定mapping或者新增field mapping,但是不能更新update field mapping

mapping的类型主要有如下几种

在这里插入图片描述

整体核心架构

在这里插入图片描述

倒排索引

假设我们有如下内容,看看是如何建立倒排索引的

文档ID 文档内容
1 Test ElasticSearch
2 ElasticSearch Server
3 ElasticSearch Server1

建立的倒排索引如下

Term Count DocumentId:Position
ElasticSearch 3 1:1,2:0,3:0
Test 1 1:0
Server 1 2:1
Server1 1 3:1

es中的每个字段都有自己的倒排索引。
我们也可以对某个字段不做索引

  • 优点: 节约存储空间
  • 缺点: 无法被搜索到

分词

简单来说就将我们的文本通过分词分成很多词根然后去建立倒排索引供我们搜索
我们在搜索的时候也会将我们的搜索词通过分词去搜索

ElasticSearch内置分词器

Standard Analyzer

  • 默认分词器,按词切分,小写处理
POST _analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ the, 2, quick, brown, foxes, jumped, over, the, lazy, dog’s, bone ]

Simple Analyzer:

  • 每当遇到不是字母的字符时,分析器就把文本分成词组。它把所有的词组都小写
POST _analyze
{
  "analyzer": "simple",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ the, quick, brown, foxes, jumped, over, the, lazy, dog, s, bone ]

Whitespace Analyzer

  • 按空格切分,不转小写
POST _analyze
{
  "analyzer": "whitespace",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ The, 2, QUICK, Brown-Foxes, jumped, over, the, lazy, dog’s, bone. ]

Stop Analyzer

  • 小写处理,停用词过滤(the、a、is)
POST _analyze
{
  "analyzer": "stop",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ quick, brown, foxes, jumped, over, lazy, dog, s, bone ]

Keyword Analyzer:

  • 不分词 直接接当输入当做输出
POST _analyze
{
  "analyzer": "keyword",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ The 2 QUICK Brown-Foxes jumped over the lazy dog’s bone. ]

Pattern Analyzer

  • 正则表达式分词,默认\W+(非字符分割)
POST _analyze
{
  "analyzer": "pattern",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

[ the, 2, quick, brown, foxes, jumped, over, the, lazy, dog, s, bone ]

Language Analyzers

  • 语言分词,提供了30多种语言

在这里插入图片描述

Fingerprint Analyzer:

  • 主要用于消除重复词
POST _analyze
{
  "analyzer": "fingerprint",
  "text": "Yes yes, Gödel said this sentence is consistent and."
}

[ and consistent godel is said sentence this yes ]

自定义分词

  • 像中文默认是没有分词器的,我们可以通过安装配置中文分词器比如IK分词
    中华人民 -》 中华 人民

URI 搜索

  • 完整参数
GET /test_index/_search?q=小奏技术&df=name&sort=age:desc&from=0&size=1&timeout=1s
{
  "profile": "true"
}

简单解释参数的意义

  • Q: 查询语句
  • df:查询的默认字段,默认不指定查询全部字段
  • Sort: 排序
  • From&Size 分页
  • Profile: 查询查询过程

查询DSL

简单理解就是通过HTTP Request Body将查询请求发送到ElasticSearch

查询所有

GET test_index/_search
{
  "query": {
    "match_all": {}
  }
}

分页

GET test_index/_search
{
  "query": {
    "match_all": {}
  }
  , "from": 0,
  "size": 2
}
  • 默认返回10条结果
  • 翻页越靠后性能越低

排序

GET test_index/_search
{
  "query": {
    "match_all": {}
  }
  , "from": 0,
  "size": 2,
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

查询指定字段

GET test_index/_search
{
  "query": {
    "match_all": {}
  }
  , "from": 0,
  "size": 2,
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ],
  "_source": ["age", "name"]
}

Match

  • 对输入值进行分词查询
GET test_index/_search
{
  "query": {
    "match": {
      "name": "小奏 小奏技术"
    }
  }
}

在这里插入图片描述

实际等价于

GET test_index/_search
{
  "query": {
    "match": {
      "name": {
        "query": "小奏 小奏技术",
        "operator": "or"
      }
    }
  }
}

可以将 or 改成 and 意思就是必须保护 两者的分词
在这里插入图片描述

Term

  • 对输入值不做任何分词处理
GET test_index/_search
{
  "query": {
    "term": {
      "name": {
        "value": "小奏"
      }
    }
  }
}

在这里插入图片描述

match_phrase

  1. match_phase中的所有term都出现在待查询字段之中
  2. 2.待查询字段之中的所有term都必须和match_phase具有相同的顺序
GET test_index/_search
{
  "query": {
    "match_phrase": {
      "name": "小奏 小奏技术"
    }
  }
}

term、match、match_phrase区别

  • term:传入的文本原封不动地(不分词)拿去查询。
  • match:对输入进行分词处理后再去查询,部分命中的结果也会按照评分由高到低显示出来
  • match_phrase: match_phrase按短语查询,会将输入的text进行分词然后进行查询,只有包含所有分词并且分词顺序一致才会查询出来

term、match_phrase都是用于精准匹配,match用于模糊匹配

query_string

GET test_index/_search
{
  "query": {
    "query_string": {
      "default_field": "name",
      "query": "小奏 AND 小奏技术"
    }
  }
}

类似URI Query

simple_query_string

GET test_index/_search
{
  "query": {
    "simple_query_string": {
      "query": "小奏 AND 小奏技术",
      "fields": ["name"]
    }
  }
}

query_string 与 simple_query_string 区别

query_string查询小奏 AND 小奏技术,会解析成查询必须要同时包含小奏小奏技术
simple_query_string则是将其分词成小奏,AND小奏技术,默认的operator为OR

filter

GET test_index/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "name": "haofeng"
        }
      },
      "boost": 1.2
    }
  }
}

有时候我们在一些查询不涉及到相关度评分,或者是完全的精准匹配。我们就可以使用filter代替query
使用filter的好处

  1. filter 可以忽略TF-IDF计算,避免相关性算分的开销
  2. filter可以有效利用缓存

Filter 查询多条件

GET test_index/_search
{
  "query": {
    "constant_score": {
      "filter": {
       "bool": {
         "should":[
           {
             "term":{"age":221}
           },
           {
             "term":{"age":222}
           }
           ]
       }
      },
      "boost": 1.2
    }
  }
}

bool查询

bool查询是一个或多个查询子句的组合

总共四种子句。两种计分、两种不计分

  • must:必须匹配,参与计分
  • Should: 选择性匹配,参与计分
  • must_not: 必须不匹配 不参与计分
  • Filter:必须匹配,但不计分
GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "name": {
              "value": "小奏"
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "age": {
              "value": "222"
            }
          }
        }
      ],
      "should": [
        {
          "term": {
            "content": {
              "value": "测试"
            }
          }
        },
        {
          "term": {
            "post_date": {
              "value": "2022-09-03"
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

Boost

boost主要用于控制相关得分的

  • 当boost > 1 ,相关度得分提升
  • 当 0< boost < 1.相关读得分降低

简单举例

GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "name": {
              "value": "haofeng",
              "boost": 2
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "age": {
              "value": "222"
            }
          }
        }
      ],
      "should": [
        {
          "term": {
            "content": {
              "value": "测试",
              "boost": 2
            }
          }
        },
        {
          "term": {
            "post_date": {
              "value": "2022-09-03"
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

Scroll

在搜索返回单页结果的时候。如果数据量很大,一次性查询比如10w数据量数据性能可能会很差,elasticsearch提供了scoll滚动查询,一批一批的查,直到所有数据都查询完处理完

每次scroll搜索我们需要指定一个时间窗口,每次查询请求在这个时间窗口完成就可以

GET test_index/_search?scroll=1m
{
  "query": {
    "match": {
      "name":{
        "query": "小奏"
      }
    }
  }
}

获得的结果会有一个scoll_id,下一次再发送scoll请求的时候,必须带上这个scoll_id

GET /_search/scroll
{
 "scroll":"1m",
 "scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFms4a1IyNTNlUzlHTmppcTQtV3ZEVEEAAAAAAGmsnBZLNktRZVZBbFFTUzdyTk5EZWZ6TWxn"
}

scroll看着像分页,但实际应用场景可能有所不同,分页是一页一页搜索给用户看。scroll主要用于一批一批查询,让系统进行处理

Aggregation

聚合简单理解就是对数据进行分组聚合查询
类似

Mysql 的 select count(lable) from  product group by type

其中
count(lable) == Metric
Group by type == Bucket

下面我们深入理解下Aggregation中的两个核心概念

Bucket

city name
北京 小张
北京 老李
广州 小奏
广州 关羽
广州 张飞

按city划分bucket,分为北京、广州两个bucket

  • 北京bucket:小张、老李
  • 广州bucket:小奏、关羽、张飞

Metric

当我们有了bucket之后我们可以对这一系列的bucket数据进行聚合分析。
简单理解就是Metric就是对Bucket进行聚合分析,比如求 平均值、最大值、最小值

简单实操

数据准备
  • 创建索引
PUT /tvs
{
        "mappings": {
                        "properties": {
                                "price": {
                                        "type": "long"
                                },
                                "color": {
                                        "type": "keyword"
                                },
                                "brand": {
                                        "type": "keyword"
                                },
                                "sold_date": {
                                        "type": "date"
                                }
                        }
        }
}
  • 插入数据
POST /tvs/_bulk
{ "index": {}}
{ "price" : 1000, "color" : "红色", "brand" : "长虹", "sold_date" : "2016-10-28" }
{ "index": {}}
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2016-11-05" }
{ "index": {}}
{ "price" : 3000, "color" : "绿色", "brand" : "小米", "sold_date" : "2016-05-18" }
{ "index": {}}
{ "price" : 1500, "color" : "蓝色", "brand" : "TCL", "sold_date" : "2016-07-02" }
{ "index": {}}
{ "price" : 1200, "color" : "绿色", "brand" : "TCL", "sold_date" : "2016-08-19" }
{ "index": {}}
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2016-11-05" }
{ "index": {}}
{ "price" : 8000, "color" : "红色", "brand" : "三星", "sold_date" : "2017-01-01" }
{ "index": {}}
{ "price" : 2500, "color" : "蓝色", "brand" : "小米", "sold_date" : "2017-02-12" }
查询每种电视机的销量
GET /tvs/_search
{
  "size": 0, 
  "aggs": {
    "group_colors": {
      "terms": {
        "field": "color"
      }
    }
  }
}
  • size:0,不展示原始数据
  • aggs: 要对一份数据执行分组聚合操作
  • group_colors:就是对每个aggs,都要起一个名字,这个名字是随便自定义的
  • terms:根据字段的值进行分组
  • field:根据指定的字段的值进行分组
查询每种颜色电视机的平均价格
GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "colors": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

等价于如下sql

Select avg(price) from tvs._doc group by color

多 metric查询

在sql 查询中使用的比较多的就是

  • Count
  • Avg
  • Max
  • Min
  • Sum
GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_colors": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "min_price":{
          "min": {
            "field": "price"
          }
        },
        "max_price":{
          "min": {
            "field": "price"
          }
        },
        "sum_price":{
          "sum": {
            "field": "price"
          }
        }
      }
    }
  }
}

histogram

指定一个interval去划分bucket
比如指定interval为2000,则划分的区域为
[0,2000) [2000,4000) [4000,6000) [6000,8000) [8000,10000)

GET tvs/_search
{
  "size": 0,
  "aggs": {
    "price": {
      "histogram": {
        "field": "price",
        "interval": 2000
      },
      "aggs": {
        "revenue": {
          "sum": {
            "field": "price"
          }
        }
      }
    }
  }
}

下钻分析

按颜色、颜色+品牌下钻分析

GET /tvs/_search
{
  "size": 0,
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "color_avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "group_by_brand":
        {
          "terms": {
            "field": "brand"
          },
          "aggs": {
            "brand_avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}

参考

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到