[16]ES mapping 的设置规则

本文集主要是总结自己在项目中使用ES 的经验教训,包括各种实战和调优。

创建mapping的时候,_all设置为false。主要是从性能方面考虑。

包括mapping创建方式、mapping相关知识等。官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html#mapping-type


新增字段

curl -XPOST "http://127.0.0.1:9200/productindex/product/_mapping?pretty" -d '{ "product": { "properties": { "onSale":{ "type":"keyword" } } } }'

创建索引的同时创建setting和mapping:

curl -XPUT "192.168.1.101:9200/index_test" -d ' # 注意这里的'号
{
  "settings": {
    "index": {
      "number_of_replicas": "1", # 设置复制数
      "number_of_shards": "5" # 设置主分片数
    }
  },
  "mappings": { # 创建mapping
    "test_type": { # 在index中创建一个新的type(相当于table)
      "properties": {
        "name": { # 创建一个字段(string类型数据,使用普通索引)
          "type": "string",
          "index": "not_analyzed"
        },
        "age": {
          "type": "integer"
        }
      }
    }
  }
}'


ES还可以使用json的方式来创建mapping:

  • 创建一个扩展名为test_type.json的文件名,其中type_test就是mapping所对应的type名。
  • 在test_type.json中输入mapping信息。假设你的mapping如下:
{
  "test_type": { # 注意,这里的test_type与json文件名必须一致
      "properties": {
        "name": {
          "type": "string",
          "index": "not_analyzed"
        },
        "age": {
          "type": "integer"
        }
      }
    }
  }
  • 在$ES_HOME/config/路径下创建mappings/index_test子目录,这里的index_test目录名必须与我们要建立的索引名一致。将test_type.json文件拷贝到index_tes目录下。
  • 步骤4 创建index_test索引。操作如下:

curl -XPUT "192.168.1.101:9200/index_test" # 注意,这里的索引名必须与mappings下新建的index_test目录名一致

这样我们就创建了一个新的索引,并且使用了test_type.json所定义的mapping作为索引的mapping。

参考链接:具体方法参考http://blog.csdn.net/xialei199023/article/details/48085125


mapping中一些常用的属性:

                  "activity_type": {
                        "type": "string",//string在新版本已经废弃
                        "index": "not_analyzed"
                    },
                    "address": {
                        "type": "string",
                        "analyzer": "ik_smart"
                    },
                    "last_update_time": {
                        "type": "date",
                        "format": "yyyy-MM-dd HH:mm:ss"
                    }

  • Fields with the same name in different mapping types in the same index must have the same mapping.(同一索引下,不同类型的相同字段必须定义成相同的mapping),但是每个字段的以下属性可以用不同的设置,如copy to、dynamic、enabled、ignore_above、include_in_all、properties。

具体可参考https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html#_field_datatypes

Field支持的数据类型:

  • 基本类型:text、keyword、date、long、double、boolean、ip
  • range datatypes:integer_range, float_range, long_range, double_range, date_range (可以直接存区间数据,目前项目中尚未应用,主要应用场景尚不明确,合同管理的日期似乎可以应用)
  • 支持json分层属性,复杂的数据类型,如array、object、nested
  • 特殊类型:地理点、地理形状、completion

防止mapping激增的一些常用设置:

  • index.mapping.total_fields.limit The maximum number of fields in an index. The default value is 1000.
  • index.mapping.depth.limit 一个字段的最大深度,默认是20
  • index.mapping.nested_fields.limit 嵌套字段的最大数目,默认50

动态mapping

  • 不允许更新已经设置好的mapping
  • multi-fields是可以使用多种分词器,如french analyzer,目前我们使用的是standard analyzer、english analyzer、id_smart analyzer。

自动映射:

  • 动态映射:可以设置为true(默认值。动态添加字段)、false(忽略新字段)、strict(如果碰到陌生字段,抛出异常)
  • 默认映射:通常,一个索引中的所有类型具有共享的字段和设置。用 default 映射来指定公用设置会更加方便,而不是每次创建新的类型时重复操作。_default 映射像新类型的模板。所有在 default 映射 之后 的类型将包含所有的默认设置,除非在自己的类型映射中明确覆盖这些配置。

例如,我们可以使用 default 映射对所有类型禁用 _all 字段,而只在 blog 字段上开启它:

PUT /my_index
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "blog": {
            "_all": { "enabled":  true  }
        }
    }
}

default 映射也是定义索引级别的动态模板的好地方。

  • 动态模板:通过dynamic_templates,你可以拥有对新字段的动态映射规则拥有完全的控制。你设置可以根据字段名称或者类型来使用一个不同的映射规则。

每个模板都有一个名字,可以用来描述这个模板做了什么。同时它有一个mapping用来指定具体的映射信息,和至少一个参数(比如match)用来规定对于什么字段需要使用该模板。

模板的匹配是有顺序的 - 第一个匹配的模板会被使用。比如我们可以为string字段指定两个模板:

  • es:以_es结尾的字段名应该使用spanish解析器
  • en:其它所有字段使用english解析器

一个动态模板的示例:

PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic_templates": [
                { "es": {
                      "match":              "*_es", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "spanish"
                      }
                }},
                { "en": {
                      "match":              "*", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "english"
                      }
                }}
            ]
}}}

另外一个典型的mapping对象:

{
    "mappings": {
        "my_type": {
        //true:表示自动识别新字段并创建索引,false:不自动索引新字段,strict:遇到未知字段,抛异常,不能存入
            "dynamic":      "strict", 

              //动态模板
             "dynamic_templates": [
                    { "stash_template": {
                      "path_match":  "stash.*",
                      "mapping": {
                        "type":           "string",
                        "index":       "not_analyzed"
                      }
                    }}
                  ],
            //属性列表
            "properties": {
                //一个string类型的字段
                "title":  { "type": "string"},

                "stash":  {
                    "type":     "object",
                    "dynamic":  true //这里设置为true主要针对的是动态模板,即新增的field要是动态模板匹配不上,就会抛异常,否则就是按照模板来创建。
                }
            }
        }
    }
}

注意点:Elasticsearch映射虽然有idnex和type两层关系,但是实际索引时是以index为基础的。 如果同一个index下不同type的字段出现mapping不一致的情况 虽然数据依然可以成功写入并生成并生成各自的mapping, 但实际上fielddata中的索引结果却依然是以index内第一个mapping类型来生成的。


一次定义一个index下的两个type mapping:

糖果库mapping设置:

curl -XPUT 'http://localhost:9200/moparticle3' -d '

{

"settings": {

"number_of_shards": 6,

"number_of_replicas": 1

},

"mappings": {

"article": {

"_all" : {

"enabled" : false

},

"_routing" : {

"required" : true

},

"properties" : {

"docid" : {

"type" : "keyword"

},

"boardid" : {

"type" : "keyword"

},

"postid" : {

"type" : "keyword"

},

"topicid" : {

"type" : "keyword"

},

"del" : {

"type" : "integer"

},

"title" : {

"type" : "text",

"analyzer" : "ik_smart",

"search_analyzer": "ik_smart",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

},

"en" : {

"type": "text",

"analyzer": "english"

}

}

},

"digest" : {

"type" : "text",

"analyzer" : "ik_smart",

"search_analyzer": "ik_smart",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

},

"en" : {

"type": "text",

"analyzer": "english"

}

}

},

"source" : {

"type" : "keyword"

},

"ptime" : {

"type" : "date",

"format": "yyyy-MM-dd HH:mm:ss"

}

}

}

}

}

'

article表mapping设置:

curl -XPOST http://localhost:9200/subscribe/article/_mapping -d '{

"article" : {

"_all" : {

"enabled" : false

},

"_routing" : {

"required" : true

},

"properties" : {

"id" : {

"type" : "text",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

}

}

},

"original" : {

"type" : "long"

},

"postState" : {

"type" : "long"

},

"publishTime" : {

"type" : "date",

"format": "yyyy-MM-dd HH:mm:ss"

},

"source" : {

"type" : "long"

},

"title" : {

"type" : "text",

"analyzer" : "ik_smart",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

},

"en" : {

"type": "text",

"analyzer": "english"

}

}

},

"tname" : {

"type" : "text",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

}

}

},

"topState" : {

"type" : "long"

},

"type" : {

"type" : "long"

},

"updateTime" : {

"type" : "date",

"format": "yyyy-MM-dd HH:mm:ss"

},

"userClassify" : {

"type" : "text",

"fields" : {

"keyword" : {

"type" : "keyword",

"ignore_above" : 256

}

}

},

"wemediaId" : {

"type" : "keyword"

}

}

}

}'

一些不常用的datatype 整理

Array datatype:四种定义方式

  • an array of strings: [ "one", "two" ]
  • an array of integers: [ 1, 2 ]
  • an array of arrays: [ 1, [ 2, 3 ]] which is the equivalent of [ 1, 2, 3 ]
  • an array of objects: [ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]

采用如下方式自动创建mapping:

PUT my_index/my_type/2
{
"tags": [ "哈哈哈","中国社会主义人民" ]
}

这时的tags是按照text的形式来定义的,但其实是个数组。会对我们数组中的每个元素进行分词,我们在搜索的时候,相当于对整个数组的数据进行分词匹配。同时还可以使用must方法来匹配一个数组的多个。比如匹配数组中既有1又有2的数据。


Binary datatype:

Binary类型的field是不能被搜索的。同时会以列的形式存到硬盘上,后续可以用来排序、聚合等,但是默认不会被store,即不会从_source字段中剥离出来,需要自己手动配置。


Range datatypes:
官网链接:https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html

具体可以参考文档,暂不做总结,觉得可能实用不是很大。


项目在实际使用中,为了提升性能,降低带宽,在es查询时,只返回部分字段。具体实现方式如下:

官网链接:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-store.html

官网说明:If you only want to retrieve the value of a single field or of a few fields, instead of the whole_source, then this can be achieved with source filtering.

主要作用:不需要返回_source数据,减少返回数据的大小

es在定义mapping时可以指定某一个字段是都设置store参数,

PUT my_index
{
 "mappings": {
   "my_type": {
     "properties": {
       "title": {
         "type": "text",
         "store": true 
       },
       "date": {
         "type": "date",
         "store": true 
       },
       "content": {
         "type": "text"
       }
     }
   }
 }
}

注:Stored fields returned as arrays

For consistency, stored fields are always returned as an array because there is no way of knowing if the original field value was a single value, multiple values, or an empty array.

Another situation where it can make sense to make a field stored is for those that don’t appear in the _source field (such as copy_to fields).

Java API的使用:

首先需要mapping定义store属性。

在SearchRequestBuilder后面可以添加storedFields(String...)或者addStoredField(String)方法。添加后,response.getHits().getHits()的hit对象的source就为空了,只能获取我们storedFields方法添加的字段。

方法如下:
hit.field("title").getValue()
hit.getFields().get("title").getValue()
hit.getFields().get("title").getValues()

同时还可以通过getValues方法或者一个List<Object>对象,因为es也不知道你要获取的这个字段到底是string还是数组,所以需要自己在写代码的时候进行选择。

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

推荐阅读更多精彩内容