2021 版本 ElasticSearch 学习手册!

2021年09月15日 阅读数:5
这篇文章主要向大家介绍2021 版本 ElasticSearch 学习手册!,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

2021 版本 ElasticSearch 学习手册!_大数据

本篇文章是 ElasticSearch 这个热门技术的基础知识手册,一共 20 张图,1.5w字,图文结合,代码讲解。欢迎你们,点赞、收藏,更多相关优质文章能够点击下面的连接,都是此类的干货。php

下面是本文章的目录,能够先收藏、转发,有空在看。mysql

ElasticSearch 入门nginx

  • 新增数据git

  • 查询数据程序员

  • 更新数据github

  • 删除数据web

  • 批量操做

ElasticSearch 高级使用

  • 聚合操做

  • 映射操做

  • 分词操做

Spring Boot 整合 ElasticSearch

  •   ...

本篇文章的内容均以 Docker 环境为基础。

首先拉取镜像:

docker pull elasticsearch:7.4.2

而后下载kibana,这是一个可视化检索数据的工具:

docker pull kibana:7.4.2

建立两个文件夹用做数据卷:

mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data

建立一个配置文件并写入配置,使得外部机器可以访问 elasticsearch:

echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml

这样就能够启动 elasticsearch 了:

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2

其中开放的 9200 端口为向elasticsearch发送请求的端口,而 9300 为集群环境下elasticsearch之间互相通讯的端口;"discovery.type=single-node" 表示以单节点运行elasticsearch;ES_JAVA_OPTS="-Xms64m -Xmx128m" 用于指定elasticsearch的内存占用,并且必须指定,不然elasticsearch将占用系统的所有内存;最后设置elasticsearch的挂载点。

如果启动报错:

"Caused by: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes",

这是由于咱们的挂载点权限不足致使的,此时修改挂载点权限从新启动elasticsearch便可:

chmod 777 /mydata/elasticsearch/data/

而后启动 Kibana:

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://www.ithui.top:9200 -p 5601:5601 -d kibana:7.4.2

启动完成后访问 http://www.ithui.top:5601/:

ElasticSearch 入门

ElasticSearch 经过接收请求的方式来对数据进行处理,接下来对elasticsearch进行一个简单的入门。首先是 _cat 请求,经过该请求可以查询elasticsearch的一些基本信息,具体以下:

  • GET    /_cat/nodes:查看全部节点

  • GET    /_cat/health:查看elasticsearch的健康情况

  • GET    /_cat/master:查看主节点

  • GET    /_cat/indices:查看全部索引

好比查看 elasticsearch 的全部节点,则须要发送 http://www.ithui.top/_cat/nodes 请求,结果以下:

2021 版本 ElasticSearch 学习手册!_java_02

如果想查看 elasticsearch 的健康状态,则发送 http://www.ithui.top:9200/_cat/health 请求,结果以下:

2021 版本 ElasticSearch 学习手册!_编程语言_03

新增数据

elasticsearch经过接收PUT和POST请求来新增数据,然而在新增数据以前,咱们须要来了解elasticsearch中的几个概念:

  • 索引

  • 类型

  • 文档

  • 属性

咱们能够类比一下mysql中的概念来更形象地理解它们。在mysql中,如果想保存一条数据,咱们首先须要建立数据库,而后在数据库中建立数据表,最后将数据做为一条记录插入数据表;而elasticsearch中的索引就至关于mysql中的数据库,类型就至关于数据表,文档就至关于一条记录。

因此,如果想在elasticsearch中新增一条数据,咱们就须要指定这条数据放在哪一个索引的哪一个类型下,该条数据也被称为一个文档,并且这些数据是json格式的,json中的键被称为属性。

在elasticsearch中新增一条数据咱们有更加专业的说法,称其为 索引一个文档 ,接下来就能够发送一个请求 http://www.ithui.top:9200/customer/external/1 ,该请求表示向customer索引下的external文档存放一个标识为1的数据,数据能够存放在请求体中携带过去:

2021 版本 ElasticSearch 学习手册!_elasticsearch_04

咱们来分析一下该请求的返回结果:

