ElasticSearch安装及基本使用

0. 学习资料

官方文档:[开始搜索|弹性搜索指南 7.5] |弹性的 (elastic.co)

ES(一):ES基本概念和原理简单介绍https://blog.51cto.com/u_14286115/3328651

版本对比:(13条消息) Elasticsearch各个版本重要特性_elasticsearch常用版本_坚持是一种态度的博客-CSDN博客

1. 概念

mysql更适合做数据的持久化处理,增删改查操作。El更适合做数据的检索分析。可以秒级在商品中检索出所需要的信息。

image-20220409202623723

基本概念

  1. Index(数据库) 动词,相当于 MySQL 中的 insert; 名词,相当于 MySQL中的 Database

  2. Type(表) 在 Index(索引)中,可以定义一个或多个类型。 类似于 MySQL 中的 Table;每一种类型的数据放在一起

    注意:7.0版本之后,type类型被移除,7.0检索的时候type可带可不带,8.0不允许带

    移除原因:在同一index中,ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的,两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。

    去掉type能够使数据存储在独立的index中,这样即使有相同的字段名称也不会出现冲突,就像ElasticSearch出现的第一句话一样“你知道的,为了搜索····”,去掉type就是为了提高ES处理数据的效率。

    查询时,我们可以将所有的type的值都使用_doc 如:查询索引consumer中id为1的数据。 GET /consumer/ _ doc/1

  3. Document(记录) 保存在某个索引(Index)下,某种类型(Type)的一个数据(Document),文档是 JSON 格 式的,Document 相当于Mysql一条记录

  4. mapping(表结构):mapping是对索引库中的索引字段及其数据类型进行定义,ES默认动态创建索引和索引类型的mapping。

  5. 倒排索引

image-20220409203139290

往ES中存储数据的时候,可以使用分词的方式。拿存储1:红海行动和5:特工红海特别探索为例。

  1. 红海行动可以拆分成,红海和行动。将红海行动存储到ES中,将红海和行动这两个词分别存放到ES的倒排索引中。

  2. 特工红海特别探索可以拆分成特工,红海,特别,探索。然后存储到倒排索引中,倒排索引中记录数据在ES中存放的位置。

  3. 查询红海行动时,会将红海行动分词,分成红海和行动,然后去倒排索引中查找

    1. 发现12345和123 都存储的有红海和行动,此时会进行相关性得分。
    2. 其中123 在两个倒排索引中都找到了,因此评分会更高,优先展示123对应的内容,靠后展示45出现的内容。

核心概念

1、分片(shard):
如果我们的索引数据量很大,超过硬件存放单个文件的限制,就会影响查询请求的速度,Es引入了分片技术。一个分片本身就是一个完成的搜索引擎,文档存储在分片中,而分片会被分配到集群中的各个节点中,随着集群的扩大和缩小,ES会自动的将分片在节点之间进行迁移,以保证集群能保持一种平衡。分片有以下特点:

  1. ES的一个索引可以包含多个分片(shard);
  2. 每一个分片(shard)都是一个最小的工作单元,承载部分数据;
  3. 每个shard都是一个lucene实例,有完整的简历索引和处理请求的能力;
  4. 增减节点时,shard会自动在nodes中负载均衡;
  5. 一个文档只能完整的存放在一个shard上
  6. 一个索引中含有shard的数量,默认值为5,在索引创建后这个值是不能被更改的。
  7. 优点:水平分割和扩展我们存放的内容索引;分发和并行跨碎片操作提高性能/吞吐量;
  8. 每一个shard关联的副本分片(replica shard)的数量,默认值为1,这个设置在任何时候都可以修改。

2、副本(replica):

副本(replica shard)就是shard的冗余备份,它的主要作用:

  1. 冗余备份,防止数据丢失;
  2. shard异常时负责容错和负载均衡;

ES的特性:

速度快、易扩展、弹性、灵活、操作简单、多语言客户端、X-Pack、hadoop/spark强强联手、开箱即用。

  1. 分布式:横向扩展非常灵活
  2. 全文检索:基于lucene的强大的全文检索能力;
  3. 近实时搜索和分析:数据进入ES,可达到近实时搜索,还可进行聚合分析
  4. 高可用:容错机制,自动发现新的或失败的节点,重组和重新平衡数据
  5. 模式自由:ES的动态mapping机制可以自动检测数据的结构和类型,创建索引并使数据可搜索。
  6. RESTful API:JSON + HTTP

2. 安装

ESdocker安装

1. 下载镜像
  1. docker pull elasticsearch:7.4.2 存储和检索数据
  2. docker pull kibana:7.4.2 可视化检索数据
2. 创建实例
  1. 创建挂载目录

    1. mkdir -p /mydata/elasticsearch/config

    2. mkdir -p /mydata/elasticsearch/data

    3. echo “http.host: 0.0.0.0” >> /mydata/elasticsearch/config/elasticsearch.yml

      1. echo 代表es可以被远程的任何服务访问,写入es的配置文件。
    4. 创建docker 容器

      1. # 创建配置文件目录
        mkdir -p /opt/docker/mid_elasticsearch_test/config
        
        # 创建数据目录
        mkdir -p /opt/docker/mid_elasticsearch_test/data
        
        # 将/mydata/elasticsearch/文件夹中文件都可读可写
        chmod -R 777 /opt/docker/mid_elasticsearch_test
        
        # 配置任意机器可以访问 elasticsearch
        echo "http.host: 0.0.0.0" >/opt/docker/mid_elasticsearch_test/config/elasticsearch.yml
            
        docker run --name elasticsearch_test  -p 9200:9200 -p 9300:9300 --restart=always \
        -e "discovery.type=single-node" \
        -e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
        -v /opt/docker/mid_elasticsearch_test/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
        -v /opt/docker/mid_elasticsearch_test/data:/usr/share/elasticsearch/data \
        -v /opt/docker/mid_elasticsearch_test/plugins:/usr/share/elasticsearch/plugins \
        -d elasticsearch:7.4.2
            
        # Docker 开机自启,所以 ES 现在也是开机自启
        docker update elasticsearch --restart=always
            
        # 参数解读
        ● -p 9200:9200 -p 9300:9300:向外暴露两个端口,9200用于HTTP REST API请求,9300 ES 在分布式集群状态下 ES 之间的通信端口;
        ● -e  "discovery.type=single-node":es 以单节点运行
        ● -e ES_JAVA_OPTS="-Xms64m -Xmx512m":设置启动占用内存,不设置可能会占用当前系统所有内存,制ES所占用的内存大小,测试服务器使用。
        ● -v:挂载容器中的配置文件、数据文件、插件数据到本机的文件夹;
        ● -d elasticsearch:7.6.2:指定要启动的镜像
            
        # 附:
        如果要修改ES的内存大小,可以停掉ES,删除容器,再重新创建一个容器,因为ES启动的时候进行了目录挂载,因此不会产生数据丢失。
        
  2. 问题:es容器闪退

    1. docker logs esid 查看es容器日志
    2. image-20220411203036153
    3. 无权限访问红色的目录,进入这个目录中查看。
    4. 查看data目录的访问权限
      1. ll 命令
      2. image-20220411203319588
      3. root 用户 可读可写,可执行,但是其他用户只能读和执行权限
      4. 将所有的权限都改成rwx,可读写执行。
        1. 命令:chmod -R 777 /mydata/elasticsearch/
        2. 递归将elasticsearch下的目录都设置为可读可写可执行。
        3. image-20220411203659609
        4. 重启docker 容器即可。
3. 浏览器访问:
  1. image-20220411203910618
4. 测试使用
  1. 查看所有节点信息

    image-20220411204702950

Kibana安装

1. 下载镜像

docker pull kibana:7.4.2 可视化检索数据

2. 创建实例
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://XX.XX.XX:9200 -p 5601:5601  -d kibana:7.4.2

 http://XX.XX.XX:9200 一定改为自己虚拟机的地址,表示要链接的es地址。
3. 效果

image-20230118110151948

3. 检索

