[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对象,因为es也不知道你要获取的这个字段到底是string还是数组,所以需要自己在写代码的时候进行选择。

你可能感兴趣的:([16]ES mapping 的设置规则)