{
    "_index": "customer",
    "_type": "external",
    "_id": "1",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

其中以 _ 开头的属性称为元数据,它表示的是elasticsearch中的基本信息,好比 _index 表示当前索引;_type 表示当前类型;_id 表示当前数据的标识;_version 表示版本;result 表示当前操做的状态,这里是新建状态,如果索引的文档已经存在,则状态为更新状态。

PUT请求方式一样也可以新增数据,然而它与POST有些许不一样,POST可以不携带id进行数据的保存,好比:

2021 版本 ElasticSearch 学习手册!_编程语言_05

此时elasticsearch会自动为咱们分配一个惟一的id,因此如果不携带id,则每次请求都将是一次新增数据的操做。然而PUT请求方式是没法实现这样的效果的,也就是说,如果使用PUT方式发送请求,则必须携带id,不然就会报错:

2021 版本 ElasticSearch 学习手册!_elasticsearch_06

查询数据

elasticsearch接收GET请求用于查询数据,好比发送 http://www.ithui.top:9200/customer/external/1 :

2021 版本 ElasticSearch 学习手册!_大数据_07

其中数据存放在 _source 中, _seq_no 属性和 _primary_term 属性是用来作并发控制的,数据在每次更新以后都会加1,是乐观锁机制。

它的原理是这样的,假设此时有两个请求同时来到而且均想要修改id为1的数据,那么可让这两个请求去判断一下当前的数据是不是最新的,怎么知道数据是最新的呢?

就是判断 seq_no_primary_term 属性,好比这个请求:http://www.ithui.top:9200/customer/external/1?if_seq_no=0&if_primary_term=1 。

它在更新数据前会去判断 seq_no 是否等于0, _primary_term 是否等于1,若成立,则证实数据是最新的,因此它可以修改为功:

2021 版本 ElasticSearch 学习手册!_java_08

此时须要注意了,当数据更新成功后, _seq_no 属性便会自动向上递增,此时第二个请求就没法更新了:

2021 版本 ElasticSearch 学习手册!_大数据_09

缘由就是当前的版本与指望的版本不一致,此时若想继续更新,就须要从新查询数据,获得并发控制属性值,再进行更新。

更新数据

在新增数据中咱们已然接触了更新数据,当要新增的数据已经存在时,新增操做就会变为更新操做,固然了,elasticsearch仍是为咱们提供了另一种更新方式。好比 http://www.ithui.top:9200/customer/external/1/_update :

2021 版本 ElasticSearch 学习手册!_数据库_10

须要注意的是,如果以这样的方式进行数据更新,则请求体数据必须用 doc 属性进行封装,它与不携带 _update 进行数据更新的方式有什么区别呢?

如果不携带 _update 更新数据,则每次发送请求elasticsearch都认为是一次更新,而携带 _update 更新数据,则elasticsearch会检查当前数据是否与原数据一致,若一致,则不会进行任何的操做,包括版本号、并发控制属性等都不会发生变化。

这里的PUT方式与POST方式效果同样,没有任何区别。

删除数据

elasticsearch经过接收DELETE请求来完成数据的删除操做。好比 http://www.ithui.top:9200/customer/external/1 ,经过该请求可以删除id为1的数据;elasticsearch还支持直接删除索引,好比 http://www.ithui.top:9200/customer ,该请求将删除customer索引;可是咱们没法直接删除类型,elasticsearch是不支持直接删除类型的。

批量操做

elasticsearch还支持批量操做,不过批量操做咱们就须要在kibana中进行测试了,咱们在前面已经启动了kibana的镜像,只需访问 http://www.ithui.top:5601/ 便可来到kibana的界面:

2021 版本 ElasticSearch 学习手册!_java_11

点击左侧导航栏的 Dev Tools 进入到开发工具:

2021 版本 ElasticSearch 学习手册!_大数据_12

编写好请求后点击运行图标便可发送请求,批量操做的数据格式很是有讲究:

{"index":{"_id":"1"}}
{"name":"zhangsan"}
{"index":{"_id":"2"}}
{"name":"lisi"}

这里的 "index" 表示新增操做,而且指定了数据的id为1,而具体须要新增的数据值是在第二行存放着:{"name":"zhangsan"} ;第三行也是如此,仍然是新增操做,指定数据id为2,数据值为:{"name":"lisi"},这样运行以后elasticsearch会执行两个新增操做,将这两个数据保存起来。

再好比这样一段复杂的批量操做:

POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}
{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"Test Title"}
{"index":{"_index":"website","_type":"blog"}}
{"title":"Test Title2"}
{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"Update Title"}}

