#ElasticSearch 入门教程

安装

ElasticSearch安装(以linux为例)

  • 下载elasticsearch:

      curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.0-linux-x86_64.tar.gz
    
  • 解压

      tar -xvf elasticsearch-7.9.0-linux-x86_64.tar.gz
    
  • 启动

      cd elasticsearch-7.9.0/bin
      ./elasticsearch
    

    也可以将ES作为后台进程启动:

      ./bin/elasticsearch -d -p pid
    

    启动成功后会将进程id写入 pid 文件

  • 检查运行状态

      curl -X GET "localhost:9200/_cat/health?v&pretty"
      epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
      1599100417 02:33:37  elasticsearch green           1         1      0   0    0    0        0             0                  -                100.0%        
    

    green表示运行正常, 集群健康

  • 停止

    • 如果是前台运行的,则直接使用 Ctrl + C 停止服务
    • 如果是后台运行的,则找到进程id, 使用 kill pid 停止服务

安装ik分词器

  • 下载

    访问 https://github.com/medcl/elasticsearch-analysis-ik/releases,下载对应版本的分词器

  • 安装

      cd  elasticsearch-7.9.0/plugins
      mkdir ik
      unzip elasticsearch-analysis-ik-7.9.0.zip -d elasticsearch-7.9.0/plugins/ik
    
  • 重启ES

  • 查看是否安装成功

      curl 'localhost:9200/_cat/plugins?v&pretty'
      name              component   version
      jenkins.csiit.net analysis-ik 7.8.0
    

    说明已经安装成功

初步使用

基本概念

近实时(NRT)

ES是一个近实时搜索平台。 这意味着从你索引一个文档到这个文档可以备搜索有很小的延迟(大约1秒钟)。

集群(Cluster)

集群是一个或多个机器/节点管理你的所有数据, 在这些所有节点上提供联和索引和搜索功能。 一个集群通过一个唯一的集群名标识(默认是 elasticsearch)。 集群名非常重要,
只有当一个节点的集群名与集群一致的时候, 此节点才能加入到集群内。

节点(Node)

节点是集群中的一个单服务,有存储数据、参与集群索引和搜索的功能。 类似于集群, 一个节点也是通过一个唯一的节点名来标识, 默认的节点名是启动的时候生成的一个UUID,
你可以定义而任意节点名代替默认的。 这个名字很重要。当你进行集群管理的时候, 查看哪些节点在当前的网络中以及哪些节点在集群中, 都需要这个节点名来辨认。

一个节点可以配置集群名加入某个集群。默认情况下, 每个节点都加入名字为 elasticsearch 的集群, 这意味着, 如果你在一个网络内启动了多个节点, 并且假如他们能够发现彼此
,他们将自动组成一个集群, 叫 elasticsearch。

索引(index)

索引是一系列有共同特征的文档的集合。 例如, 你可以为顾客数据创建一个索引, 为产品的目录创建一个索引,还有另外一个订单索引。 一个索引通过索引名(必须是全小写)来标识。
当进行索引、搜索、更新、删除文档的时候需要此索引名来指明索引。

在一个集群内, 可以定义任意多个索引。

Type/类型/映射

在索引内, 可以定义一个或多个映射。 映射可以理解为逻辑上的索引分区, 一个映射的用途完全取决于用户。 一般来说, 映射用来定义有一系列相同属性的文档。 例如,
假设你有一个博客平台,并且将数据存入了一个索引。 在这个索引内部, 你可以为User定义一个映射, 为Blog定义一个映射, 为评论定义一个映射。

文档/Document

文档是可索引信息的基本单元。 例如, 你有一个文档对应于一个用户, 另一个文档对应于一个商品, 另一个文档对应于订单。 文档以JSON表示。

在索引的映射里面, 你可以存储任意多的文档。 虽然文档在物理上属于索引, 但是文档必须指定索引内的映射类型。 也就是说, 我们先定义索引,
然后创建映射, 然后将文档多索引到映射里面。

分片/复制

