白日梦的ES笔记三:万字长文 Elasticsearch基础概念统一扫盲(二)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 白日梦的ES笔记三:万字长文 Elasticsearch基础概念统一扫盲(二)

八、分词器#


ES官网分词器模块 https://www.elastic.co/guide/en/elasticsearch/reference/6.2/analysis.html


8.1、什么是分词器?#


我们使用分词器可以将一段话拆分成一个一个的单词,甚至可以进一步对分出来的单词进行词性的转换、时态的转换、单复数的转换的操作。

为什么使用分词器呢?


你想一个doc那么长,成千上万字。为了对它进行特征的提取,分析。就得把它还原成组成它的词条。这样会提高检索时的召回率,让更多的doc被检索到。


8.2、分词器的组成#


character filter:

在一段文本在分词前先进行预处理,比如过滤html标签, 将特殊符号转换成123..这种 阿拉伯数字等特殊符号的转换。


tokenizer:

进行分词、拆解句子、记录词条的位置(在当前doc中占第几个位置term position)及顺序。


token filter:

进行同义词的转换,去除同义词,单复数的转换等等。


ES内置的分词器:

  • standard analyzer(默认)
  • simple analyzer
  • whitespace
  • language analyzer(特定语言的分词器,English)


另外比较受欢迎的中文分词器为IK分词器,这个分词器的插件包、安装方式我都整理成文档了,公众号后台回复:es即可领取。


8.3、修改Index使用的分词器#


PUT /my_index
{
  "settings":{
    "analysis":{
      "analyzer":{
        "es_std":{
          # 指定分词器的类型是:standard
          "type":"standard",
          # 指定分词器的停用词:_english_
          "stopwords":"_english_"
        }
      }
    }
  }
}


九、mapping#


9.1、认识mapping#


看到这里你肯定知道了,我们想往ES中写数据是需要一个index的。其实我们在往ES中PUT数据之前是可以手动创建Mapping,这里的mapping其实好比你搞一个java类,做一次对数据结构的抽象,比如name 的类型是String,age的类型是Integer。

就好比下面这样:


PUT my_index
{   
  # 指定index的primary shard数量以及 replicas的数量
  “settings”:{
    "number_of_shards":1,
    "number_of_repicas":0
  },
   # 关键字,我们手动自定my_index中的mapping
  "mappings": {
    "my_index": { # index的名称
      "properties": { # 关键字,mapping的属性,字段
        "my_field1": { # 相当于Java中的   String my_field1
          "type": "text",
           "analyzer":"english"# 指定分词器,说明这个字段需要分词建立倒排索引
        },
        "my_field2": { # 相当于Golang中的 var my_field2 float
          "type": "float",
          # 指定是否要分词。analyzed表示要,not_analyzed表示不要
          "index":"not_analyzed"
        },
        "my_field3": {
          "type": "scaled_float",
          "scaling_factor": 100
        }
      }
    }
  }
}


1、mapping json中包含了诸如propertiesmatadata(_id,_source,_type)settings(analyzer)以及其他的settings。

2、我们把上面的json中的properties部分称为:root object

3、自己创建mapping一般是为了更好的控制各个字段的数据类型,包括使用到的分词器。

4、另外注意:field的mapping只能新增,不能修改。


你也可以在往ES中PUT数据之前不创建任何Mapping,ES会自动为我们生成mapping。就像下面这样,自动生成的mapping信息叫做dynamic mapping,下文中我们还会详细讲这个dynamic


PUT my_index/_doc/1
{
  "title": "This is a document"
}


9.2、查看mapping#


# 查看某个index下的某个type的mapping
GET /index/_mapping/type
# 查看某个index的mapping
GET /index/_mapping


9.3、dynamic mapping (动态mapping)#


就像下面这样,我们直接往ES中PUT数据,ES在为我们创建index时就会自动生成dynamic mapping。其实用大白话讲就是ES自动推断你往它里面存的json串的类型。比如下面的"first_name"会被dynamic mapping成string 类型的。


