当前位置: 首页 > 工具软件 > Elastic-Bg > 使用案例 >

分布式搜索引擎:Elasticsearch 快速入门

敖硕
2023-12-01

本文中使用的 ES 为 7.x 版本。

一、Elasticsearch介绍

1.1 介绍

Elasticsearch(以下简称 ES)是一个开源的高扩展的分布式全文搜索引擎,它可以近乎实时的存储、查询数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。

ES 的底层是开源库 Apache Lucene,由于Lucene 库的原理十分复杂,还需要我们自己写代码实现接口,所以 ES 就使用 RESTful 风格的 API 封装了 Lucene,使其更加简单易用

1.2 安装(Ubuntu 20.04)

  1. 导入 ES 的 PGP 密钥:

    wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
    
  2. 上安装 apt-transport-https 包:

    sudo apt-get install apt-transport-https
    
  3. 添加存储库:

    echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list
    
  4. 安装 ES:

    sudo apt-get update && sudo apt-get install elasticsearch
    
  5. 启动服务:

    sudo systemctl start elasticsearch.service
    
  6. 测试安装:

    curl -X GET "localhost:9200/?pretty"
    

    出现主机、版本等信息,就说明安装成功。

  7. 设置开机自启动(可选):

    sudo systemctl enable elasticsearch.service
    

1.3 安装 Kinaba (官方客户端,可选)

  1. 到官网下载压缩包:传送门,这里以 windows 系统为例。

  2. 解压到合适的目录。

  3. 修改 config/kibana.yml 文件,找到 elasticsearch.hosts,去掉注释,改为 ES 的链接地址:

    elasticsearch.hosts: ["http://localhost:9200"]
    
  4. 运行 bin/kibana.bat(windows下)。

  5. 用浏览器打开 http://localhost:5601。

  6. 主要是使用 dev tools,其他的看着玩玩。

1.4 核心概念

  • 集群(Cluster):

    ES 可以运行在许多互相合作的服务器上,以实现容错和高可用性,这些服务器的集合称为集群。

  • 节点(Node):

    集群中的每一个服务器称为一个节点。

  • 分片(Shard):

    所有数据存储的最小单元块。

  • 副本(Replia):

    副本是一个分片的精确复制,可以提高查询吞吐量或实现高可用性。每个分片可以有零个或多个副本。

  • 全文查询(Full-text Search):

    全文索引就是把内容根据词的意义进行分词,然后分别创建索引,例如”今日是周日我们出去玩” 可能会被分词成:“今天“,”周日“,“我们“,”出去玩“ 等token,这样当你搜索“周日” 或者 “出去玩” 都会把这句搜出来。

1.5 与 MySQL 对比

ES 本质上是一个分布式数据库,所以,其中的很多概念在关系型数据库中都能找到与之相类似的概念。

