Elasticsearch学习笔记(11) - Mapping及自定义Analyzer

Elasticsearch的Mapping,定义了索引的结构,类似于关系型数据库的Schema。Elasticsearch的Setting主要是为了定义搜索的最关键组件,即:Analyzer,也就是分析器。

Dynamic Mapping及常用字段类型

Mapping的定义

Mapping类似于关系型数据库的Schema,主要包含以下内容:

  1. 定义索引中字段的名称
  2. 定义字段的数据类型,如:字符串、数字、boolean等
  3. 可对字段设置倒排索引的相关配置,如是否需要分词,使用什么分词器

从7.x开始,一个Mapping只属于一个索引的type

  1. 每个文档属于一个type
  2. 一个type有且仅有一个Mapping定义
  3. 从7.x开始,不需要在Mapping中指定type信息,默认type为_doc

常用字段类型

在Elasticsearch中,字段数据类型有以下常用的类型:

  1. 简单类型
    • Text / Keyword - 文本 / 关键字
    • Date - 日期
    • Integer / Float - 数字 / 浮点
    • Boolean - 布尔值
    • IPv4 / IPv6 - ip地址
  2. 复杂类型,包括对象和数组
    • 对象
    • 数组
  3. 特殊类型,如地理信息
    • geo_point / ...

动态Mapping

动态Mapping,英文名为Dynamic Mapping。

  1. 在写入文档时,如果索引不存在,会自动创建索引
  2. 这种机制,使得我们无需手动定义mappings。Elasticsearch会自动根据文档信息,推算出字段的类型
  3. 有的时候,Elasticsearch可能会推算不对,如:地理位置信息
  4. 当类型推算得不对时,可能导致一些功能无法正常运行,如Range查询。

常用类型的自动识别规则

类型 规则
字符串 匹配到日期格式,设置成Date。
字符串为数字时,当成字符串处理,但我们设置转换为数字。
其他情况,类型就是Text,并且会增加keyword的子字段
布尔值 Boolean
浮点数 Float
整数 Long
对象 Object
数组 由第一个非空数值的类型决定
空值 忽略
# 写入文档,查看 Mapping
PUT mapping_test/_doc/1
{
  "firstName": "Chan", -- Text
  "lastName":  "Jackie", -- Text
  "loginDate": "2018-07-24T10:29:48.103Z" -- Date
}

# Dynamic Mapping,推断字段的类型
PUT mapping_test/_doc/1
{
    "uid": "123", -- Text
    "isVip": false, -- Boolean
    "isAdmin": "true", -- Text
    "age": 19, -- Long
    "heigh": 180 -- Long
}

# 查看 Dynamic Mapping
GET mapping_test/_mapping

字段类型是否可修改

  1. 新增加的字段
    • dynamic设为true时,新增字段的文档写入时,Mapping同时被更新
    • dynamic设为false时,Mapping不会被更新,新增字段的数据无法被索引,但是会出现在_source中
    • dynamic设为strict,文档将写入失败
  2. 已存在的字段,一旦数据被写入,就不再支持修改字段定义
    • Lucene本身的限制
  3. 如果希望更改字段类型,必须Reindex api,即:重建索引。在数据量多的时候,开销将非常大
# dynamic设置为false
PUT idx1
{
    "mapping": {
        "_doc": {
            "dynamic": "false"
        }
    }
}

# 修改为dynamic为false
PUT idx1/_mapping
{
  "dynamic": false
}

# 查看索引
GET idx1/_mapping

dynamic属性和索引字段可变性的规则,我们可以总结如下:

\ true false strict
文档可索引 yes yes no
字段可索引 yes no no
Mapping被更新 yes no no

显式Mapping及常见参数

在本文的上一段落,我们的Mapping都是自动生成的。自动生成机制虽然方便,但是也可能导致一些问题。比如:生成的字段类型不正确,字段的附加属性不满足我们的需求,等等。这时,我们可以通过显式Mapping的方式来解决。

那么,我们如何进行显式Mapping的设置呢?

  1. 参考官网api,纯手写
  2. 为减少工作量,减少出错概率,可如下进行:
    1. 创建一个临时index,写入一些样本数据
    2. 通过访问Mapping API获取该临时文件的动态Mapping定义
    3. 修改后,再使用此配置创建自己的索引
    4. 删除临时索引

我们推荐使用第二种方式,效率高,且不容易出错。

常见参数 - index

index,可用于设置字段是否被索引,默认为true,false即为不可搜索。在下述例子中,mobile字段将不能被搜索到。

# 设置 index 为 false
DELETE users
PUT users
{
    "mappings" : {
      "properties" : {
        "firstName" : {
          "type" : "text"
        },
        "lastName" : {
          "type" : "text"
        },
        "mobile" : {
          "type" : "text",
          "index": false
        }
      }
    }
}

常见参数 - index_options

记录索引级别。Text类型默认为positions,其他类型默认为docs。我们需要记住一条准则。

记录的内容越多,占用的存储空间就越大。

索引级别有以下几种,更细节的内容可参考【官网索引级别】

  1. docs
  2. freqs
  3. positions
  4. offsets