PUT my_index/_doc/1
{
  "first_name": "John"
}


ES使用_type来描述doc字段的类型,原来我们直接往ES中存储数据,并没有指定字段的类型,原因是ES存在动态类型推断(ES支持的类型上文中我们也一起看过了,如果不记得阔以再去看一下哈)。默认的mapping中定义了每个field对应的数据类型以及如何进行分词。


null          --> no field add
true flase    --> boolean
123           --> long
123.123       --> double
1999-11-11    --> date
"hello world" --> string
Object        --> object


9.4、定制dynamic mapping 策略#

  • ture: 语法陌生字段就进行dynamic mapping。
  • false: 遇到陌生字段就忽略。
  • strict: 遇到默认字段就报错。


示例


PUT /my_index/
{
    "mappings":{
        "dynamic":"strict"
    }
}


  • 禁用ES的日期探测的Demo


# 创建mapping并制定:禁用ES的日期探测
PUT my_index
{
  "mappings": {
    "_doc": {
      "date_detection": false
    }
  }
}
# 添加一条doc
PUT my_index/_doc/1 
{
  "create": "1985/12/22"
}
# 查看doc,结果如下
GET my_index/_doc/1 
{
  "_index": "my_index",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "create": "1985/12/22"
  }
}
# 查看mapping 
GET my_index/_mapping
# 结果如下:
{
  "my_index": {
    "mappings": {
      "_doc": {
        "date_detection": false,
        "properties": {
          "create": {
            # 被任务是text类型
            "type": "text",
            # ES会自动帮你创建的下面的field部分
            # 即 create是text类型,create.ketword是keyword类型
            # keyword类型不会分词,默认保留前256字符
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}


  • 定制日期发现规则


PUT my_index
{
  "mappings": {
    "_doc": {
      "dynamic_date_formats": ["MM/dd/yyyy"]
    }
  }
}
PUT my_index/_doc/1
{
  "create_date": "09/25/2015"
}


  • 定制数字类型的探测规则


PUT my_index
{
  "mappings": {
    "_doc": {
      "numeric_detection": true
    }
  }
}
PUT my_index/_doc/1
{
  "my_float":   "1.0",
  "my_integer": "1" 
}


定制type field#


ES中type相当于MySQL的数据表嘛,ES中可以给现存的type添加field。但是不能修改,否则就会报错。


type在高版本的ES7中被废弃了,Index的概念依然保留着。


# 创建index:twitter
PUT twitter
{
  "mappings": {
    # user为type
    "user": {
      "properties": {
        "name": {
         # 会被全部检索
        "type": "text" ,
         # 指定当前field使用 english分词器
        "analyzer":"english" 
        },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    "tweet": {
      "properties": {
        "content": { "type": "text" },
        "user_name": { "type": "keyword" },
        # "tweeted_at": { "type": "date" },
        "tweeted_at": {
        "type": "date" 
         # 通过index设置为当前field  tweeted_at不能被分词
         "index": "not_analyzeed" 
        }
      }
    }
  }
}


9.5、mapping复杂数据类型在底层的存储格式#


Object类型


# object类型的json
{
    "address":{
        "province":"shandong",
        "city":"qingdao"
    },
    "name":"bairimeng",
    "age":"12"
}
# ES会将上面的json转换成如下的格式存储
{
    "name" : [bairimeng],
    "age" : [12],
    "address.province" : [shandong]
    "address.city" : [qingdao]
}


Object数组类型


# Object数组类型
{
    "address":[
        {"age":"12","name":"张三"},
        {"age":"12","name":"张三"},
        {"age":"12","name":"张三"}
    ]
}
# ES会将上面的json转换成如下的格式存储
{
    "address.age" : [12,12,12],
    "address.name" : [张三,张三,张三]
}


9.6、ES7中废弃了type的概念#


在一开始我们将ElasticSearch的index比作MySQL中的database,将type比作table,其实这种类比是错误的。因为在MySQL中不同表之间的列在物理上是没有关系的,各自占有自己的空间。


但是在ES中不是这样,可能type=Student中的name列和type=Teacher中的name列会被lucene认为是同一个field。导致Lucene处理效率下降。


所以在ES7中直接就将type概念废弃了。


不过你也不用担心,大部分企业都倾向于使用低版本的ES,比好比你现在用的依然是java8 而不是JDK14。


9.7、认识一些mate-field(元数据字段)#


这里说的元数据字段指定的是,当你检索doc时,除了返回的doc本身的数据之外,其他的出现在检索结果中的数据,我们是需要了解这些字段都是什么含义的。如下:

_index , _type , _id , _source , _version



_id


它是document的唯一标识信息。上图中我手动指定了id等于1。如果不指定的话,ES会自动为我们生成一个长20个字符的id,ES会保证集群中的生成的doc id不会发生冲突。 有这种场景,比如你的数据是从MySQL这种数据库中倒入进ES的,那其实完全可以使用MySQL中的数据行的ID作为doc id。


_index


你可以简单粗暴的将es的index的地位理解成MYSQL中的数据库。这里的元数据_index被用来标识当前的doc存在于哪个index中。index的命名规范,名称小写,不能用下划线开头,不能包含逗号。


ES支持跨域index进行检索


详情见官网 https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-index-field.html


_type


这个字段用来标识doc的类型。但它其实是一个逻辑上的划分。


field中的value在顶层的lucene建立索引的时候,全部使用的opaque bytes类型,不区分类型的lucene是没有type概念的。


为了方便我们区分出不通doc的类型,于是在document中加了一个_type属性。

ES会通过_type进行type的过滤和筛选,一个index中是存放的多个type实际上是存放在一起的,因此一个index下,不可能存在多个重名的type。


_version `_version`是doc的版本号,可以用来做并发控制,当一个doc被创建时它的`_version`是1,之后对它的每一次修改,都会使这个版本号+1,哪怕是你将这个doc删除了,这个doc的版本号也会增加1。


_source


通过这个字段可以定制我们想要返回字段。比如说一个type = user类型的doc中存在100个字段,但是可能前端并不是真的需要这100个字段,于是我们使用_source去除一些字段,注意和filter是不一样的,因为filter不会影响相关性得分。


你可用像下面这样禁用_source


PUT tweets
{
  "mappings": {
    "_doc": {
      "_source": {
        "enabled": false
      }
    }
  }
}


_all


首先它也是一个元数据,当我们往ES中插入一条document时。ES会自动的将这个doc中的多个field的值串联成一个字符串,然后用这个作为_all字段的值并建立索引。当用户发起检索却没有指定从哪个字段查询时,默认就会在这个_all中进行匹配。


_field_names


举个例子说明这个属性怎么用:

首先往index=my_index的索引下灌两条数据


# Example documents
PUT my_index/_doc/1
{
  "title": "This is a document"
}
PUT my_index/_doc/2?refresh=true
{
  "title": "This is another document",
  "body": "This document has a body"
}


然后像下面这样使用_field_names检索,并且指定了字段=“title”。此时ES会将所有包含title字段,且title字段值不为空的doc检索出来。


GET my_index/_search
{
  "query": {
    "terms": {
      "_field_names": [ "title" ] 
    }
  }
}


禁用_field_names:


PUT tweets
{
  "mappings": {
    "_doc": {
      "_field_names": {
        "enabled": false
      }
    }
  }
}


_routing

下面路由导航中细说。


_uid

在ES6.0中被弃用。


9.8、copy_to#


在上一篇文章中跟大家介绍过可以像下面这样跨越多个字段搜索


# dis_max
GET /your_index/your_type/_search
{   
    # 基于 tie_breaker 优化dis_max
    # tie_breaker可以使dis_max考虑其它field的得分影响
    "query": { 
     # 直接取下面多个query中得分最高的query当成最终得分
     # 这也是best field策略
     "dis_max": { 
        "queries":[
           {"match":{"name":"关注"}},
           {"match":{"content":"白日梦"}}
        ],
        "tie_breaker":0.4
     }
    }
} 
# best_field
# 使用multi_match query简化写法如下:
GET /your_index/your_type/_search
{    
    "query": { 
       "multi_match":{
           "query":"关注 白日梦",
            # 指定检索的策略 best_fields(因为dis_max就是best field策略)
           "type":"best_fields",
            # content^2 表示增加权重,相当于:boost2
           "fields":["name","content^2"],
           "tie_breaker":0.4,
           "minimum_should_match":3
       }
    }
}
# most_field
GET /your_index/your_type/_search
{    
    # most_fields策略、优先返回命中更多关键词的doc
    # 如下从title、name、content中搜索包含“赐我白日梦”的doc
    "query": { 
       "multi_match":{
           "query":"赐我白日梦",
            # 指定检索的策略most_fields
           "type":"most_fields",
           "fields":["title","name","content"]
       }
    }
}


针对跨越多个字段的检索除了上面的most_field和best_field之外,还可以使用copy_to预处理。


这个copy_to实际上是在允许我们自定义一个_all字段, ES会将多个字段的值复制到一个_all中,然后再次检索时目标字段就使用我们通过copy_to创建出来的_all新字段中。

示例:


PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "first_name": {
          "type": "text",
          # 把当前的first_name copy进full_name字段中
          "copy_to": "full_name" 
        },
        "last_name": {
          "type": "text",
          # 把当前的last_name copy进full_name字段中
          "copy_to": "full_name" 
        },
        "full_name": {
          "type": "text"
        }
      }
    }
  }
}
PUT my_index/_doc/1
{
  "first_name": "John",
  "last_name": "Smith"
}
GET my_index/_search
{
  "query": {
    "match": {
      "full_name": { 
        "query": "John Smith",
        "operator": "and"
      }
    }
  }
}


9.9、Arrays 和 Multi-field#


更多内容参见官网 https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-types.html


相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
2月前
|
存储 分布式计算 大数据
大数据-169 Elasticsearch 索引使用 与 架构概念 增删改查
大数据-169 Elasticsearch 索引使用 与 架构概念 增删改查
62 3
|
3月前
|
数据可视化 Java Windows
Elasticsearch入门-环境安装ES和Kibana以及ES-Head可视化插件和浏览器插件es-client
本文介绍了如何在Windows环境下安装Elasticsearch(ES)、Elasticsearch Head可视化插件和Kibana,以及如何配置ES的跨域问题,确保Kibana能够连接到ES集群,并提供了安装过程中可能遇到的问题及其解决方案。
Elasticsearch入门-环境安装ES和Kibana以及ES-Head可视化插件和浏览器插件es-client
|
1月前
|
测试技术 API 开发工具
ElasticSearch核心概念:倒排索引
ElasticSearch核心概念:倒排索引
52 6
|
2月前
|
存储 JSON Java
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
这篇文章是关于Elasticsearch的学习指南,包括了解Elasticsearch、版本对应、安装运行Elasticsearch和Kibana、安装head插件和elasticsearch-ik分词器的步骤。
147 0
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
|
2月前
|
JSON 关系型数据库 API
ElasticSearch 的概念解析与使用方式(二)
ElasticSearch 的概念解析与使用方式(二)
25 1
|
2月前
|
存储 搜索推荐 Java
ElasticSearch 的概念解析与使用方式(一)
ElasticSearch 的概念解析与使用方式(一)
64 1
|
2月前
|
自然语言处理 搜索推荐 Java
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(一)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图
52 0
|
2月前
|
存储 自然语言处理 搜索推荐
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
35 0
|
3月前
|
JSON 自然语言处理 数据库
ElasticSearch基础1——索引和文档。Kibana,RestClient操作索引和文档+黑马旅游ES库导入
概念、ik分词器、倒排索引、索引和文档的增删改查、RestClient对索引和文档的增删改查
ElasticSearch基础1——索引和文档。Kibana,RestClient操作索引和文档+黑马旅游ES库导入
|
4月前
|
自然语言处理 Java 索引
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
43 0