小白学ES 12 - 什么是Elasticsearch的动态映射 + 如何自定义动态映射

1 动态映射(dynamic mapping)

1.1 什么是动态映射

没有提前定义字段和数据类型间的映射关系, 而是直接向ES中插入数据, 此时ES将根据每个field可能对应的数据类型, 自动为其配置type及其对应的mapping, 这个过程就是动态映射(dynamic mapping).

示例:

true or false		-->	boolean
233			-->	long
123.45			-->	double
2018-10-10		-->	date
"hello world"		-->	string/text

可以配置自定义映射, 供ES在动态映射时使用.

  • 动态映射虽然方便, 可并不直观, 为了个性化自定义相关设置, 可以在添加文档之前, 先创建index和type, 并配置type对应的mapping, 以取代动态映射.

1.2 体验动态映射

  • 插入如下数据:

    PUT website/blog/1
    {
        "post_date": "2018-01-01",
    	"title": "my first blog",
    	"content": "my first blog in the website",
    	"author_id": 5520
    }
    
    PUT website/blog/2
    {
        "post_date": "2018-01-02",
        "title": "my second blog",
        "content": "my second blog in the website",
        "author_id": 5520
    }
    
    PUT website/blog/3
    {
        "post_date": "2018-01-03",
        "title": "my third blog",
        "content": "my third blog in the website",
        "author_id": 5520
    }
    
  • 进行如下检索测试:

    GET website/blog/_search?q=2018				// 3条结果
    GET website/blog/_search?q=2018-01-01			// 3条结果
    GET website/blog/_search?q=post_date:2018-01-01		// 1条结果
    GET website/blog/_search?q=post_date:2018		// 1条结果
    
  • 查看ES自动建立的mapping:

    GET website/_mapping/blog
    
    // 结果如下: 
    {
      "website": {
        "mappings": {
          "blog": {
            "properties": {
              "author_id": {
                "type": "long"
              },
              "content": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "post_date": {
                "type": "date"			// post_date为date类型
              },
              "title": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          }
        }
      }
    }
    

1.3 搜索结果不一致的原因分析

上节中搜索结果不一致, 是因为ES自动建立mapping时, 为不同的field映射了不同的数据类型.

—— 不同数据类型在分词、搜索等行为上也是不同的.

(1) GET website/blog/_search?q=2018

ES默认将每个文档的所有field的值抽取到一个名为_all的元字段中, 如id=1的文档的_all的值为:

2018-01-01 my first blog my first blog in the website 5520
  • _all字段的倒排索引结果如下:

    说明: _all字段将所有的值作为字符串索引, 所以日期被索引为年、月、日三个值.

    doc1 doc2 Doc3
    2018 * * *
    01 * * *
    02 *
    03 *

此项搜索中, ES是从_all字段中检索, 3个文档中都有 2018 , 所以结果数是3.

(2) GET website/blog/?q=2018-01-01

同(1), ES也是从_all字段中检索, 结果数同样是3.

(3) GET website/blog/_search?q=post_date:2018-01-01

此检索指定了检索条件, ES将从post_date字段中检索, 而post_date被映射为date类型, 所以将进行精确匹配.

  • post_date字段的倒排索引结果如下:

    注意: date类型的字段索引的内容有其默认的固定格式, 如下:

    doc1 doc2 doc3
    2018-01-01 00:00:00 UTC *
    2018-01-02 00:00:00 UTC *
    2018-01-03 00:00:00 UTC *

可以看出, 满足条件的只有1个结果, 即doc1.

(4) GET /_search?q=post_date:2018

这是ES 5.2版本中做的一个优化, 搜索01/02等是不会出现结果的, 搜索2018会出现第一条结果.

2 开启dynamic mapping策略

2.1 约束策略

(1) true: 开启 —— 遇到陌生字段时, 进行动态映射;
(2) false: 关闭 —— 遇到陌生字段时, 忽略之;
(3) strict: 遇到陌生字段时, 报错处理.

2.2 策略示例

  • 使用不同的约束策略:

    PUT website
    {
      "mappings": {
          "user": {
              "dynamic": "strict",			// 严格控制策略
              "properties": {
                  "name": { "type": "text" },
                  "address": {
                      "type": "object",
                      "dynamic": "true"		// 开启动态映射策略
                  }
              }
          }
      }
    }
    
  • 插入数据演示:

    // 插入数据时多添加一个字段
    PUT website/user/1
    {
        "name": "shou feng",
        "content": "this is my blog",		// 多添加的字段
        "address": {
            "province": "guangdong",		// 多添加的字段
            "city": "guangzhou"			// 多添加的字段
        }
    }
    
    // 出错信息: 
    {
      "error": {
        "root_cause": [
          {
            "type": "strict_dynamic_mapping_exception",
            // 不允许动态添加field
            "reason": "mapping set to strict, dynamic introduction of [content] within [user] is not allowed"
          }
        ],
        "type": "strict_dynamic_mapping_exception",
        "reason": "mapping set to strict, dynamic introduction of [content] within [user] is not allowed"
      },
      "status": 400
    }
    
    // 添加符合的数据, 操作成功: 
    PUT website/user/1
    {
        "name": "shou feng",
        "address": {
            "province": "guangdong",
            "city": "guangzhou"
        }
    }
    
  • 查看映射信息:

    GET website/_mapping/user
    
    // 映射信息如下: 
    {
      "website": {
        "mappings": {
          "user": {
            "dynamic": "strict",			// 严格约束条件
            "properties": {
              "address": {
                "dynamic": "true",			// 开启动态映射策略
                "properties": {
                  "city": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  },
                  "province": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  }
                }
              },
              "name": {
                "type": "text"
              }
            }
          }
        }
      }
    }
    