查询、更新时,我们可以将所有的type的值都使用_doc 如:查询索引consumer中id为1的数据。 GET /consumer/ _ doc/1

es的所有请求,都封装成了restapi,我们查询数据发送请求即可。

_cat

GET /_ cat/nodes:查看所有节点

GET /_cat/health:查看 es 健康状况

GET / _ cat/master:查看主节点

GET /_cat/indices:查看所有索引 show databases

image-20220411210302421

保存

保存一个数据,保存在哪个索引的哪个类型下,指定用哪个唯一标识 .

PUT/POST customer/external/1;在 customer 索引下的 external 类型下保存 1 号数据

es存储的数据都是json对象。

# 已淘汰,执行后会提示[types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
PUT customer/external/1
{
"name": "John Doe"
}
# 最新  以下三种方式均可
POST customer/_create/6   id 为6
{
  "name": "张三"
}

POST customer/_doc/4    id 为 4
{
  "name": "张三"
}

POST customer/_doc      id随机生成
{
  "name": "张三"
}

结果:不管使用_doc还是_create,_type的结果都是_doc
{
  "_index" : "customer",
  "_type" : "_doc",
  "_id" : "10",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 8,
  "_primary_term" : 1
}


PUT 和 POST 都可以,
POST 新增。如果不指定 id,会自动生成 id。指定 id 就会修改这个数据,并新增版本号
PUT 可以新增可以修改。PUT 必须指定 id;由于 PUT 需要指定 id,我们一般都用来做修改
操作,不指定 id 会报错。

put新增:

image-20230119150615899

新增:

image-20230119150637031

查询

通过id查询

GET customer/external/1

淘汰:GET customer/external/1
最新:GET customer/_doc/1  type统一用_doc
    
{ "_index": "customer", //在哪个索引
"_type": "external", //在哪个类型
"_id": "1", //记录 id
"_version": 2, //版本号
"_seq_no": 1, //并发控制字段,每次更新就会+1,用来做乐观锁
"_primary_term": 1, //同上,主分片重新分配,如重启,就会变化
"found": true, "_source": { //真正的内容
"name": "John Doe"
}
}
乐观锁操作

_seq_no 表示乐观锁

假如A,B两个请求都过来了,都要修改同一条数据,如果A将数据修改后,那么_seq_no 会发生变化,由1变为2,B修改的时候,传递一个查询条件,_ seq_no 为1,那么此时B就不发生修改。

假如此时我同时发送两次更新请求:

A:http://39.XX.XX.XX:9200/customer/external/1?if_seq_no=2&if_primary_term=1 修改name = 1

此时_seq_no = “_seq_no”: 3,

B也发送http://39.XX.XX.XX:9200/customer/external/1?if_seq_no=2&if_primary_term=1 请求时,因为seq_no此时已变成3,因此修改不成功。

image-20220411212628115

更新

  1. post带update

    1. POST customer/external/1/_update
      { 
          "doc":{ "name": "John Doew"}
      }
      
    2. post携带update的时候,必须带doc

    3. 更新之后,再次更新时,如果最新的数据和原来的数据一样,那么什么都不进行更新。

  2. 直接使用update

    1. PUT customer/external/1
      { 
          "name": "John Doe"
      }
      
  3. 不同点:

    1. POST 操作会对比源文档数据,如果相同不会有什么操作,文档 version 不增加
      PUT 操作总会将数据重新保存并增加 version 版本;
      带_update 对比元数据如果一样就不进行任何操作。
      看场景;
      对于大并发更新,不带 update;
      对于大并发查询偶尔更新,带 update;对比更新,重新计算分配规则。
      
  4. 更新同时增加属性

    1. POST customer/external/1/_update
      { 
          "doc": { "name": "Jane Doe", "age": 20 }
      }
      PUTPOST 不带_update 也可以
      

删除

删除文档:DELETE customer/external/1

删除索引:DELETE customer
    
没有删除类型的操作。

批量操作

批量添加数据

POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }

语法格式

语法格式:
{ action: { metadata }}
{ request body }
{ action: { metadata }}
{ request body }

混合操作

POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123"} }
{ "doc" : {"title" : "My updated blog post"} }

image-20220411220058663

导入测试数据

G:\ComputerStudy\openitem\gulimallallfiles\课件和文档\高级篇\课件\es文件.txt

POST bank/account/_bulk
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@/文件路径"

注意:如果是官网下载的json文件,要打开删除最后一个空行,再按回车添加一个空行。官方文件:https://raw.githubusercontent.com/elastic/elasticsearch/7.5/docs/src/test/resources/accounts.json

问题:[使用ElasticSearch在bulk导入json数据时,The bulk request must be terminated by a newline \n]_BackToMeNow的博客-CSDN博客_bulkrequest写入json

4. 进阶搜索

基本检索

参考文档

[查询和筛选上下文|弹性搜索指南 7.5] |弹性的 (elastic.co)

基本检索

ES 支持两种基本方式检索 :

一个是通过使用 REST request URI 发送搜索参数(uri+检索参数)

GET /bank/_search  //查询bank索引下的所有数据
GET bank/_search?q=*&sort=account_number:asc
q=* :查询所有
sort = account_number:asc   排序方式

一个是通过使用 REST request body 来发送它们(uri+请求体)

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

返回结果:

image-20220412095044584

具体记录:

image-20220412095641874

image-20220412095717906

分页检索

For example, the following request gets hits 10 through 19:

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

https://www.elastic.co/guide/en/elasticsearch/reference/7.5/getting-started-search.html)

Query DSL

查询和筛选上下文

相关性分数

默认情况下,Elasticsearch 会按相关性对匹配的搜索结果进行排序 分数,用于衡量每个文档与查询的匹配程度。

相关性分数在查询结果中返回,级别越高,文档越相关。_score

image-20230118143005697

查询上下文

查询子句根据条件来决定文档匹配,还会计算字段中的相关性分数。

筛选上下文

回答查询子句当前记录是否符合,是否达到计算分数要求。

GET /_search
{
  "query": {   该参数指示查询上下文。query
    "bool": {  must两个子句用于查询上下文, 这意味着它们用于对每个文档进行评分比赛。boolmatch
      "must": [
        { "match": { "title":   "Search"        }},
        { "match": { "content": "Elasticsearch" }}
      ],
      "filter": [  该参数指示筛选器上下文。其 and 子句用于筛选器上下文。他们会过滤掉不匹配的文件,但它们不会影响匹配文档的分数。filtertermrange
        { "term":  { "status": "published" }},
        { "range": { "publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}

基本语法格式

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

# match_all 查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询;
# from+size 限定,完成分页功能;从第几条数据开始,每页有多少数据
# sort 排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;

基本返回结果

语法结构

Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSL(domain-specific language 领域特 定语言)。这个被称为 Query DSL。

查询语句 的典型结构:

image-20220412102022524

如果是针对某个字段:

image-20220412102109747

image-20220412102216410

image-20220412102233573

match【匹配查询】

image-20220412102407551

match_phrase【短语匹配】

image-20220412102515759

multi_match【多字段匹配】

image-20220412102557345

Term【精确检索】

和 match 一样。匹配某个属性的值。全文检索字段用 match,其他非 text 字段匹配用 term。

Avoid using the term query for text fields.

避免使用 term 查询文本字段

By default, Elasticsearch changes the values of text fields as part of analysis. This can make finding exact matches for text field values difficult.

默认情况下,Elasticsearch 会通过analysis分词将文本字段的值拆分为一部分,这使精确匹配文本字段的值变得困难。

To search text field values, use the match query instead.

如果要查询文本字段值,请使用 match 查询代替。

使用match

image-20220412105706771

使用term

image-20220412105728335

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "age": {
              "value": "28"
            }
          }
        },
        {
          "match": {
            "address": "990 Mill Road"
          }
        }
      ]
    }
  }
}
match的keyword

普通match

image-20220412105934277

keyword

image-20220412110016703

短语匹配

image-20220412110114125

bool【复合查询】

bool 用来做复合查询: 复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味 着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。

