Mapping parameters 映射的参数

这一部分和Field datatypes是相辅相成的,主要就是用来各个数据类型的mapping定义过程。
很多参数只对一些数据类型是适用的。
这一章感觉还是会很长。

analyzer

这个算是剩下的参数里面比较常见的了。
analyzer的使用流程一般是你可以先定义analyzer的分词方法,停用词等等--目的是决定text字段如何被转换成token。比如The quick Brown Foxes.这个text字段,可能会被分成quick, brown, fox(跟analyzer设置有关),这三个词会新成为一个字段被索引。这使得在很长的text用几个独立的token去查询变得效率很高。
另一方面在查询的时候也应该设置analyzer,保证查询的内容会用同样的analyzer分析去查询。

ES内置的analyzer

ES已经内置了十种左右的预定义的analyzer,包括Standard Analyzer、Simple Analyzer、Whitespace Analyzer、Stop Analyzer、Keyword Analyzer、Pattern Analyzer、Language Analyzers、Fingerprint Analyzer、Custom Analyzer。
你可以用_analyze来比较他们不同,只需要修改analyzer(注意都是小写字母):

POST _analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

自己构建analyzer

当然也可以自己构建analyzer,下面的例子构建了一个std_english的analyzer,mapping里写了要用my_text.english这个组合字段才会是std_english。

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "std_english": { 
          "type":      "standard",
          "stopwords": "_english_"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "my_text": {
          "type":     "text",
          "analyzer": "standard", 
          "fields": {
            "english": {
              "type":     "text",
              "analyzer": "std_english" 
            }
          }
        }
      }
    }
  }
}

POST my_index/_analyze
{
  "field": "my_text", 
  "text": "The old brown cow"
}

POST my_index/_analyze
{
  "field": "my_text.english", 
  "text": "The old brown cow"
}

关于多个analyzer的顺序优先级

建索引的的时候

1.首先找field mapping里面的analyzer
2.没有1的时候,去找index settings的叫做default的analyzer
3.使用基础的Standard Analyzer

查询的时候

1.优先级最高的是你在query的时候指定analyzer,像下面这样

GET /_search
{
    "query": {
        "match_phrase" : {
            "message" : {
                "query" : "this is a test",
                "analyzer" : "my_analyzer"
            }
        }
    }
}

2.没指定,就去找field mapping里面的search_analyzer,推荐
3.没有search_analyzer,就按照field mapping里面的analyzer
4.还没有就去index settings找叫做default_search的analyzer
5.还没有就去index settings找叫做default的analyzer
6.使用基础的Standard Analyzer

search_quote_analyzer

search_quote_analyzer可以为短语指定分析器,使用功能query_string里面的query用双引号包住时或者match_phrase相关的查询,不希望停用词干扰,就可以用这个字段。
下面这个例子就是想消除短语里的停用词的影响,设置了search_quote_analyzer,目的就是

PUT my_index
{
   "settings":{
      "analysis":{
         "analyzer":{
            "my_analyzer":{ 
               "type":"custom",
               "tokenizer":"standard",
               "filter":[
                  "lowercase"
               ]
            },
            "my_stop_analyzer":{ 
               "type":"custom",
               "tokenizer":"standard",
               "filter":[
                  "lowercase",
                  "english_stop"
               ]
            }
         },
         "filter":{
            "english_stop":{
               "type":"stop",
               "stopwords":"_english_"
            }
         }
      }
   },
   "mappings":{
      "_doc":{
         "properties":{
            "title": {
               "type":"text",
               "analyzer":"my_analyzer", 
               "search_analyzer":"my_stop_analyzer", 
               "search_quote_analyzer":"my_analyzer" 
            }
         }
      }
   }
}

PUT my_index/_doc/1
{
   "title":"The Quick Brown Fox"
}

PUT my_index/_doc/2
{
   "title":"A Quick Brown Fox"
}

GET my_index/_search
{
   "query":{
      "query_string":{
         "query":"\"the quick brown fox\"" 
      }
   }
}

例子中的analyzer和search_quote_analyzer是没有去停用词的,search_analyzer是去停用词的。

当上面搜索的时候,由于是query_string而且双引号包着,等同于match_phrase,短语整体的匹配,这时候search_quote_analyzer就会不分词的去找,保证精确,不然就会两个都出现,只是score不同。

normalizer

normalizer属性对于keyword类型来说的,其功能相当于text的analyzer属性。