ESMySQL
Index 索引Database 数据库
Type 类型(7.x 版本后被移除!Table 表
Document 文档Row 记录或行
Field 字段Column 字段或列
Mapping 映射Schema 结构
Everything is indexedIndex 索引
Query DSLSQL
GET http://……SELECT * FROM table ……
PUT http://……UPDATE table SET ……

补充说明:

  • ES 会索引所有字段,经过处理后写入一个反向索引(Inverted Index,也叫倒排索引)。查找数据的时候,直接查找该索引。所以,Elastic 数据管理的顶层单位就叫做 Index(索引)

  • 同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

  • 类型可以看作是对同一索引下文档的分组(7.x 版本已经移除这一概念)。

  • 在创建索引的时候,可以预先定义字段的数据类型及相关属性(静态映射),ES 也可以自己推断出数据类型(动态映射,不建议)。映射就是对字段数据类型的定义。

  • 所有的操作都是通过 http 请求完成,而不是使用特定客户端的命令。

二、反向索引原理

反向索引(Inverted Index)也叫倒排索引,与之对应的是正向索引(Forward Index)。简单来说,正向索引是通过 key 找 value,反向索引则是通过 value 找 key。

我们以古诗的搜索为例,看看这两种索引建立方法各自的表现:

  • 正向索引:一般以古诗在数据库中的 id 号作为索引,查询包含“明月”一词的古诗时,需要一个个读取古诗的内容,显然不适合我们的应用场景。
  • 反向索引在建立索引之前,将古诗的内容进行分词处理,比如“床前明月光”拆分为“床、前、明月、月光、光”。然后以这些词作为索引,以包含这些词的所有古诗为文档(MySQL 中叫记录)。这样,只要在索引中找到“明月”,就能一下子找到所有“明月”古诗。

三、ES 安装插件

ES 本身只支持英文分词,所以我们需要安装一个 Elasticsearch-Analysis-IK 插件(以下简称IK)来使其支持中文分词,我们就以此为例,学习一下 ES 的插件安装。

3.1 离线安装

  1. 从 github 下载 IK:传送门,注意版本要和 ES 的版本对应,IK 的 github 首页有对应表。

  2. 创建插件目录:

    cd ES的根目录/plugins/ && mkdir ik
    

    使用 apt 安装的 ES 根目录为 /usr/share/elasticsearch。

  3. 解压到 plugins/ik 目录。

3.2 在线安装

  1. 使用 elasticsearch-plugin 插件安装,在 ES 根目录下执行:

    sudo ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip
    

    注意!将“7.17.0”更换为你自己的 ES 版本。

安装完成后,重启 ES 使用即可。

四、ES 操作

下面的操作,均在 Kibana 的 Dev Tools 中完成。当然,也可以使用 curl 命令(如果会用 curl 的话),甚至 postman 等工具。

4.1 索引操作

  • 新增索引:

    PUT /索引名称
    {
      "settings": {
        "index": {
          "number_of_shards": 分片数量, 
          "number_of_replicas": 副本数量
        }
      }
    }
    

    注意!每个 Index (即数据库)的名字必须是小写。

    settings 是可选的。

  • 查询索引:

    GET /索引名称
    
  • 删除索引:

    DELETE /索引名称
    

4.2 映射操作

ES 6.x 及以上版本中创建的索引只包含一个类型,不过旧版本创建的索引可以正常使用。而在 7.x 版本中类型被完全移除。

4.2.1 字段类型

  • 字符串类型:text、keyword;
  • 数字类型:long、integer、short、byte、double、float;
  • 日期类型:date;
  • 布尔类型:boolean;
  • 二进制类型:binary;
  • 复杂类型:object(对象或实体)、nested(列表);
  • geo 类型:geo-point、geo-shape(地理位置);
  • 专业类型:ip、competion(搜索建议)。

4.2.2 映射参数

属性描述适合类型
store值为yes表示存储,no表示不存储,默认为noall
indexyes表示分析,no表示不分析,默认为truetext
null_value如果字段为空,可以设置一个默认值,比如"NA"(传过来为空,不能搜索,na可以搜索)all
analyzer可以设置索引和搜索时用的分析器,默认使用的是standard分析器,还可以使用whitespace,simple。都是英文分析器all
include_in_all默认es为每个文档定义一个特殊域_all,它的作用是让每个字段都被搜索到,如果想让某个字段不被搜索到,可以设置为falseall
format时间格式字符串模式date

4.2.3 映射操作

  • 创建索引的同时创建映射:

    PUT 索引名称
    {
      "mapping":{
        "字段名称":类型,
        "age":    { "type": "integer" },  
        "email":  { "type": "keyword"  }, 
        "name":   { "type": "text"  }  
      }
    }
    
  • 向已存在映射添加字段:

    PUT 索引名称/_mapping
    {
      "properties": {
        "字段名称":类型,
      }
    }
    
  • 更新映射:

    不允许更新,只能新建索引,然后将重新索引数据。

  • 查询映射:

    GET 索引名称/_mapping
    
  • 删除映射:

    只能连通索引一起删除。

4.3 文档操作

  • 索引文档(即插入文档或者说给文档建立索引):

    POST 索引名称/_doc
    {
      "@timestamp": "2099-05-06T16:21:15.000Z",
      "event": {
      "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
      }
    }
    
    • _doc 就是7.x 及更高版本默认的类型(type),不允许再创建。

    • 插入文档时,可以不按照映射来,多几个或少几个字段都能正常插入。

    • 如果向不存在的索引插入文档,会自动创建索引。

  • 查询文档:

    GET 索引名称/_doc/id号
    
  • 修改文档:

    POST 索引名称/_update/id号
    {
      "doc": {
        "name": "new_name"
      }
    }
    
  • 删除文档:

    DELETE 索引名称/_doc/id号
    

4.4 文档查询

4.4.1 全文查询

全文查询会将用户给出的关键字进行分词处理,然后用分好的词进行查询。

  • match:

    分词后任意一个词项匹配,文档就会查询到。

    GET 索引名称/_doc/_search
    {
      "query":{
        "match":{
          "字段":"值"
          "name":"hugh"
        }
      }
    }
    
  • match_phrase:

    文档满足以下条件才能被匹配:

    • 分词后所有词项都要出现在该字段中。
    • 字段中所有的词项顺序要一致
    GET 索引名称/_doc/_search
    {
      "query":{
        "match_phrase":{
          "name":"hugh"
        }
      }
    }
    
  • match_phrase_prefix:

    和match_phrase搜索类似,只不过支持词项的前缀匹配。

    GET 索引名称/_search
    {
      "query": {
        "match_phrase_prefix": {
          "name":"hu"
        }
      }
    }
    
  • multi_match搜索:

    用于搜索多个字段。

    GET 索引名称/_search
    {
      "query": {
        "multi_match": {
            "query": "hugh",
            "fields": ["student", "Chinese"]
        }
      }
    }
    
    

    上述语句会在studentChinese字段中查询包含单词 hugh 的文档。

4.4.2 词语级别查询

与全文查询不同,词语级查询不会将关键字分词,而是精确匹配关键字。

  • term:

    返回“地址”字段包含关键字“亚洲中国”的文档。

    GET 索引名称/_search
    {
      "query": {
        "term": {
          "地址":"亚洲中国"
        }
      }
    }
    
  • terms:

    可以指定多个关键字,在文档中匹配到其中一个或全部,就返回该文档。

    GET 索引名称/_search
    {
      "query": {
        "terms": {
          "user.id": [ "kimchy", "elkbee" ],
          "boost": 1.0
        }
      }
    }
    

4.4.3 布尔查询

通过下面四个操作符,将多个简单查询组合在一起:

  • must:必须匹配其中的条件,等价于 and。

  • should:可以匹配也可以不匹配,但是匹配到的文档,会增加评分(评分越高,排名越靠前)。

  • must_not:会剔除匹配到的文档。

  • filter:过滤子句,实现更加复杂的过滤。(下一小节单独学习)

    GET 索引名称/_search
    {
      "query": {
        "bool": {
          "must": [{
            "term": {
              "text_entry": {
                "value": "must"
              }
            }}
          ],
          
          "should": [{
            "term": {
              "text_entry": {
                "value": "have"
              }
            }}
          ]
        }
      }
    }
    

4.4.5 过滤查询

  • 直接过滤:

    给出字段和值,不符合的直接被过滤掉。

    GET 索引名称/_search
    {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "字段": "值"
            }
          }
        }
      }
    }
    
  • 范围过滤:

    和 range 合用,过滤掉不在范围内的结果。range 选项:

    • gt : 大于。
    • lt : 小于。
    • gte : 大于等于。
    • lte :小于等于。
    GET 索引名称/_search
    {
      "query": {
        "bool": {
          "must": {
            "term": {
              "merchant_id": "2501"
            }
          }, 
          "filter": {
            "range": {
              "age": {
               "lte":25
              }
            }
          }
        }
      }
    }
    