一个索引可能存储非常多的数据以至于超过单个节点的硬件限制。 例如一个有数十亿文档的索引, 大概占据1TB的磁盘空间, 这样可能就不适合存在一个节点(尽管可以这么做),
因为可能会导致查询搜索非常缓慢。

为了解决这个问题, ES提供了将索引细分到多个机器上面的能力, 称为分片。 在创建索引的时候, 可以指定此索引有几个分片。 每个分片都是独立、完整的,
可以存于集群中的任意一个节点。

分片特性非常重要, 有以下原因:

可以水平伸缩、扩容
可以在多个分片之间分布式、并行的操作查询(可能是在多个节点上), 这样可以增加性能/吞吐量.
至于分片如何分布、索引的文档如何聚合回到查询请求里面, 这些都是ES本身就提供的, 对用户是透明的。

基于以下两点原因,复制也很重要:

它提供了高可用防止节点或者分片不可用。 从这点来说, 从分片不会跟主分片分配到同一个节点上面。
可以扩展查询吞吐量, 因为查询可以并行的在从分片执行。 这一点类似于上面分片的功能。
总结一下, 每个索引可以切分到多个分片上。 一个索引也可以有0个或多个复制副本。 一旦配置了复制, 每个索引将会有一个主分片和从分片。分片的数量和复制的
数量可以在创建索引的时候指定。 索引创建之后, 可以动态修改复制的数量,但是不可修改分片的数量。

默认情况下, 每个索引分配5个主分片和1个从分片. 这意味着如果集群中有两个节点, 你的索引将包括5个主分片和另外5个从分片, 这样每个索引就是10个分片。

查看、创建索引(index)

ElasticSearch的索引即index,可以理解为MySQL的 Table (6.x之前可以理解为database, 6.x之后可理解为table)。

  • 查看索引

      curl -XGET 'localhost:9200/_cat/indices?v&pretty'
      health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
    

    此时还没任何索引。

  • 创建索引

      curl -XPUT 'localhost:9200/mj2_corp_info?pretty&pretty'
    
      {
        "acknowledged" : true,
        "shards_acknowledged" : true,
        "index" : "mj2_corp_info"
      }
    

    可以看到输出, 索引已经创建。 我们再次查看索引:

      curl 'localhost:9200/_cat/indices?v&pretty'
      health status index         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
      yellow open   mj2_corp_info NtZXt8V4QV-kRtbd9BkBAA   1   1          0            0       208b           208b
    

    已经可以看到刚刚创建的mj2_corp_info索引了。 至于状态是yellow, 则是因为mj2_corp_info没有分片。ES默认为每个索引创建一个复制分片,但是我们只有一个node,复制分片不能与
    主分片在同一个node上面,所以无法创建复制分片,状态就是yellow.

  • 删除索引

      curl -XDELETE 'localhost:9200/mj2_corp_info?pretty'
      {
        "acknowledged" : true
      }
    

查看、创建映射

ES的映射即mapping, 可以将mapping类比为MySQL的表(两者之间还是有很大区别的,见官方文档))。 6.x之前, 一个索引可以创建多个mapping, 不过6.x以后, 一个索引仅能创建一个mapping。

  • 查看mapping

      curl localhost:9200/mj2_corp_info/_mapping?pretty
    
      {
        "mj2_corp_info" : {
          "mappings" : { }
        }
      }
    

    此时还没有任何mapping

  • 创建mapping

      curl -XPOST http://localhost:9200/mj2_corp_info/_mapping -H 'Content-Type:application/json' -d'
      {
              "properties": {
                  "id":{
                      "type":"long"
                  },
                  "address": {
                      "type": "text",
                      "analyzer": "ik_max_word",
                      "search_analyzer": "ik_smart"
                  },
                  "build_state": {
                      "type":"integer"
                  },
                  "corp_type": {
                      "type": "integer"
                  },
                  "chargeman": {
                      "type":"keyword"
                  },
                  "name":{
                      "type":"text",
                      "analyzer": "ik_max_word",
                      "search_analyzer": "ik_smart"
                  },
                  "coalmine_code":{
                      "type":"keyword"
                  }
              }
      }'
    
      {"acknowledged":true}
    

    出现 acknowledged: true 则代表mapping已经创建成功. ES支持的属性类型,请参考官方文档-映射类型

    此时再度查看mapping:

      curl localhost:9200/mj2_corp_info/_mapping?pretty
      {
        "mj2_corp_info" : {
          "mappings" : {
            "properties" : {
              "address" : {
                "type" : "text",
                "analyzer" : "ik_max_word",
                "search_analyzer" : "ik_smart"
              },
              "build_state" : {
                "type" : "integer"
              },
              "chargeman" : {
                "type" : "keyword"
              },
              "coalmine_code" : {
                "type" : "keyword"
              },
              "corp_type" : {
                "type" : "integer"
              },
              "id" : {
                "type" : "long"
              },
              "name" : {
                "type" : "text",
                "analyzer" : "ik_max_word",
                "search_analyzer" : "ik_smart"
              }
            }
          }
        }
      }                
    