3 定制dynamic mapping策略

3.1 date_detection - 日期识别策略

ES默认按照一定的格式识别date类型的数据, 如"yyyy-MM-dd". 存在这种情况:

① 第一次添加文档时, 某个field是类似"2018-01-01"的值, 其类型就被动态映射成了date;

② 后期再次添加文档, 该field是类似"hello world"的值, ES就会因为类型不匹配而报错.

为解决这一问题, 可以手动关闭某个type的date_detection, 如果有需要, 请指定某个field为date类型.

PUT website/_mapping/user
{
    "date_detection": false
}

3.2 自定义动态映射模板 - type层面

  • 在type中定义动态映射模板(dynamic mapping template):

    PUT website
    {
        "mappings": {
            "user": {
                "dynamic_templates": [
                    {
                        "en": {
                            "match": "*_en", 			// 匹配"*_en"的field
                            "match_mapping_type": "string",
                            "mapping": {
                                "type": "text",
                                "analyzer": "english"		// 使用english分词器
                            }
                        }
                    }
                ]
            }
        }
    }
    
  • 添加数据:

    PUT website/user/1
    {
        "name": "the first register user"
    }
    
    PUT website/user/2
    {
        "name_en": "the second register user"
    }
    
  • 检索数据:

    // 有结果: "name"没有匹配到任何动态模板, 默认使用standard分词器
    GET website/user/_search
    {
        "query": {
            "match": {"name": "the"}
        }
    }
    
    // 无结果: "name_en"匹配到了动态模板, 使用english分词器, the是停用词, 被过滤掉了
    GET website/user/_search
    {
        "query": {
            "match": {"name_en": "the"}	
        }
    }
    
  • 注意:

    这里的match_mapping_type的类型支持 [object, string, long, double, boolean, date, binary], 若使用text将抛出如下警告信息:

    Deprecation: match_mapping_type [text] is invalid and will be ignored: 
    No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]
    

3.3 自定义默认映射模板 - index级别

  • 在index中定义默认映射模板(default mapping template):

    PUT website
    {
        "mappings": {
            "_default_": {
                "_all": { "enabled":  false }
            },
            "blog": {
                "_all": { "enabled":  true  }
            }
        }
    }
    
  • 默认映射模板的使用:

    个人理解: 默认映射模板应该是类似于全局变量的存在, 对当前配置的索引起作用.

    具体使用: 尚未尝试, 后续接触到时再补充此部分内容.

版权声明

作者: ma_shoufeng(马瘦风)

出处: CSDN 马瘦风的博客

您的支持是对博主的极大鼓励, 感谢您的阅读.

本文版权归博主所有, 欢迎转载, 但未经博主同意必须保留此段声明, 且在文章页面明显位置给出原文链接, 否则博主保留追究相关人员法律责任的权利.

猜你喜欢

转载自blog.csdn.net/ma_shou_feng/article/details/84782624