4.5 排序

GET 索引名称/_doc/_search
{
  "query":{
    "match":{
      "字段":"值"
      "name":"hugh"
    }
  },
  "sort":[
    {
      "排序字段":{
        "order":"asc升序或者desc降序"
      }
    }
  ]
}

4.6 分页

GET 索引名称/_doc/_search
{
  "query":{
    "match":{
      "字段":"值"
      "name":"hugh"
    }
  },
  "from": 起始位置,
  "size": 每页文档数
}

4.7 高亮显示

GET 索引名称/_doc/_search
{
  "query":{
    "match":{
      "字段":"值"
      "name":"hugh"
    }
  },
  "highlight":[
    {
      "pre_tags":"在搜索结果之前加的样式标签",
      "post_tags":"在结果后面加的样式标签",
      "fields":{
        "要高亮的字段":{}
      }
    }
  ]
}

4.8 聚合(Aggregations)

  • max:

    GET 索引名称/_doc/_search
    {
      "aggs": {
        "max_price": { "max": { "field": "price" } }
      }
    }
    
  • min:

    GET 索引名称/_doc/_search
    {
      "aggs": {
        "min_price": { "min": { "field": "price" } }
      }
    }
    
  • sum:

    GET 索引名称/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "match": { "type": "hat" }
          }
        }
      },
      "aggs": {
        "hat_prices": { "sum": { "field": "price" } }
      }
    }
    
  • avg:

    {
      "aggs": {
        "avg_grade": { "avg": { "field": "grade" } }
      }
    }
    

五、IK 的使用

  • IK 的两种分词方式:

    • ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;

    • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。

  • 设置方法:

    在建立索引时,通过 analyzer 指定:

    PUT 索引名称
    {
      "mappings":{
        "properties":{
          "字段名称":{
            "type":"text",
            "analyzer":"分词方式"
          },
          "字段名称":{
            "type":"text",
            "analyzer":"分词方式"
          }
        }
      }
    }
    

六、通过 python 使用 Elasticsearch

Python Elasticsearch Client 是 ES 官方提供的底层客户端。

  • 安装:

    pip install elasticsearch
    

    如果使用 async/await ,则使用下面的方法安装:

    pip install elasticsearch[async]
    
  • 快速入门:

    # 导入客户端
    from elasticsearch import Elasticsearch
    
    # 实例化客户端实例
    es = Elasticsearch()
    
    # 插入文档
    doc = {
        'author': 'kimchy',
        'text': 'Elasticsearch: cool. bonsai cool.',
        'timestamp': datetime.now(),
    }
    res = es.index(index="test-index", id=1, document=doc)
    
    # 查询文档
    res = es.search(index="test-index", body={"query": {"match_all": {}}})
    
    
 类似资料: