zoukankan      html  css  js  c++  java
  • Elasticsearch系列---定制mapping

    概要

    本篇接着前一篇内容,继续介绍mapping信息,重点倾向于自定义mapping、自定义对象以及数组集合类的底层结构。

    自定义mapping

    上一篇文章介绍的都是Elasticsearch的自动mapping,我们在创建索引时,可以先指定好mapping的信息,还是以music索引为例:

    PUT /music
    {
      "mappings": {
        "children": {
          "properties": {
            "content": {
              "type": "text",
              "fields": {
                "keyword": {
                  "type": "keyword",
                  "ignore_above": 256
                }
              }
            },
            "language": {
              "type": "keyword"
            },
            "length": {
              "type": "long"
            },
            "likes": {
              "type": "long"
            },
            "name": {
              "type": "text",
              "fields": {
                "keyword": {
                  "type": "keyword",
                  "ignore_above": 256
                }
              }
            }
          }
        }
      }
    }
    

    共包含name,content,language,length,likes 5个field,这里我们把language field的type指定为keyword,这个field无需分词,精确匹配即可。

    修改mapping

    我们可以在创建索引时指定mapping信息,也可以为索引增加新的field时指定mapping信息,但是已经存在的field,我们不能修改其mapping,否则会报错,例如:
    我们为music索引增加一个author field,并指定其type为text,analyzer、search_analyzer均为english

    PUT /music/_mapping/children
    {
      "properties" : {
        "author" : {
          "type" : "text",
          "index": true, 
          "analyzer": "english",
          "search_analyzer": "english"
        }
      }
    }
    

    如果尝试修改一下已经存在的field,如name字段,则会提示报错:
    请求:

    PUT /music/_mapping/children
    {
      "properties" : {
        "name" : {
          "type" : "text",
          "index": true, 
          "analyzer": "english",
          "search_analyzer": "english"
        }
      }
    }
    

    报错信息:

    {
      "error": {
        "root_cause": [
          {
            "type": "illegal_argument_exception",
            "reason": "Mapper for [name] conflicts with existing mapping in other types:
    [mapper [name] has different [analyzer]]"
          }
        ],
        "type": "illegal_argument_exception",
        "reason": "Mapper for [name] conflicts with existing mapping in other types:
    [mapper [name] has different [analyzer]]"
      },
      "status": 400
    }
    

    复杂数据类型底层结构

    前面提及的mapping信息,都是针对基础数据类型的,我们知道Elasticsearch是面向对象的分布式存储文档系统,肯定会遇到各种各样的自定义对象、集合数组、嵌套对象等数据结构,ES是如何支持、处理这些复杂对象类型的呢?

    1. 数组、集合类

    集合在JSON串中与数组等同,均可以认为是数组类型,在Elasticsearch中对数组没有特殊的映射需求,建立索引时与text是一样的,也是分词处理后得到多个词条,建立倒排索引。

    有一点要注意的是数组内的元素,数据类型要一致,比如都是字符器,都是日期,或都是数值,不能混搭,自动mapping是以第1位元素来决定type的,如果混搭,数组内与第1位元素类型不一致的元素,索引时会报错。

    1. Null类型
      底层的Lucene不能存储null值,null类型的不会被索引:
    "null_value":               null,
    "empty_array":              [],
    "array_with_null_value":    [ null ]
    
    1. 复杂对象
      复杂对象一般有一层或多层嵌套,即对象中的属性,类型也是对象,有时候还混搭着数组一类的,举个例子:
    {
      "address": {
        "country": "CN",
        "province": "GD",
        "city": "SZ"
      },
      "name": "Herry",
      "age": 28,
      "birth_date": "1992-04-29"
    }
    

    人员信息除了姓名年龄外,还有地址信息address,而address属性本身也是个对象,里面包含了country、province、city三个属性,查看它的mapping信息,也是呈嵌套结构(内容有删除,只保留体现层级的属性):

    {
      "person": {
        "mappings": {
          "info": {
            "properties": {
              "address": {
                "properties": {
                  "city": {
                    "type": "text"
                  },
                  "country": {
                    "type": "text"
                  },
                  "province": {
                    "type": "text"
                  }
                }
              }
            }
          }
        }
      }
    }
    

    Elasticsearch在存储层级结构的数据对象时,会做一些扁平化处理,Luence文档是一组KV结构的列表,把上述的数据存储成这样:

    {
      "address.country": "CN",
      "address.province": "GD",
      "address.city": "SZ",
      "name": "Herry",
      "age": 28,
      "birth_date": "1992-04-29"
    }
    

    所以最终想要根据address下的country作为条件进行查询,应该这么写:

    GET /person/info/_search
    {
      "query": {
        "match": {
          "address.country": "CN"
        }
      }
    }
    
    1. 数组内的复杂对象
      数组内的元素如果是基础数据类型还好说,只要求数组内所有元素类型一致即可,如果里面的元素是一个对象,又是如何索引的呢?
      假如一个数组结构是这样的:
    {
        "likes": [
            { "name": "Three Zhang", "datetime": "2019-12-01 08:58:12"},
            { "name": "Four Lee", "datetime": "2019-12-01 09:12:23"},
            { "name": "Five Wang", "datetime": "2019-12-01 09:15:58"}
        ]
    }
    

    经过扁平化处理(由列变成行),得到的数据将会是这样:

    {
    	"likes.name": ["Three Zhang","Four Lee","Five Wang"],
    	"likes.datetime": ["2019-12-01 08:58:12","2019-12-01 09:12:23","2019-12-01 09:15:58"],
    }
    

    发现什么问题没?这样处理完了之后,原本对象之间的关联性就没有了,数组内只是一堆无序的元素,如果要查询"Three Zhang在2019-12-01 09:00:00之后有没有给我点过赞"这样的组合条件查询,是会出现一条查询记录的,这跟预期的结果不同,显然是错了。

    留意一下这个问题,数组内的元素是对象类型时,需要声明为nested类型,才能得到预期的查询效果,nested类型在后续会有介绍。

    小结

    本篇主要对mapping的内容进行一些补充,并简单描述了各种复杂对象的底层存储结构,需要注意的是数组内的对象处理,需要声明为nested才能得到正确的结果。

    专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
    Java架构社区

  • 相关阅读:
    done apple两轮白人教你ci/cd
    done 没看完但是别处不考 沃尔玛sv大数据组的友好小哥
    done hitachi network四轮但是中规中矩
    23. Merge k Sorted Lists 合并k个排序链表
    SpringBoot+Spring常用注解总结
    MyBatisPlus 之 入门HelloWorld
    MyBatisPlus 之 集成MP
    MyBatisPlus 之 MyBatis 简介
    09月24日总结
    09月22日总结
  • 原文地址:https://www.cnblogs.com/huangying2124/p/12105382.html
Copyright © 2011-2022 走看看