PUT index
{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["lowercase", "asciifolding"]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "foo": {
          "type": "keyword",
          "normalizer": "my_normalizer"
        }
      }
    }
  }
}

上面的如果两个doc分别为bar和BAR,因为添加了lowercase过滤器都会变成小写,在索引里存的内容都是bar。可以用聚合操作查看

GET index/_search
{
  "size": 0,
  "aggs": {
    "foo_terms": {
      "terms": {
        "field": "foo"
      }
    }
  }
}

boost

主要用来设置字段的权重,为了使查询的时候的相关性分数更高。默认值是1.0。
通常被用来应用于词查询(前缀、范围、模糊查询都是不值得)。像下面这样设置。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type": "text",
          "boost": 2 
        },
        "content": {
          "type": "text"
        }
      }
    }
  }
}

5.0版本前后说明

5.0以后的boost参数设置在建索引的时候设置已经不推荐了,推荐在查询中使用这个参数,但是因为这个在mapping这一章,所以放在这,不推荐上面那种。官方建议像下面直接查询使用:

POST _search
{
    "query": {
        "match" : {
            "title": {
                "query": "quick brown fox",
                "boost": 2
            }
        }
    }
}

不推荐的原因

1.index时候放了进去就不容易改了
2.search能够达到相同的效果,但是可以随意更改。
3.boost被放进index,虽然只有一个字节,但是会改变相关性得分。

coerce

数据不总是赶紧的,比如你定义了一个字段是Integer,但是doc里是String的"5",此时可以自动。
coerce就是强制类型转换默认均为true,如果设置false,就不能把String的"5"放进Integer的字段。
可以通过这个 "index.mapping.coerce": false,将所有字段都定义成false,然后专门设置某个字段为true。

PUT my_index
{
  "settings": {
    "index.mapping.coerce": false
  },
  "mappings": {
    "_doc": {
      "properties": {
        "number_one": {
          "type": "integer",
          "coerce": true
        },
        "number_two": {
          "type": "integer"
        }
      }
    }
  }
}

copy_to

Mapping 之 Meta-Fields那篇文章中讲了是6.0以后_all字段的替换方案的使用。那里面讲的很详细,不重复。

doc_values

这个在之前的Mapping 之 Meta-Fields那篇文章讲Percolator type也涉及到过这个。首先数据结构和倒排索引正好相反,倒排索引结构就不说了。但是聚合操作、排序分析或者执行脚本他们不是用倒排索引那一套来解决的,我们不是通过单词找文档,而是通过文档找到某个字段的term情况。
doc_values是磁盘数据,在创建索引的时候建立,每一个doc存的跟_source数据一模一样,默认所有支持doc_values参数的字段都是开启的,如果没有聚合排序或者脚本访问的操作,可以关闭。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "status_code": { 
          "type":       "keyword"
        },
        "session_id": { 
          "type":       "keyword",
          "doc_values": false
        }
      }
    }
  }
}

dynamic

你可以不设置doc的mapping结构直接往一个索引里添加doc,这就是ES自带的动态添加实现的。如果你希望整体结构是固定的,某些字段或者其内部字段可以动态添加,可以像下面这样设置。

PUT my_index
{
  "mappings": {
    "_doc": {
      "dynamic": false, //整体非动态
      "properties": {
        "user": { 
          "properties": {
            "name": {
              "type": "text"
            },
            "social_networks": { 
              "dynamic": true, //social_networks里面动态
              "properties": {}
            }
          }
        }
      }
    }
  }
}

enabled

enabled的场景是这样的,你想在_source看到它,但你又不想它被索引,比如session相关的数据这种。你可以将enabled设置为false。默认全部都是true。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "user_id": {
          "type":  "keyword"
        },
        "last_updated": {
          "type": "date"
        },
        "session_data": { //不可以被检索,但会在_source显示
          "enabled": false
        }
      }
    }
  }
}

fielddata

大多数字段都是建索引的,这样就可以被搜索了。搜索好像是在解决哪些文档包含这个词,而聚合、排序需要回答的是这个文档的这个字段的值是什么
上面的doc_values字段仿佛已经解决了大多数这种问题,但是text字段不支持doc_values参数。
解决方法就是text字段存在一个fielddata字段,这个字段是在内存中的。当一个text字段第一次使用聚合排序或者脚本的时候,这个字段会被创建,通过读取倒排索引的内容然后翻转term和doc的关系存到java的堆内存中。

fielddata与java堆内存

fielddata会消耗大量的java堆内存空间,尤其是在加载高基数text字段。一旦被创建,就会一直存在java的堆内存中。创建之后的加载过程也是很消耗资源,这也是为什么一般都不用这个字段。
假如你使用fielddata,一定要想清楚有没有必要,通常没有。比较好的做法就是添加keyword属性,用my_field.keyword来做聚合等操作:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_field": { 
          "type": "text",
          "fields": {
            "keyword": { 
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

假如你还是要用的话,就是像下面这样利用put实现:

PUT my_index/_mapping/_doc
{
  "properties": {
    "my_field": { 
      "type":     "text",
      "fielddata": true
    }
  }
}

fielddata_frequency_filter

fielddata可以使用fielddata_frequency_filter来减少加载到内存中的term的数量。min和max代表term的df(文档频率)的比例,可以转换成百分比。min_segment_size常用来表示term的最少出现次数。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "tag": {
          "type": "text",
          "fielddata": true,
          "fielddata_frequency_filter": {
            "min": 0.001,
            "max": 0.1,
            "min_segment_size": 500
          }
        }
      }
    }
  }
}

eager_global_ordinals

Global ordinals 是存在于doc_values之上的数据结构。按照字典顺序给每一个term都有一个唯一的值。A的值就会小于B的值。只有keyword和text字段的数据才会有Global ordinals。Keyword默认会存Global ordinals,text字段需要打开fielddate属性。
Doc Values也有序数,这是在那个字段的每个term的唯一编号。Global ordinals建立在这个序号的基础上,不管什么字段,统一编号。每当Doc Values出现新的值,Global ordinals也会重新构建。
Global ordinals同来做term聚合会提高效率,在每个分片上都是用这个分片上Global ordinals去执行,最后会将不同分片的结果对应出来返回。
默认情况下,搜索的时候是会加载Global ordinals的。但是要使用term聚合的时候,为了速度,还是要先设置为true。这样每添加一个文档,Global ordinals就会被刷新,而不是搜索的时候在去查找。

PUT my_index/_mapping/_doc
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": true
    }
  }
}

你可以在任意时间重新设置为false:

PUT my_index/_mapping/_doc
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": false
    }
  }
}

format

主要是对于日期而言的,比如下面这个是自己的格式:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type":   "date",
          "format": "yyyy-MM-dd"
        }
      }
    }
  }
}

此外支持date的加减,now-1m/d 现在的时间减去一个月的那一天

内置格式

太多了,直接放官方链接吧,看起来也不难

ignore_above

一个String放到字段里,"ignore_above": 20 表示如果这个字段的长度大于20就不会被索引到,但是_source属性还是有的,String数组的话就是看每一个。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "message": {
          "type": "keyword",
          "ignore_above": 20 
        }
      }
    }
  }
}

ignore_malformed 忽略畸形

有时候你不能控制你收到的数据,一个用户登陆可能是email,另一个是phone num。
默认情况下,试图将错误的数据类型放到index会引发异常,并拒绝整个文档。如果ignore_malformed设置为true,则允许忽略该异常。
文档中的畸形字段不被索引,但是文档中的其他字段被正常处理。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "number_one": {
          "type": "integer",
          "ignore_malformed": true
        },
        "number_two": {
          "type": "integer"
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
  "text":       "Some text value",
  "number_one": "foo" 
}

PUT my_index/_doc/2
{
  "text":       "Some text value",
  "number_two": "foo" 
}

上面的例子中,1的number_one字段没添进去为空,但是不会报错,2会直接拒绝。

index_options

index_options参数控制向倒排索引添加哪些信息,主要用于搜索和高亮。有下面四种参数:
1. docs 仅仅doc的id被建索引,可以回答这个term在这个字段出现过吗
2. freqs doc的id和term的频率被索引。术语频率用于重复的term比单个出现的term打分要高
3. positions doc的id和term的频率以及term的位置被索引。位置对于phrase这种短语查询很重要。
4. offsets doc的id、term的频率、term的位置以及开始字符以及结束字符的偏移量被索引,偏移量对于unified highlighter 高亮来说可以加速高亮。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "text": {
          "type": "text",
          "index_options": "docs"
        }
      }
    }
  }
}
PUT my_index/_doc/1
{
  "text": "Quick brown fox"
}

GET my_index/_search
{
  "query": {
    "match": {
      "text": "brown fox"
    }
  },
  "highlight": {
    "fields": {
      "text": {} 
    }
  }
}