首先是第一行, _bulk 指定了此次操做为批量操做,由于没有设置其它的任何信息,因此接下来的全部操做都须要指定索引、类型等信息;

第二行 delete 表示一个删除操做,其后指定了索引、类型以及要删除的数据id,由于删除操做不须要携带请求体数据,因此咱们能够直接在下一行编写第二个操做;

第三行 create 表示一个新增操做,并指定了索引、类型、数据id,第四行就是须要新增的数据值了;第五行与第六行也是一个新增操做;第七行和第八行是一个更新操做,并且由于是update更新,因此请求体数据须要用 doc 属性包装。

运行该操做,获得结果:

2021 版本 ElasticSearch 学习手册!_编程语言_13

第一个删除操做,由于不存在这样的一个数据,因此删除失败了,状态为404,可是后面的操做却成功了,从这里能够说明,elasticsearch批量操做中的每一个操做都是相互独立的,互相不会形成任何影响。

ElasticSearch高级使用

elasticsearch有两种检索方式,一种是前面说过的,发送REST请求,将数据以请求体的方式携带,还有一种方式就是直接将数据拼接在url路径上,好比:

GET bank/_search?q=*&sort=account_number:asc

它表示这是一个GET请求,要操做的索引是bank, _search 表示这是一次检索, q=* 表示查询全部, sort=account_number:asc 表示以 account_number 的值进行升序排序。

咱们还能够这样进行检索:

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "account_number": "asc"
    }
  ]
}

两种检索方式获得的结果是同样的,来介绍一下这种检索方式的语法。首先是 query ,它用于指定查询条件, match_all 表示匹配全部,大括号后面能够编写匹配的规则;其次是 sort ,它用于指定排序条件, account_number:asc 则表示以该属性的值进行升序排序,如果想指定多个规则,能够继续在 sort 属性中进行编写:

"sort": [
    {
      "account_number": "asc"
    },
    {
      "balance": "desc"
    }
  ]

此时则表示先按 account_number 进行升序,再按 balance 进行降序排序。

经过请求体数据进行检索的方式被称为 Query DSL ,即:查询领域对象语言,在elasticsearch中咱们将会大量地编写这种语言。查询领域对象语言的基本语法是:

{
 QUERY_NAME:{
   ARGUMENT:VALUE,
    ARGUMENT:VALUE,
    ....
  }
}

首先整个语句须要被一对大括号包含,在大括号内须要编写对应的操做,好比查询,就编写:

{
 "query":{
   
  }
}

query 中又须要指定查询的条件,好比匹配部分、匹配全部,以及匹配的规则等等,如果想要排序,则编写:

{
 "sort":[
  
  ]
}

sort 是一个数组,表示能够指定多个排序的规则。在其中还能够指定分页,只须要设置 fromsize 属性值便可:

GET bank/_search
{
  "from": 0,
  "size": 5
}

这里表示从第一条数据开始,每页显示5条数据。咱们还能经过指定 _source 属性来决定 elasticsearch 检索出的数据中包含哪些属性值,以剔除没必要要的属性:

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "account_number": "asc"
    }
  ],
  "from": 0,
  "size": 5,
  "_source": ["balance","firstname"]
}

此时表示只返回 balance  和 firstname 属性值。匹配规则中除了能够指定匹配所有外,还能够匹配指定的属性值,好比:

GET bank/_search
{
  "query": {
    "match": {
      "account_number": "20"
    }
  }
}

此时表示检索 account_number 为20的数据,这是一个精确检索的操做。它固然还支持模糊检索,好比:

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill lane"
    }
  }
}

此时表示检索 address 中包含mill和lane的数据,咱们来看看elasticsearch返回的结果:

2021 版本 ElasticSearch 学习手册!_编程语言_14

来观察一下这两条数据,第一条数据有一个 _score 的属性,它表示的是当前数据的得分状况,由于该数据中包含了Mill lane字符串,因此可以最大程度地匹配上咱们的匹配规则,故它的得分最高;再看第二条数据,由于该数据中只包含了Mill而没有Lane,因此它的匹配度更低一些,故而得分低一些。

由此可知,elasticsearch会将匹配规则中包含的全部字符串都去与待检索的数据进行匹配,实际上,elasticsearch底层采用的是分词策略,具体是如何进行分词的咱们暂且先不作了解。咱们还能够根据得分状况去获知哪些数据与咱们想要的数据匹配度更接近。