匹配与其他布尔组合匹配的文档的查询。类型:BooleanQuery

OccurDescription
mustThe clause (query) must appear in matching documents and will contribute to the score. 子句必须出现在匹配的文档中,并且贡献分数
filterThe clause (query) must appear in matching documents. However unlike the score of the query will be ignored. Filter clauses are executed in filter context, meaning that scoring is ignored and clauses are considered for caching.must 子句(查询)必须出现在匹配的文档中。
shouldThe clause (query) should appear in the matching document. 子句应该出现在匹配文档中
must_notThe clause (query) must not appear in the matching documents. Clauses are executed in filter context meaning that scoring is ignored and clauses are considered for caching. Because scoring is ignored, a score of for all documents is returned.0 子句(查询)不得出现在匹配项中 文件。

在boolean查询中,must, shouldmust_not 元素都被称为查询子句 。 文档是否符合每个“must”或“should”子句中的标准,决定了文档的“相关性得分”。 得分越高,文档越符合您的搜索条件。 默认情况下,Elasticsearch 返回根据这些相关性得分排序的文档。

“must_not”子句中的条件被视为“过滤器”。 它影响文档是否包含在结果中,但不影响文档的评分方式。还可以显式地指定任意过滤器来包含或排除基于结构化数据的文档。

POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user" : "kimchy" }
      },
      "filter": {
        "term" : { "tag" : "tech" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [
        { "term" : { "tag" : "wow" } },
        { "term" : { "tag" : "elasticsearch" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}

minimum_should_match:最小匹配度,必须紧跟should,用参数指定数字或返回文档的子句百分比必须匹配。

不是很明白,之后需要了的话再细看。

参考文章(18条消息) elasticsearch中minimum_should_match的一些理解_xiao_jun_0820的博客-CSDN博客_minimumnumbershouldmatch

must

必须达到 must 列举的所有条件

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "gender": "M"
          }
        }
      ]
    }
  }
}

should

should:应该达到 should 列举的条件,如果达到**会增加相关文档的评分,并不会改变查询的结果。**如果 query 中只有 should 且只有一种匹配规则,那么 should 的条件就会被作为默认匹配条件而去改变查询结果

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "gender": "M"
          }
        }
      ],
      "should": [
        {
          "match": {
            "address": "lane"
          }
        }
      ]
    }
  }
}
must_not

必须不是指定的情况

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "gender": "M"
          }
        }
      ],
      "should": [
        {
          "match": {
            "address": "lane"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "email": "baluba.com"
          }
        }
      ]
    }
  }
}
address 包含 mill,并且 gender 是 M,如果 address 里面有 lane 最好不过,但是 email 必须不包含 baluba.com

image-20220412103448606

filter

filter 对结果进行过滤,使用方法和must一样,但是没有相关性得分。

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "filter": {
        "range": {
          "balance": {
            "gte": "10000",
            "lte": "20000"
          }
        }
      }
    }
  }
}
# 这里先是查询所有匹配 address 包含 mill 的文档,
# 然后再根据 10000<=balance<=20000 进行过滤查询结果
GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "gender": "M"
          }
        }
      ],
      "should": [
        {
          "match": {
            "address": "lane"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "email": "baluba.com"
          }
        }
      ],
      "filter":{
        "range": {
          "age": {
            "gte": 18,
            "lte": 30
          }
        }
      }
    }
  }
}
Aggregations(执行聚合)

**聚合提供了从数据中分组和提取数据的能力。(mysql的聚合函数)**最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数。

在 Elasticsearch 中,您有执行搜索返回 hits(命中结果),并且同时返 回聚合结果,把一个响应中的所有 hits(命中结果)分隔开的能力。

这是非常强大且有效的, 您可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用 一次简洁和简化的 API 来避免网络往返。

1. address 中包含 mill 的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  },
  "aggs": {     //聚合:求年龄的分布情况 。  如年龄38的有多少人。
    "group_by_state": {   //聚合名称
      "terms": {          //分布情况,使用terms
        "field": "age"    //聚合字段
      }
    },
    "ageavg": {  //聚合:求年龄的平均值
      "avg": {          
        "field": "age"
      }
    }
  },
  "size":0  //不显示人的详情。
}