常见参数 - null_value

需要对Null值实现搜索时使用。只有keyword类型才支持设定null_value

# 设定Null_value
DELETE users
PUT users
{
    "mappings" : {
      "properties" : {
        "firstName" : {
          "type" : "text"
        },
        "lastName" : {
          "type" : "text"
        },
        "mobile" : {
          "type" : "keyword",
          "null_value": "NULL"
        }
      }
    }
}

PUT users/_doc/1
{
  "firstName":"Zhang",
  "lastName": "Fubing",
  "mobile": null
}

PUT users/_doc/2
{
  "firstName":"Zhang",
  "lastName": "Fubing2"
}

# 查看结果,有且仅有_id为2的记录
GET users/_search
{
  "query": {
    "match": {
      "mobile":"NULL"
    }
  }
}

常见参数 - copy_to

这个属性用于将当前字段拷贝到指定字段。

  1. _all在7.x版本已经被copy_to所代替
  2. 可用于满足特定场景
  3. copy_to将字段数值拷贝到目标字段,实现类似_all的作用
  4. copy_to的目标字段不出现在_source中
# 设置 Copy to
DELETE users
PUT users
{
  "mappings": {
    "properties": {
      "firstName":{
        "type": "text",
        "copy_to": "fullName"
      },
      "lastName":{
        "type": "text",
        "copy_to": "fullName"
      }
    }
  }
}
PUT users/_doc/1
{
  "firstName":"Zhang",
  "lastName": "Fubing"
}

GET users/_search?q=fullName:(Zhang Fubing)

特殊的数组类型

Elasticsearch不提供专门的数组类型。但任何字段,都可以包含多个相同类型的数值。

# 数组类型
PUT users/_doc/1
{
  "name":"onebird",
  "interests":"reading"
}

PUT users/_doc/1
{
  "name":"twobirds",
  "interests":["reading","music"]
}

POST users/_search
{
  "query": {
        "match_all": {}
    }
}

# interests字段还是text类型
GET users/_mapping

多字段类型及自定义Analyzer

多字段类型

所谓多字段类型,即:一个字段可以有多个子字段。这种特性带来了以下好处。

  1. 增加一个keyword子字段,可用于精确匹配
  2. 可对子字段设置不同的analyzer
    1. 不通语言的支持
    2. 可对中文拼音字段进行搜索
    3. 可对搜索和索引指定不同的Analyzer

精确值和全文本

精确值(Exact Values) vs 全文本(Full Text)

  1. 精确值,包括数字、日期、具体的字符串(如“192.168.0.1”)
    • Elasticsearch中类型为keyword,索引时,不需要做特殊的分词处理
  2. 全文本,非结构化的文本数据
    • Elasticsearch中类型为text,索引时,需要对其进行分词处理

如下结构的数据,我们可以大致判断出哪些是精确值,哪些是全文本。其中的200、info、debug都是精确值。而message的内容为全文本。

{
    "code": 200,
    "message": "this is a error item, you can change your apollo config !",
    "content": {
        "tags": [
            "info",
            "debug"
        ]
    }
}

自定义分词器

自定义分词器,可通过组合不同的Character FilterTokenizerToken Filter来实现。

Character Filter,常用的字符过滤器包括:

类型 作用
html strip 去除html
mapping 字符串替换
pattern replace 正则匹配替换

Tokenizer,用于将原始文本按照一定规则切分为词(Term或Token)。我们除了使用Elasticsearch自动的分词器外,还可以自己通过开发插件的方式来实现。常用的分词器包括:

  • whitespace
  • standard
  • uax_url_email
  • pattern
  • keyword
  • path
  • hierarchy

Token Filter,分词过滤器。主要用于对输出的单词,进行增删改。常用的分词过滤器包括:

类型 作用
lowercase 转换为小写
stop 去掉the、a、an等单词
synonym 转换为近义词

在下面的例子中,我们实现了一个自定义分词器。

# 编写自定义分析器
PUT index1
{
    "settings": {
        "analysis": {
            "analyzer": {
                "my_custom_analyzer": {
                    "type": "custom",
                    "char_filter": ["emoticons"],
                    "tokenizer": "punctuation",
                    "filter": [
                        "lowercase",
                        "english_stop"
                    ]
                }
            },
            "tokenizer": {
                "punctuation": {
                    "type":"pattern",
                    "pattern": "[ .,!?]"
                }
            },
            "char_filter": {
                "emoticons": {
                    "type": "mapping",
                    "mappings": [
                        ":) => _happy_",
                        ":( => _sad_"
                    ]
                }
            },
            "filter": {
                "english_stop": {
                    "type": "stop",
                    "stopwords": "_english_"
                }
            }
        }
    }
}

# 查看自定义分析器的效果
POST index1/_analyze
{
    "analyzer": "my_custom_analyzer",
    "text": "I'm a :) person, and you?"
}

总结

通过这篇文章,我们了解了Mapping的作用及常用字段类型,也知道了动态Mapping和显式Mapping的区别。另外,我们还了解了Mapping的多字段特性,以及如何自定义一个Analyzer。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341