索引文档

  • 添加文档

      curl -XPUT -H 'Content-Type:application/json' 'http://localhost:9200/mj2_corp_info/_create/1' -d '
       {
          "id":77,
          "address":"赤峰市元宝山区元宝山镇",
          "build_state":3,
          "corp_type":1,
          "chargeman":"祝文东",
          "name":"内蒙古平庄煤业集团有限责任公司元宝山露天煤矿",
          "coalmine_code":"150403B0012000310033"
       }
      '
    
      {"_index":"mj2_corp_info","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}
    
  • 根据ID获取文档

      curl 'localhost:9200/mj2_corp_info/_doc/1?pretty'
      {
        "_index" : "mj2_corp_info",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "_seq_no" : 0,
        "_primary_term" : 1,
        "found" : true,
        "_source" : {
          "id" : 77,
          "address" : "赤峰市元宝山区元宝山镇",
          "build_state" : 3,
          "corp_type" : 1,
          "chargeman" : "祝文东",
          "name" : "内蒙古平庄煤业集团有限责任公司元宝山露天煤矿",
          "coalmine_code" : "150403B0012000310033"
        }
      }
    
  • 添加多个文档(批量)

      curl -XPUT -H 'Content-Type:application/json' 'http://localhost:9200/mj2_corp_info/_bulk?pretty' -d '
       {"index": {"_index":"mj2_corp_info"}}
       {"id":146,"address":"鄂尔多斯市准格尔旗纳日松镇","build_state":3,"corp_type":3,"chargeman":"马军","name":"内蒙古汇能煤电集团羊市塔煤炭有限责任公司二矿","coalmine_code":"150622B0016000200108"}
       {"index": {"_index":"mj2_corp_info"}}
       {"id":223,"address":"内蒙古自治区鄂尔多斯市准格尔旗大路镇","build_state":3,"corp_type":3,"chargeman":"段心灵","name":"内蒙古三维资源集团小鱼沟煤炭有限公司","coalmine_code":"150622B0016000200030"}
       {"index": {"_index":"mj2_corp_info"}}
       {"id":229,"address":"准格尔旗纳日松镇","build_state":3,"corp_type":3,"chargeman":"马军","name":"内蒙古汇能煤电集团羊市塔煤炭有限责任公司一矿","coalmine_code":"150622B0016000200017"}
      '
    
      {
        "took" : 173,
        "errors" : false,
        "items" : [
          {
            "index" : {
              "_index" : "mj2_corp_info",
              "_type" : "_doc",
              "_id" : "WL_ZUnQBQ1l86axHOlC2",
              "_version" : 1,
              "result" : "created",
              "_shards" : {
                "total" : 2,
                "successful" : 1,
                "failed" : 0
              },
              "_seq_no" : 1,
              "_primary_term" : 1,
              "status" : 201
            }
          },
          {
            "index" : {
              "_index" : "mj2_corp_info",
              "_type" : "_doc",
              "_id" : "Wb_ZUnQBQ1l86axHOlC8",
              "_version" : 1,
              "result" : "created",
              "_shards" : {
                "total" : 2,
                "successful" : 1,
                "failed" : 0
              },
              "_seq_no" : 2,
              "_primary_term" : 1,
              "status" : 201
            }
          },
          {
            "index" : {
              "_index" : "mj2_corp_info",
              "_type" : "_doc",
              "_id" : "Wr_ZUnQBQ1l86axHOlC8",
              "_version" : 1,
              "result" : "created",
              "_shards" : {
                "total" : 2,
                "successful" : 1,
                "failed" : 0
              },
              "_seq_no" : 3,
              "_primary_term" : 1,
              "status" : 201
            }
          }
        ]
      }
    

    批量接口,也可以通过文件调用:

      curl -XPUT -H 'Content-Type:application/json' 'http://localhost:9200/mj2_corp_info/_bulk?pretty' --data-binary '@corp_info.json'
    

    详细的批量语法, 请参考官方文档-文档批量操作

搜索文档

根据单一字段查询

  • 根据煤矿编码查询

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d ' 
      {
          "query":{
              "match":{
                  "coalmine_code":"152921B0016000110366"
              }
          }
      }
      '    
    
     {
       "took" : 6,
       "timed_out" : false,
       "_shards" : {
         "total" : 1,
         "successful" : 1,
         "skipped" : 0,
         "failed" : 0
       },
       "hits" : {
         "total" : {
           "value" : 1,
           "relation" : "eq"
         },
         "max_score" : 5.8309045,
         "hits" : [
           {
             "_index" : "mj2_corp_info",
             "_type" : "_doc",
             "_id" : "ab_mUnQBQ1l86axHBVCp",
             "_score" : 5.8309045,
             "_source" : {
               "corp_type" : "3",
               "address" : "内蒙古自治区阿拉善左旗温都尔勒图镇",
               "build_state" : "4",
               "coalmine_code" : "152921B0016000110366",
               "chargeman" : "王成滨",
               "name" : "阿拉善左旗青岭煤炭有限责任公司煤矿",
               "id" : 1031
             }
           }
         ]
       }
     }                       
    
  • 根据煤矿地址查询

      //查询煤矿地址中包含 关键字 “鄂尔多斯的”
      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d ' 
      {
          "query":{
              "match":{
                  "address":"鄂尔多斯"
              }
          }
      }
      '  
    
  • 根据煤矿原始ID查询

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d ' 
      {
          "query":{
              "match":{
                  "id": 1422
              }
          }
      }
      '  
    

复合查询

ES中的查询操作分为2种:查询(query)和过滤(filter)。查询即是之前提到的query查询,它(查询)默认会计算每个返回文档的得分,然后根据得分排序。
而过滤(filter)只会筛选出符合的文档,并不计算得分,且它可以缓存文档。所以,单从性能考虑,过滤比查询更快。

bool组合查询

bool查询可以组合多种叶子查询,包含如下:

  • must: 出现于匹配查询当中,有助于匹配度(_score)的计算
  • filter: 必须满足才能出现,属于过滤,不会影响分值的计算,但是会过滤掉不符合的数据
  • should: 该条件下的内容是应该满足的内容,如果符合会增加分值,不符合降低分值,不会不显示
  • must_not: 满足的内容不会出现,与filter功能相反,属于过滤,不会影响分值的计算,但是会过滤掉不符合的数据
示例
  • 查询地址包含 “鄂尔多斯” 且 build_state 为 3 的数据

    SQL: where address like '%鄂尔多斯%' and build_state = 3

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
      {   
          "from":5,
          "size": 20,
          "query":{
              "bool":{
                  "filter":[
                      {"term": {"address": "鄂尔多斯"}},
                      {"term": {"build_state": 3}}
                  ]
              }
          }
      }
      '    
    

    参数中的 fromsize 是用于分页参数。

  • 或 另一种写法

     curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
     {   
       "from":5,
       "size": 5,
       "query":{
           "bool":{
               "must":[
                   {"match": {"address": "鄂尔多斯"}},
                   {"term": {"build_state": 3}}
               ]
           }
       }
     }
     ' 
    

    match 与 term 区别: match会对输入的值进行分词,而term不会

  • 查询 地址包含 鄂尔多斯 且 build_state 不为 3 的数据 且 corp_type 为 1 的数据

    SQL: where address like '%鄂尔多斯%' and build_state != 3 and corp_type = 1

       curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
       {   
         "from":5,
         "size": 5,
         "query":{
             "bool":{
                 "must":[
                     {"match": {"address": "鄂尔多斯"}},
                     {"match": {"corp_type": 1}}
                 ],
                 "must_not": [
                      {"term": {"build_state": 3}}
                 ]
             }
         }
       }
       '   
    

       curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
       {   
         "from":5,
         "size": 5,
         "query":{
             "bool":{
                 "must":[
                     {"match": {"address": "鄂尔多斯"}}
                 ],
                 "must_not": [
                      {"term": {"build_state": 3}}
                 ],
                 "filter": [
                      {"term": {"corp_type": 1}}
                 ]
             }
         }
       }
       ' 
    
  • 查询 地址包含 鄂尔多斯 且 build_state 为 1,2,3 的数据 且 corp_type 为 1,3 的数据

    SQL: where address like '%鄂尔多斯%' and build_state in (1,2,3) and corp_type in (1,3)

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
       {   
         "from":5,
         "size": 5,
         "query":{
             "bool":{
                 "must":[
                     {"match": {"address": "鄂尔多斯"}},
                     {"terms": {"corp_type": [1,3]}},
                     {"terms": {"build_state": [1,2,3]}}
                 ]
             }
         }
       }
       '
    
  • 查询 地址包含 鄂尔多斯 且 build_state 不为 1,2,3 的数据 且 corp_type 为 1,3 的数据

    SQL: where address like '%鄂尔多斯%' and build_state not in (1,2,3) and corp_type in (1,3)

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
       {   
         "from":5,
         "size": 5,
         "query":{
             "bool":{
                 "must":[
                     {"match": {"address": "鄂尔多斯"}},
                     {"terms": {"corp_type": [1,3]}}
                 ],
                 "must_not": [
                      {"terms": {"build_state": [1,2,3]}}
                 ]
             }
         },
         "sort": [
              {"id": "desc"}               
         ]
       }
       '   
    

聚合

  • 查询地址包含鄂尔多斯的不同build_state的煤矿数量

    SQL: select build_state, count(*) from mj2_corp_info where address like '%鄂尔多斯%' group by build_state

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
      {   
          "query":{
              "bool":{
                  "must":[
                      {"term": {"address": "鄂尔多斯"}}
                  ]
              }
          },
          "aggs": {
              "build_state_count": {
                  "terms": {"field": "build_state"}
              }
          }
      }
      '  
    
  • 查询 每个 build_state 的 最大id与最小id

    SQL: select build_state, min(id),max(id) from mj2_corp_info group by build_state

      curl -XGET -H 'Content-Type:application/json' 'localhost:9200/mj2_corp_info/_search?pretty' -d '
      {   
          "aggs": {
              "build_state_count": {
                  "terms": {"field": "build_state"},
                  "aggs": {
                      "min_id": {
                          "min": {"field": "id"}
                      },
                      "max_id": {
                          "max": {"field": "id"}
                      }
                  }
              }
          }
      }
      '                             
    

SQL查询支持

ElasticSearch也支持SQL语句查询,例如:

curl -XGET -H 'Content-Type: application/json' 'localhost:9200/_sql?pretty' -d '
 {
    "query": "select * from mj2_corp_info where build_state in (1,2,3)",
    "fetch_size": 5,
    "filter": {
        "term": {"address": "鄂尔多斯"}
    }
 }
 '

或 聚合

curl -XGET -H 'Content-Type: application/json' 'localhost:9200/_sql?pretty' -d '
{
   "query": "select build_state, min(id),max(id) from mj2_corp_info group by build_state",
   "fetch_size": 5
}
'       

关于SQL的完整文档,请查看X-Pack Sql Access

ES所有的REST API 可参考官方文档

Q.E.D.