image-20220412145520539

2. 按照年龄聚合,并且请求这些年龄段的这些人的平均薪资
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age"
      },
      "aggs": {
        "balance_avg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

image-20220412150220933

3. 复杂:查出所有年龄分布,并且这些年龄段中 M 的平均薪资和 F 的平均薪资以及这个年龄 段的总体平均薪资

分析:

  1. 年龄分布:进行一次聚合
  2. 年龄段中的M和F:在年龄分布的基础上再进行一次聚合
  3. M和F的平均薪资,记载M和F的基础上再进行一次聚合
  4. 年龄段的总体信息:在年龄分布的基础上,进行一次聚合
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "genderAgg": {
          "terms": {
            "field": "gender.keyword",  //因为gender是文本类型。具体见下文报错。
            "size": 2
          },
          "aggs": {
            "balanceAvg": {
              "avg": {
                "field": "balance"
              }
            }
          }
        },
        "ageAggBalanceAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

image-20220412152002694

image-20220412152132213

语法参考文档

[Quick start | Elasticsearch Guide 8.1] | Elastic

image-20220412152415550

5. Mapping

介绍

Maping是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的。

比如:使用maping来定义:

  • 哪些字符串属性应该被看做全文本属性(full text fields);
  • 哪些属性包含数字,日期或地理位置;
  • 文档中的所有属性是否都嫩被索引(all 配置);
  • 日期的格式;
  • 自定义映射规则来执行动态添加属性;

查看mapping信息

image-20230118172721998

GET bank/_mapping
{
  "bank" : {
    "mappings" : {
      "properties" : {
        "account_number" : {
          "type" : "long"
        },
        "address" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "age" : {
          "type" : "long"
        },
        "balance" : {
          "type" : "long"
        },
        "city" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "email" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "employer" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "firstname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "gender" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "lastname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "state" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

属性类型

[Field datatypes | Elasticsearch Guide 7.5] | Elastic

映射操作

创建映射操作

1. 创建索引映射

创建索引并指定属性的映射规则(相当于新建表并指定字段和字段类型)

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

结果:

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "my_index"
}

2. 给已有映射增加字段

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/explicit-mapping.html#add-field-mapping

PUT /my_index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

# 这里的 "index": false,表明新增的字段不能被检索。默认是true
# https://www.elastic.co/guide/en/elasticsearch/reference/7.5/mapping-index.html
结果:
{
  "acknowledged" : true
}

3. 查看映射

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/explicit-mapping.html#view-mapping

GET /my_index/_mapping
# 查看某一个字段的映射
GET /my_index/_mapping/field/employee-id

结果:

{
  "my_index" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "employee-id" : {
          "type" : "keyword",
          "index" : false
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}
# index false 表示不能被索引找到

4. 更新映射

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/explicit-mapping.html#update-mapping

对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。

Changing an existing field could invalidate data that’s already indexed.

5. 数据迁移

迁移方式分为两种,一种是7和7之后去掉type的情况,一种是包含type 迁移的情况。

无type数据迁移
POST reindex [固定写法]
{
  "source":{
      "index":"twitter"
   },
  "dest":{
      "index":"new_twitters"
   }
}
有type数据迁移
POST reindex [固定写法]
{
  "source":{
      "index":"twitter",
      "twitter":"twitter"
   },
  "dest":{
      "index":"new_twitters"
   }
}

6. 数据迁移实例

对于我们的测试数据,是包含 type 的索引 bank。

现在我们创建新的索引 newbank 并修改一些字段的类型来演示当需要更新映射时的数据迁移操作。

当前bank的文档类型

{
  "bank" : {
    "mappings" : {
      "properties" : {
        "account_number" : {
          "type" : "long"
        },
        "address" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "age" : {
          "type" : "long"
        },
        "balance" : {
          "type" : "long"
        },
        "city" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "email" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "employer" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "firstname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "gender" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "lastname" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "state" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

创建新索引 newbank 并修改字段类型

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

结果:

#! Deprecation: [types removal] Specifying types in reindex requests is deprecated.
{
  "took" : 269,
  "timed_out" : false,
  "total" : 1000,
  "updated" : 0,
  "created" : 1000,
  "deleted" : 0,
  "batches" : 1,
  "version_conflicts" : 0,
  "noops" : 0,
  "retries" : {
    "bulk" : 0,
    "search" : 0
  },
  "throttled_millis" : 0,
  "requests_per_second" : -1.0,
  "throttled_until_millis" : 0,
  "failures" : [ ]
}

6. 分词

一个tokenizer(分词器)接收一个字符流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流。

例如:whitespace tokenizer遇到空白字符时分割文本。它会将文本“Quick brown fox!”分割为[Quick,brown,fox!]。

该tokenizer(分词器)还负责记录各个terms(词条)的顺序或position位置(用于phrase短语和word proximity词近邻查询),以及term(词条)所代表的原始word(单词)的start(起始)和end(结束)的character offsets(字符串偏移量)(用于高亮显示搜索的内容)。

elasticsearch提供了很多内置的分词器,可以用来构建custom analyzers(自定义分词器)。

POST _analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
结果:
{
  "tokens" : [
    {
      "token" : "the",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "2",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<NUM>",
      "position" : 1
    },
    {
      "token" : "quick",
      "start_offset" : 6,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

默认的分词器一般都是针对于英文,对于中文我们需要安装额外的分词器来进行分词。

没有中文分词器

GET _analyze
{
   "analyzer": "ik_max_word", 
    "text":"蔡徐坤"
}

image-20230119095034681

安装IK分词器

# 进入挂载的插件目录 /opt/docker/mid_elasticsearch_test/plugins
cd /opt/docker/mid_elasticsearch_test/plugins

# 安装 wget 下载工具
 yum install -y wget

# 下载对应版本的 IK 分词器(这里是7.4.2)
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2

# 进入容器内部
docker exec -it elasticsearch /bin/bash

# 查看 es 插件目录
ls /usr/share/elasticsearch/plugins

# 可以看到 elasticsearch-analysis-ik-7.4.2.zip

配置远程词库

这里对于默认词库中没有的词,不会有词语的组合,所以我们可以通过配置自定义词库或远程词库来实现对词库的扩展。

安装nginx

# 创建初始nginx容器,目的复制默认配置
docker run -p 80:80 --name nginx -d nginx:1.10
# 将容器呢欸文件拷贝到当前目录:
docker cp nginx:/etc/nginx .
# 修改文件名称nginx 为conf,并移动到/opt/docker/mid_nginx_test/nginx目录下
mv nginx conf
mv conf/ /opt/docker/mid_nginx_test
# 直接删除旧的容器
docker rm -f nginx
# 启动新的容器
docker run -p 80:80 --name nginx-test -v /opt/docker/mid_nginx_test/nginx/html:/usr/share/nginx/html -v /opt/docker/mid_nginx_test/nginx/logs:/var/log/nginx -v /opt/docker/mid_nginx_test/nginx/conf:/etc/nginx -d nginx:1.10

配置远程词库

进入:vi /opt/docker/mid_elasticsearch_test/plugins/ik/config/IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
 <entry key="remote_ext_dict">http://82.157.236.113/fenci.txt</entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

重启es:docker restart elasticsearch

测试分词效果:

GET _analyze
{
   "analyzer": "ik_max_word", 
    "text":"蔡徐坤"
}
结果:
{
  "tokens" : [
    {
      "token" : "蔡徐坤",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    }
  ]
}

问题汇总

1. 聚合搜索结果默认只有10条

解决方案:在聚合的terms中添加size字段。

GET bank/_search
{
  "size":0,  # 只返回聚合字段age的信息
  "aggs": {     
    "group_by_age": {  
      "terms": {         
        "field": "age",    
        "size":100
      }
    }
  }
}

因为es中数据量巨大,因此必须通过size限制,进而防止OOM。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

See you !

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值