虽然elasticsearch能够进行分词模糊匹配,但咱们如果就想查询哪些数据中含有mill road呢?这个时候,咱们能够采用 match_phrase 来实现短语匹配,好比:

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill lane"
    }
  }
}

咱们还能够进行多字段匹配,好比:

GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": ["address","city"]
    }
  }
}

此时表示在 addresscity 属性中匹配mill字符串,假若有一个知足,都符合咱们的匹配规则。

当咱们须要同时指定多个检索规则的时候,咱们可使用 bool 属性完成复合检索:

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "gender": "F"
          }
        },
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "age": "28"
          }
        }
      ],
      "should": [
        {
          "match": {
            "lastname": "Wallace"
          }
        }
      ]
    }
  }
}

在这段语句中,首先是 bool 属性复合了四个检索条件,而后是 must 属性,它表示必须知足检索条件,条件为 gender 等于F, address 包含mill;而 must_not 表示必须排除检索条件,条件为 age 等于28;最后是 should ,它表示应该知足检索条件,检索条件为 lastname 等于Wallace。

它的关键在于即便不知足该条件,这条数据也能够被查询出来,但若是某条数据知足了 should 指定的条件,它就会获得相应的得分,能够认为这是一个加分项。

咱们还能够经过 filter 过滤器实现检索,好比:

GET bank/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "age": {
            "gte": 18,
            "lte": 30
          }
        }
      }
    }
  }
}

这段语句表示检索年龄在18~30之间的数据,但须要注意的是,使用 filter 属性并不会影响数据的得分。

使用 match 可以实现精确检索,但elasticsearch推荐咱们使用 match 属性进行模糊检索,而使用 term 进行精确检索,好比:

GET /bank/_search
{
  "query": {
    "term": {
      "balance": "32838"
    }
  }
}

聚合

elasticsearch 除了可以检索数据,它还可以对数据进行分析,elasticsearch 中的聚合就提供了从数据中分组和提取数据的能力。

好比这样的一个需求,检索地址中包含mill的全部人的年龄分布以及平均年龄,该如何编写语句呢?

GET /bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    } 
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 10
      }
    },
    "ageAvg":{
      "avg": {
        "field": "age"
      }
    }
  }
}

首先经过 query 属性检索地址中包含mill的人,而后经过 aggs 指定聚合操做,第一个聚合为 ageAgg ,这是聚合操做的名字,能够取任意值,其中 terms 表示分布状况;第二个聚合为 ageAvg ,其中 avg 表示平均值,来看检索获得的结果:

2021 版本 ElasticSearch 学习手册!_大数据_15

其中 buckers 中的数据是每种年龄的分布状况,好比38岁的有2人,28岁的有1人;ageAvg 中的就是年龄的平均值了,为34.0。

又好比这个需求,按照年龄聚合,并求这些年龄段的人的平均薪资,语句就该这么写:

GET /bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "ageAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

这是一个嵌套的聚合操做,首先经过 terms 按照年龄聚合, size 表示分布有多少种状况,这里假设有100种,而后在该聚合的基础上进行平均聚合,这里须要注意平均聚合语句的位置是在年龄聚合的里面的。

映射

映射是用来定义一个文档,以及它所包含的属性是如何存储和索引的,但咱们发现,在使用elasticsearch的过程当中,咱们并无对数据进行类型的指定,这是由于elasticsearch会自动猜想映射类型,咱们能够经过请求查看映射信息:

GET bank/_mapping

获得结果:

{
  "bank" : {
    "mappings" : {
      "properties" : {
        "account_number" : {
          "type" : "long"
        },
        "address" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "age" : {
          "type" : "long"
        }
        ...
      }
    }
  }
}

每一个属性的 type 中都显示了它的类型。然而有些属性的类型并非咱们想要的,这个时候咱们能够修改指定属性的类型:

PUT /my_index
{
  "mappings": {
    "properties": {
      "age":{"type": "integer"},
      "email":{"type": "keyword"},
      "name":{"type": "text"}
    }
  }
}

按照咱们获取到的映射规则对其进行设置便可。此时咱们获取my_index索引的映射信息进行查看:

{
  "my_index" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "name" : {
          "type" : "text"
        }
      }
    },
    ...
  }
}

说明设置是成功的。但这种方式仅限于设置不存在的索引,如果须要设置的索引已经存在,则咱们没法经过这种方式来改变属性的类型。咱们须要采用另一种方式:

PUT /my_index/_mapping
{
  "properties":{
    "id":{"type":"long"}
  }
}

然而这种方式只能用于添加新的映射规则, 而不能修改以前属性的类型。因此如果想要修改已存在的属性类型,只能再建立一个新的索引,并指定好映射规则,再将以前的数据迁移到新的索引中来:

# 建立新的映射规则索引
PUT /newbank
{
  "mappings": {
    "properties": {
      "address": {
        "type": "text"
      },
      "age": {
        "type": "integer"
      },
      "balance": {
        "type": "long"
      },
      "city": {
        "type": "text"
      }
    }
  }
}

# 数据迁移
POST _reindex
{
  "source": {
    "index": "bank"
  },
  "dest": {
    "index": "newbank"
  }
}

分词

在前面有说到elasticsearch是经过分词来进行模糊检索的,而分词在elasticsearch中是经过tokenizer分词器实现的,分词器经过接收一个字符流,将其分割为独立的tokens词元。

elasticsearch中默认有一些分词器,但它们都只支持英文的分词,对于中文,咱们须要额外安装一个分词器,这里以 ik 分词器为例,首先下载ik分词器,下载地址:https://github.com/medcl/elasticsearch-analysis-ik

2021 版本 ElasticSearch 学习手册!_数据库_16

一样下载 7.4.2 版本的 ik 分词器:

2021 版本 ElasticSearch 学习手册!_数据库_17

在该位置复制下载地址,而后在服务器上进行安装,由于在启动 elasticsearch 镜像的时候咱们作了数据卷的映射,因此只需将其下载好存放在 /mydata/elasticsearch/plugins 目录下便可:

cd /mydata/elasticsearch/plugins
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip

下载完成后解压一下:

unzip elasticsearch-analysis-ik-7.4.2.zip

解压完成后重启elasticsearch,这样咱们就安装好了ik分词器,能够来测试一下:

POST _analyze
{
  "analyzer": "ik_smart",
  "text": "分词器检索。"
}

分词结果:

{
  "tokens" : [
    {
      "token" : "分词器",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "检索",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 1
    }
  ]
}

咱们来看看elasticsearch默认使用的分词器的分词效果:

POST _analyze
{
  "analyzer": "standard",
  "text": "分词器检索。"
}

分词结果:

{
  "tokens" : [
    {
      "token" : "分",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<IDEOGRAPHIC>",
      "position" : 0
    },
    {
      "token" : "词",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "<IDEOGRAPHIC>",
      "position" : 1
    },
    {
      "token" : "器",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "<IDEOGRAPHIC>",
      "position" : 2
    },
    {
      "token" : "检",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "索",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<IDEOGRAPHIC>",
      "position" : 4
    }
  ]
}

很显然,默认的分词器只是把每一个字都当成了一个词而已,在中文的处理上与ik分词器相比就要逊色不少了。

但有时候ik分词器仍然达不到咱们的需求,好比最近才流行起来的网络热词,ik分词器确定是没有办法对其进行分词的,因此咱们须要对其定制扩展词库,找到nginx映射目录下的html文件夹,在该文件夹下建立es文件夹用于存放ik分词器须要使用到的一些资源( 关于nginx的运行与配置请看文章最后一节 ):

cd cd /mydata/nginx/html/
mkdir es

而后在es目录下新建一个文本信息:

[root@izrcf5u3j3q8xaz es]# vim fenci.txt
[root@izrcf5u3j3q8xaz es]# cat fenci.txt 
乔碧萝

将须要定制的词放在该文本中便可,此时咱们测试一下访问

http://www.ithui.top/es/fenci.txt :

2021 版本 ElasticSearch 学习手册!_elasticsearch_18

没有问题,咱们继续下一步,来到ik分词器的配置目录:

cd /mydata/elasticsearch/plugins/ik/config/

修改它的配置文件:

vim IKAnalyzer.cfg.xml

修改内容以下,将定制的文本地址配置进去便可:

2021 版本 ElasticSearch 学习手册!_数据库_19

配置完成后必定要重启elasticsearch,此时咱们来测试一下:

POST _analyze
{
  "analyzer": "ik_smart",
  "text": "乔碧萝殿下。"
}

分词结果:

{
  "tokens" : [
    {
      "token" : "乔碧萝",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "殿下",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 1
    }
  ]
}