index

index选项控制字段值是否被索引。它接受true或false,默认为true。没有索引的字段是不可查询的,但是聚合操作没有什么问题。

fields

一个字段可能不同目的有不同的形式,fields字段就是让一个字段同时具有多种类型。但是注意这并不会改变_source内容。这个在Field datatypes那篇文章已经讲过了。
还有一种情况,fields字段就是让一个字段同时具有多种analyzer。下面这个例子

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "text": { 
          "type": "text",
          "fields": {
            "english": { 
              "type":     "text",
              "analyzer": "english"
            }
          }
        }
      }
    }
  }
}

PUT my_index/_doc/1
{ "text": "quick brown fox" } 

PUT my_index/_doc/2
{ "text": "quick brown foxes" } 

GET my_index/_search
{
  "query": {
    "multi_match": {
      "query": "quick brown foxes",
      "fields": [ 
        "text",
        "text.english"
      ],
      "type": "most_fields" //两个的分加在一起
    }
  }
}

norms

norms 存储各种标准化因子,为后续查询计算文档对该查询的匹配分数提供依据。
虽然 norms 参数对评分很有用,但他需要占用大量的磁盘空间(通常是为了索引每个文档中每个字段每个字节的顺序,甚至不包含此字段的文档也是如此)。因此,如果你不需要计算此字段的评分,应该让字段取消 norms 的功能.特别是那些仅仅只是用做过滤条件或者聚合条件的字段。
一句话,只要是有可能用来打分,就默认为true。比如上面的boost如果创建索引就会当作一个norm

PUT my_index/_mapping/_doc
{
  "properties": {
    "title": {
      "type": "text",
      "norms": false
    }
  }
}

null_value

null 值不能被索引或者搜索.当一个字段被设置成 null(或者一个空数组,或者值全为 null 的数组)时, 该字段将被视为没有值。
null_value 参数允许你用一个特殊的值替换一个显示的 null 值, 以确保这个字段能被索引和搜索,但不会影响_source。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "status_code": {
          "type":       "keyword",
          "null_value": "NULL" //没东西可存,写个null进去
        }
      }
    }
  }
}
DELETE m*

PUT my_index/_doc/1
{
  "status_code": null
}

PUT my_index/_doc/2
{
  "status_code": [null] 
}

GET my_index/_search
{
  "query": {
    "term": {
      "status_code": "NULL" 
    }
  }
}

position_increment_gap

被解析的 text fields(文本字段)会将 term(词根)的位置考虑进去,目的是为了能支持 proximity queries(近似查询)和 phrase queries(短语查询)。当我们索引一个含有多个值的 text fields(文本字段)时,会在各个值之间加入一个"假想"的间隙,这样就可以阻止大多数跨值匹配的phrase短语查询。这个间隙的大小使用 position_increment_gap 参数来设定,默认值是100。

PUT my_index/_doc/1
{
    "names": [ "John Abraham", "Lincoln Smith"]
}

GET my_index/_search
{
    "query": {
        "match_phrase": {
            "names": {
                "query": "Abraham Lincoln" 
            }
        }
    }
}

GET my_index/_search
{
    "query": {
        "match_phrase": {
            "names": {
                "query": "Abraham Lincoln",
                "slop": 101 
            }
        }
    }
}

第二个短语查询会匹配到文档,因为虽然 Abraham 和 Lincoln 在不同的 string(字符串),但 slop > position_increment_gap值。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "names": {
          "type": "text",
          "position_increment_gap": 0 
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
    "names": [ "John Abraham", "Lincoln Smith"]
}

GET my_index/_search
{
    "query": {
        "match_phrase": {
            "names": "Abraham Lincoln" 
        }
    }
}

position_increment_gap为0,肯定能匹配上。

properties

type(类型)映射、object fields(对象字段)和 nested fields(嵌入字段)包含的 sub-fields(子字段),称之为 properties(属性)。这些 properties(属性)可以为任意 datatype(数据类型),包括 object(对象)和 nested(嵌入数据)。properties(属性)可以通过以下方式加入:
1.通过创建索引是明确地定义他们。
2.通过使用 PUT mapping API 添加或更新映射类型时明确地定义他们。
3.索引包含新字段的文档时动态的加入。