Spring Boot 整合 elasticsearch

前面介绍了elasticsearch的一些基础知识,接下来咱们来看看如何在SpringBoot应用中使用elasticsearch。

想要经过Java程序操做elasticsearch,咱们也须要发送请求给elasticsearch进行操做,这里咱们使用ElasticSearch-Rest-Client对elasticsearch进行操做。

首先引入依赖:

<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>7.4.2</version>
</dependency>

由于SpringBoot管理了elasticsearch的依赖版本,因此咱们须要指定一下elasticsearch的版本与其一致:

<properties>
  <elasticsearch.version>7.4.2</elasticsearch.version>
</properties>

而后编写一个配置类,向容器中注册一个操做elasticsearch的组件:

@Configuration
public class GuliMailElasticSearchConfig {

    @Bean
    public RestHighLevelClient esRestClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("www.ithui.top", 9200, "http")));
    }
}

接下来咱们就能够经过它操做 elasticsearch 了:

@Autowired
private RestHighLevelClient client;

@Data
class User {
    private String name;
    private Integer age;
    private String gender;
}

@Test
public void index() throws IOException {
    IndexRequest indexRequest = new IndexRequest("users");
    indexRequest.id("1");
    // indexRequest.source("name","zhangsan","age",20,"gender","男");
    User user = new User();
    user.setName("zhangsan");
    user.setAge(20);
    user.setGender("男");
    String json = JSON.toJSONString(user);
    indexRequest.source(json, XContentType.JSON);
    // 执行保存操做
    IndexResponse index = client.index(indexRequest, RequestOptions.DEFAULT);
    // 响应数据
    System.out.println(index);
}

RestHighLevelClient提供了很是多的方式用于保存数据,但比较经常使用的是经过json数据直接保存,首先须要指定索引, IndexRequest indexRequest = new IndexRequest("users"); 指定了users索引,而后指定数据id,接着指定数据值,最后使用client执行保存操做,而后能够拿到响应数据。

elasticsearch的其它简单操做,诸如:更新、删除等,都只须要转换一下调用方法便可,如更新操做,就须要使用client调用update方法,接下来咱们看看Java程序该如何实现较为复杂的检索操做。

好比如今想聚合出年龄的分布状况,并求出每一个年龄分布人群的平均薪资,就应该这样进行编写:

@Test
public void search() throws IOException {
    // 建立检索请求
    SearchRequest searchRequest = new SearchRequest("bank");
    // 指定须要检索的索引
    // searchRequest.indices("bank");
    // 指定检索条件
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 查询条件
    builder.query(QueryBuilders.matchQuery("address","mill"));
    // 聚合条件
    builder.aggregation(AggregationBuilders.terms("ageAgg").field("age"));
    builder.aggregation(AggregationBuilders.avg("balanceAvg").field("balance"));
    searchRequest.source(builder);
    // 执行检索请求
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    // 分析结果
    System.out.println(searchResponse.toString());
}

这些语法咱们在最开始接触elasticsearch的时候都已经了解过了,只不过这里是一个链式的方法调用而已。

Nginx

首先拉取 nginx 的镜像:

docker pull nginx:1.10

而后随意地启动一个 nginx 实例:

docker run -p 80:80 --name nginx -d nginx:1.10

启动该 nginx 实例的目的是将 nginx 中的配置文件复制出来:

docker container cp nginx:/etc/nginx .

这样当前目录下就会产生一个nginx文件夹,将其先重命名为conf,而后再建立一个nginx文件夹,并将conf文件夹移动进去:

mv nginx conf
mkdir nginx
mv conf/ nginx/

而后正式启动一个新的 nginx 实例:

docker run -p 80:80 --name nginx \
        -v /mydata/nginx/html:/usr/share/nginx/html \
        -v /mydata/nginx/logs:/var/log/nginx \
        -v /mydata/nginx/conf:/etc/nginx \
                -d nginx:1.10

将刚才准备好的 nginx 文件夹与 nginx 容器内的文件夹做一个一一映射。

公众号运营至今,离不开小伙伴们的支持。为了给小伙伴们提供一个互相交流的平台,特意开通了官方交流群。关注公众号「Java后端」回复「进群」便可。
推荐阅读 
1. 推荐几个好玩的 GitHub 项目
2. 推荐几个程序员经常使用的软件
3. 图解 Spring 解决循环依赖