PUT my_index
{
  "mappings": {
    "_doc": { 
      "properties": {
        "manager": { 
          "properties": {
            "age":  { "type": "integer" },
            "name": { "type": "text"  }
          }
        },
        "employees": { 
          "type": "nested",
          "properties": {
            "age":  { "type": "integer" },
            "name": { "type": "text"  }
          }
        }
      }
    }
  }
}

通过使用“.”符号可以使内嵌字段引入查询、聚合等功能中

GET my_index/_search
{
  "query": {
    "match": {
      "manager.name": "Alice White" 
    }
  },
  "aggs": {
    "Employees": {
      "nested": {
        "path": "employees" //这个路径是不能少的
      },
      "aggs": {
        "Employee Ages": {
          "histogram": {
            "field": "employees.age",
            "interval": 5
          }
        }
      }
    }
  }
}

search_analyzer

通常情况下,我们在搜索和创建索引时使用的是同一分析器,以确保我们搜索是的词根与倒排索引中的词根拥有相同的格式。
但是有时我们又会有意识的在搜索时使用不同的分析器,例如使用 edge_ngram 解析器自动解析。
默认情况下,查询将会使用字段映射时定义的分析器,但也能通过 search_analyzer 设置来进行修改:
edge_ngram就是一个字母一个字母的分,具体可以用_analyze试一下:

PUT my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 20
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "text": {
          "type": "text",
          "analyzer": "autocomplete", 
          "search_analyzer": "standard" 
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
  "text": "Quick Brown Fox" 
}

GET my_index/_search
{
  "query": {
    "match": {
      "text": {
        "query": "Quick Br", 
        "operator": "and"
      }
    }
  }
}

POST my_index/_analyze
{
  "analyzer": "autocomplete",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

similarity

Elasticsearch允许你为每一个字段配置一个得分算法或 similarity(匹配算法)。similarity 设置提供了一个简单的方式让你选择匹配算法,而不仅仅是默认的BM25算法,比如可以选择 TF/IDF。
BM25:
Okapi BM25 算法。这个算法是 Elasticsearch 和 Lucene 的默认算法。不实用
classic:
TF/IDF 算法,也是 Elasticsearch 和 Lucene 的默认算法之一。
Boolean:
不算相关性打分,就是看有没有,然后和boost值合并。

store

默认情况下,所有的字段值会被索引使他们能搜索,但他们不会被 stored(存储)。意思就是这个字段能查询,但不能取回他的原始值。
但这没有关系。这个字段值已经是 _source 字段的一部分,他是被默认存储的。如果你只想取回一个字段或者少部分字段的值,而不是整个 _source,当然着可以通过 source filtering 达到目的。
但是在这种情况下其实也可以有意识的去 store(存储)一个字段。例如,你有一个包含title(标题), date(时间)和一个很大的 content(内容)字段,你仅仅只想取回 title 和 date ,而不需要从整个 _source 字段提取内容:

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

PUT my_index/_doc/1
{
  "title":   "Some short title",
  "date":    "2015-01-01",
  "content": "A very long content field..."
}

GET my_index/_search
{
  "stored_fields": [ "title", "date" ] 
}

注意

1.存储的字段将作为数组返回
2.为了保持一致性,存储的字段将总是作为数据返回,因为没有办法知道原始字段是单个值、多值还是空数组。
3.如果你需要原始值,你应该从 _source 字段返回。

term_vector

Term vectors是通过分析器解析产生的信息,包括:
1.一组 terms (词根)。
2.每个词根的位置(顺序)。
3.映射词根的首字符和尾字符与原始字符串原点的偏移量。
这些 term vectors 将被存储,一遍将他从一个特定的文档中取出。
term_vector 有一下参数设置:
1.no 不存储term vectors信息。(默认值)
2.yes 仅次字段中的 terms(词根)被存储。
3.with_positions 存储词根和位置。
4.with_offset 存储词根和字符偏移量。
5.with_positions_offsets 存储词根、位置和字符偏移量。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "text": {
          "type":        "text",
          "term_vector": "with_positions_offsets"
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
  "text": "Quick brown fox"
}

GET my_index/_search
{
  "query": {
    "match": {
      "text": "brown fox"
    }
  },
  "highlight": {
    "fields": {
      "text": {} 
    }
  }
}

这个和index_options相比,有点像,index_options是决定放在倒排索引里的内容,这个是查询的时候分析要用到的东西,而且index_options多了一个。

另外默认高亮使用的是 fast vector highlighter,这个需要设置成with_positions_offsets,将这些信息直接存起来比较好。

发表评论

电子邮件地址不会被公开。