Field datatypes

Elasticsearch 提供了大量的字段的数据类型,接近30种,感觉这一篇会写很久。
而且官方文档顺序竟然A-Z排序,所以来来回回。
慢慢来吧!!!


大概分为,核心数据类型、复杂数据类型、地理数据类型、专门数据类型、multi-fields五个部分

Core datatypes

string

两种情况,text和keyword

text

text标志着这个字段是全文搜索,这标志着你的这个type的数据会被分词,对于中文来说,就是单个单个的字,默认是standard analyzer。text有很多参数,有些我也说不明白,到具体的Mapping parameters时再说,关键的要记录一下:
analyzer是设置如何看待String字符串的,如果不被search_analyzer重写,搜索也是一样,默认是standard analyzer,不过可以设置,可以用_analyze接口测试。

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

    PUT my_index
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "my_english_analyzer": {
              "type": "standard",
              "max_token_length": 5,
              "stopwords": "_english_"
            }
          }
        }
      }
    }

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

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "full_name": {
              "type":  "text",
              "index_prefixes" : {
                "min_chars" : 1,    
                "max_chars" : 10    
              }
            }
          }
        }
      }
    }

keyword

必须重头到尾完全匹配,多个空格都不行其他就没什么好说的了,和text基本差不多,而且用不着analyze定义
有时候希望一个文本即是text又是keyword,可以参考最后的Multi-fields。

Numeric datatypes

都是数值型,主要分为下面8种:long, integer, short, byte, double, float, half_float, scaled_float
long: [-263 , 263 -1]
integer: [-231 , 231 -1]
short: [-32768,32767]
byte: [-128,127]
double: 64-bit的准确率
float:32-bit的准确率
half-float: 16-bit的准确率
scaled-float: 有时long太长,没必要就缩放,scaling factor是缩放因子,如果因子是10000,你存的如果是19290,会变成1.929

Date datatype

date可以是很多种样子,但是需要知道的是milliseconds-since-the-epoch这个样子的:1420070400001,因为当你进行排序或者range时,不管什么样子都会转换成这个样子去计算。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type": "date" 
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" //设置格式
        }
      }
    }
  }
}

PUT my_index/_doc/1
{ "date": "2015-01-01" } 

PUT my_index/_doc/2
{ "date": "2015-01-01T12:10:30Z" } 

PUT my_index/_doc/3
{ "date": 1420070400001 } 

GET my_index/_search
{
  "sort": { "date": "asc"} 
}

Boolean datatype

boolean类型的值就俩,true和false,带不带双引号都行

Binary datatype

二进制一般我的理解是存图片和音频这些东西。

Range datatypes

integer_range, float_range, long_range, double_range, date_rang, ip_range
range就是设置最大值和最小值,可以查,一看例子就懂

PUT range_index
{
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "_doc": {
      "properties": {
        "expected_attendees": {
          "type": "integer_range"
        },
        "time_frame": {
          "type": "date_range", 
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
      }
    }
  }
}

PUT range_index/_doc/1?refresh
{
  "expected_attendees" : { 
    "gte" : 10,
    "lte" : 20
  },
  "time_frame" : { 
    "gte" : "2015-10-31 12:00:00", 
    "lte" : "2015-11-01"
  }
}
//值搜索
GET range_index/_search
{
  "query" : {
    "term" : {
      "expected_attendees" : {
        "value": 12
      }
    }
  }
}
//范围搜索
GET range_index/_search
{
  "query": {
    "range": {
      "time_frame": {
        "gte": "2015-10-03",
        "lte": "2015-10-11",
        "relation": "within"
      }
    }
  }
}

PUT range_index/_mapping/_doc
{
  "properties": {
    "ip_whitelist": {
      "type": "ip_range"
    }
  }
}

PUT range_index/_doc/2
{
  "ip_whitelist" : "192.168.0.0/16"
}

Complex datatypes

复杂类型就分了三种,nested可以关注一下

Array datatype

ES虽然没特别把数组当回事,但是json本身支持的好,理解起来还是很简单

  • 字符串: [ "one", "two" ]
  • 整数: [ 1, 2 ]
  • 数据类型必须相同,然后是等价的: [ 1, [ 2, 3 ]] which is the equivalent of [ 1, 2, 3 ]
  • object数组: [ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]

值得注意的是

  1. 数组的第一个值会决定数组的类型,然后[ 2, "two" ]这样是不行的
  2. 数组可以存空值,但是空数组等于在_source里不存在
  3. 多说一句,查询的时候字符串数组每一个会变成text类型被检索
  4. 还要说一句,object数组不能只被检索出一个东西,因为这个等于被内部变成单个key之后多个合并成一个数组,如果想要被每个数组中的一个独立的检索,就应该设置成nested类型,当时困惑了好久
  5. 检索方式等同于object内部转换之后的key
    GET my_index/_search
    {
      "query": {
        "match": {
          "lists.name.keyword": "cool_list"
        }
      }
    }
    

Multi-value fields与倒排索引

事实上,所有字段类型都支持多个值字段。因为lucene本来就已经给一个字段添加了很多值,本身就是一个字段多个值。
这是Lucene被设计成一个全文搜索引擎的代价。为了能够在一大块文本中搜索单个词,Lucene将文本标记为单个词,并将每个词分别添加到倒排索引中。
这意味着即使是一个简单的文本字段也必须能够默认支持多个值。当添加其他数据类型(如数字和日期)时,它们使用与字符串相同的数据结构,结果变成多个值

Object datatype

想一下其实单个json可以嵌套数组,可以变得很复杂,那么ES对json支持的很好,所以Object就是复杂点的json
要注意的是,ES内部会对结构进行转化成简单的一层key:value,便于索引

PUT my_index/_doc/1
{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}

会被转换成:

{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}     

如果你现在查看mapping,由于manager是一个object,所以会有properties属性,同理name也是,而且first和last字段会有fields属性,同时是text和keyword

Nested datatype

刚才说了简单类型的数组能被索引,但是object不能被独立检索,现在来测试,比如下面这个:

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

//不同的还是可以被检索出了,因为它等同于
{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "user.first": "John"
          }
        },{
          "match": {
            "user.last": "white"
          }
        }
      ]
    }
  }
}

而想要达到分合有度,就应该用nested类型,注意一个索引的nested类型数量不能超过50,像下面这样:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "user": {
          "type": "nested" 
        }
      }
    }
  }
}

检索方式要使用nested专属查询方式,这时候你就查不到了,path属性不要忘了,inner_hits可以设置高亮的字段:

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": { 
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }} 
          ]
        }
      },
      "inner_hits": { 
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}    

Geo datatypes

感觉我都用不到,略,等什么时候觉得有用再说

Specialised datatypes

IP datatype

ip类型,就是放ip(4和6都行),没什么好说的,看个例子

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "ip_addr": {
          "type": "ip"
        }
      }
    }
  }
}

PUT my_index/_doc/1
{
  "ip_addr": "192.168.1.1"
}
//范围检索
GET my_index/_search
{
  "query": {
    "term": {
      "ip_addr": "192.168.0.0/16"
    }
  }
}

Completion datatype

这个类型的全称应该是Completion Suggester。故名思义,complete类型是为你提供自动补全的建议的。
这个类型主要提供自动完成以及在你打字时搜索相关功能。这是一种导航功能,引导用户在键入时获得相关结果,提高搜索精度。它不意味着拼写校正,或者是像term或phrase建议提供“你是不是想搜这个”的功能。

关于suggester

这个本来是属于搜索API里的,但是为了解释上面那句话,还是需要理解一下suggester。
suggester相当于完成了下面自动补全(第一个)或者纠错(第二个)的功能,不过都可以化为查询建议(Query Suggesters):


下面说一下怎么实现的,首先你要懂编辑距离,我的经验是这个编辑距离得分大于默认的0.8,es就会提供建议
怎么算大概就是index添加文档的时候会建立词典,然后比如elasticsearch的长度是12,如果你输入的就是elasticsearch,编辑距离是0,不会提供建议。但是如果是elasticsearc(少了个h),那么编辑距离就是1,得分就是 1-(编辑距离/现在的长度)=0.9166667。这个得分比较高,就会出现在option里面。

term、phrase和completion 三种suggester区别

首先说明term和phrase不需要专门声明类型,而completion是需要在mapping里先声明的,这也是为什么写这个的原因,completion会单独来讲。这部分主要参考的是es中文社区的一篇介绍

测试term和phrase

这两个不需要实现声明mapping动态构建,当然也可以像那篇文章里先构建mapping

POST _bulk/?refresh=true
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"Lucene is cool"}
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"Elasticsearch builds on top of lucene"}
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"Elasticsearch rocks"}
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"Elastic is the company behind ELK stack"}
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"elk rocks"}
{"index":{"_index":"blogs","_type":"tech"}}
{"body":"elasticsearch is rock solid"}

此时blogs索引里已经有一些文档了,可以进行下一步的探索。为帮助理解,我们先看看哪些term会存在于词典里。利用_analyze接口将输入的文本分析一下得到的结果就是一个词典,这些分出来的token都会成为词典里一个term,注意有些token会出现多次,因此在倒排索引里记录的词频会比较高,同时记录的还有这些token在原文档里的偏移量和相对位置信息。

POST _analyze
{
  "text": [
    "Lucene is cool",
    "Elasticsearch builds on top of lucene",
    "Elasticsearch rocks",
    "Elastic is the company behind ELK stack",
    "elk rocks",
    "elasticsearch is rock solid"
  ]
}

然后分别用term和phrase两种suggester查询一下:

//这个会将每一个词都寻找相应的编辑的距离,并且分别进行推荐
POST blogs/_search
{ 
  "suggest": {
    "my-suggestion": {
      "text": "elasticsearc rock",
      "term": {
        "suggest_mode": "missing",
        "field": "body"
      }
    }
  }
}

//term查询部分结果
"suggest": {
    "my-suggestion": [
      {
        "text": "elasticsearc",
        "offset": 0,
        "length": 12,
        "options": [
          {
            "text": "elasticsearch",
            "score": 0.9166667,
            "freq": 3
          }
        ]
      },
      {
        "text": "rock",
        "offset": 13,
        "length": 4,
        "options": [
          {
            "text": "rocks",
            "score": 0.75,
            "freq": 2
          }
        ]
      }
    ]
  }

//phrase顾名思义就是词组,是整个词组放在一起
POST blogs/_search
{ 
  "suggest": {
    "my-suggestion": {
      "text": "lucne and elasticsear rock",
      "phrase": {
        "field": "body",
        "highlight": {
          "pre_tag": "<em>",
          "post_tag": "</em>"
        }
      }
    }
  }
}
//phrase查询结果截取
"suggest": {
"my-suggestion": [
      {
        "text": "lucne and elasticsear rock",
        "offset": 0,
        "length": 26,
        "options": [
          {
            "text": "lucene and elasticsear rocks",
            "highlighted": "<em>lucene</em> and elasticsear <em>rocks</em>",
            "score": 0.025621036
          },
          {
            "text": "lucene and elasticsear rock",
            "highlighted": "<em>lucene</em> and elasticsear rock",
            "score": 0.021010999
          },
          {
            "text": "lucne and elasticsear rocks",
            "highlighted": "lucne and elasticsear <em>rocks</em>",
            "score": 0.020430265
          }
        ]
      }
    ]
  }

options直接返回一个phrase列表,由于加了highlight选项,被替换的term会被高亮。

completion测试

Completion Suggester,它主要针对的应用场景就是"Auto Completion"。 此场景下用户每输入一个字符的时候,就需要即时发送一次查询请求到后端查找匹配项,在用户输入速度较高的情况下对后端响应速度要求比较苛刻。因此实现上它和前面两个Suggester采用了不同的数据结构,索引并非通过倒排来完成,而是将analyze过的数据编码成FST和索引一起存放。对于一个open状态的索引,FST会被ES整个装载到内存里的,进行前缀查找速度极快。但是FST只能用于前缀查找,这也是Completion Suggester的局限所在。
看个例子就好:

PUT music
{
    "mappings": {
        "_doc" : {
            "properties" : {
                "suggest" : {
                    "type" : "completion"
                },
                "title" : {
                    "type": "keyword"
                }
            }
        }
    }
}

PUT music/_doc/1?refresh
{
    "suggest" : {
        "input": [ "Nevermind", "Nirvana" ],
        "weight" : 34
    }
}


POST music/_search?pretty
{
    "suggest": { //这个固定结构
        "song-suggest" : { //这个随便取名
            "prefix" : "nir", //这个就是要查的东西
            "completion" : { 
                "field" : "suggest", //这个是在哪个字段里
                "size" : 5 //提供五个建议
            }
        }
    }
}
简单说一下FST

一个专门用来理解fst的网站
Finite State Transducers 简称 FST,中文名:有穷状态转换器。在自然语言处理等领域有很大应用,其功能类似于字典的功能(STL 中的map,C# 中的Dictionary),但其查找是O(1)的,仅仅等于所查找的key长度。
如果不考虑FST的输出,FST本质上是一个最小的,有向无环DFA(确定性有穷状态自动机)。
ps:一个有趣的正则表达式利用DFA思想匹配 3 的倍数
字典树我还是比较,我看了一些博客发现FST的思想不仅利用字典树的前缀存储方式,对后缀存储也很类似,具体可以自行了解,下面贴一张经典数据结构对比:

Token count datatype

token_count类型的字段实际上是一个整数字段,它接受字符串值,对其进行分析,然后对字符串中的分词数量进行索引。
主要就是对已经是text类型像再添个keyword类型一样添加成token_count类型,直接看代码秒懂:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "name": { 
          "type": "text",
          "fields": {
            "length": { 
              "type":     "token_count",
              "analyzer": "standard"
            }
          }
        }
      }
    }
  }
}

PUT my_index/_doc/1
{ "name": "John Smith" }

PUT my_index/_doc/2
{ "name": "Rachel Alice Williams" }

GET my_index/_search
{
  "query": {
    "term": {
      "name.length": 3 
    }
  }
}

mapper-murmur3

这个需要一个官方插件支持,使用下面这个命令安装,安装之后要重启一下ES

sudo bin/elasticsearch-plugin install mapper-murmur3

MMAPUR-MURMUR3插件提供了计算创建索引的时候某个字段数据的hash值并将它们存储在索引中。
人话就是:murmur3类型的字段将会存一个hash值,用处在于相同的值的hash值也是相同的,比如你某个字段在两个doc里面都是“武汉大学”,他们的hash值是相同的。
这个类型主要用来进行cardinality这个运算,就是算有多少不同的基数。

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "my_field": {
          "type": "keyword",
          "fields": {
            "hash": {
              "type": "murmur3"
            }
          }
        }
      }
    }
  }
}

PUT my_index/my_type/1
{
  "my_field": "This is a document"
}

PUT my_index/my_type/2
{
  "my_field": "This is another document"
}

//用hash值做cardinality运算
GET my_index/_search
{
  "aggs": {
    "my_field_cardinality": {
      "cardinality": {
        "field": "my_field.hash" 
      }
    }
  }
}

GET my_index/_search
{
  "aggs": {
    "my_field_cardinality": {
      "cardinality": {
        "field": "my_field" 
      }
    }
  }
}

用原始值做cardinality运算和用hash值做cardinality运算的结果是一样的,就是没它快。
另外一点,官方不建议对数值型的字段或者那种本来字段的值就基本不相同的字段设置成murmur3这个类型,因为会变慢,而且增加了磁盘的开销。

Percolator type

首先要理解query-dsl(dsl--DOMAIN SPECIFIED LANGUAGE,特定领域的语言),对于ES来说dsl都是json格式;然后你本来在_search接口那些写在query下的查询的语言query-dsl;所以Percolator之所以就像过滤器,就是因为先把这些query-dsl写到索引的某个字段,然后你把n个文本放进去进行Percolator检索时,就会返回n个能被这个query-dsl检索到的slot,也就是下标。
Percolator type相当于把查询存到了索引中。
这个需要结合Percolator Query来讲,下面先讲一下Percolator Query的相关。

Percolator Query

定义好mapping,并且添加一个percolator相关字段的值。

PUT /my-index
{
    "mappings": {
        "_doc": {
            "properties": {
                "message": {
                    "type": "text"
                },
                "query": {
                    "type": "percolator"
                }
            }
        }
    }
}
//在query里放的是match的dsl语句
PUT /my-index/_doc/1?refresh
{
    "query" : {
        "match" : {
            "message" : "bonsai tree"
        }
    }
}
普通使用
//目的就是看message里被索引的过滤器类型的命中情况,document单文档查询
GET /my-index/_search
{
    "query" : {
        "percolate" : {
            "field" : "query",
            "document" : {
                "message" : "A new bonsai tree in the office"
            }
        }
    }
}
使用filter

如果不关心文档匹配的分数,可以想下面那样,所有文档只要匹配打分都是1

GET /my-index/_search
{
    "query" : {
        "constant_score": {//所有score都会是1
            "filter": {
                "percolate" : {
                    "field" : "query",
                    "document" : {
                        "message" : "A new bonsai tree in the office"
                    }
                }
            }
        }
    }
}
多文档匹配
GET /my-index/_search
{
    "query" : {
        "percolate" : {
            "field" : "query",
            "documents" : [ //注意这个地方
                {
                    "message" : "bonsai tree"
                },
                {
                    "message" : "new tree"
                },
                {
                    "message" : "the office"
                },
                {
                    "message" : "office tree"
                }
            ]
        }
    }
}
用文档去查询
PUT /my-index/_doc/2
{
  "message" : "A new bonsai tree in the office"
}
GET /my-index/_search
{
    "query" : {
        "percolate" : {
            "field": "query",
            "index" : "my-index",
            "type" : "_doc",
            "id" : "2",//先存个文档2,直接用文档2去查询
            "version" : 1 
        }
    }
}

重新回到正题,Percolator type

其实看了上面的Percolator Query部分,很多都应该明白了。这部分官方文档大部分讲的也就是这些,还有reindex这个没什么兴趣,另外就是Optimizing query time text analysis。

Optimizing query time text analysis

因为都是文本分析,所以解析或者查询文本的时间是比较浪费时间。
解决方案就是让这种文本解析只使用一次,采用两个不同的analyzer,第一个analyzer在创建索引中用来解析text,第二个在添加文档时使用,一般就使用whitespace分析器(空格分词)这个就比较直接高效。

PUT /test_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer" : {
          "tokenizer": "standard",
          "filter" : ["lowercase", "porter_stem"] //设置小写,波特词干
        }
      }
    }
  },
  "mappings": {
    "_doc" : {
      "properties": {
        "query" : {
          "type": "percolator"
        },
        "body" : {
          "type": "text",
          "analyzer": "my_analyzer" //这边设置成上面的my_analyzer
        }
      }
    }
  }
}

PUT /test_index/_doc/1?refresh
{
  "query" : {
    "match" : {
      "body" : {
        "query" : "miss bicycl",
        "analyzer" : "whitespace" //第二个analyzer,分词,但两个都要出现
      }
    }
  }
}
Optimizing wildcard queries

通配符查询(使用正则表达式查询)使用的资源更多时间更长,尤其是正则表达式很长。
在具有前缀通配符表达式的通配符查询或直接使用prefix查询的情况下,可以使用edge_ngram 过滤器在配置edge_ngram过滤器的字段上用常规查询替换这些查询。

PUT my_queries1
{
  "settings": {
    "analysis": {
      "analyzer": {
        "wildcard_prefix": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "wildcard_edge_ngram" //使用下面设置的过滤器
          ]
        }
      },
      "filter": {
        "wildcard_edge_ngram": { 
          "type": "edge_ngram", //通配符设置
          "min_gram": 1,
          "max_gram": 32
        }
      }
    }
  },
  "mappings": {
    "query": {
      "properties": {
        "query": {
          "type": "percolator"
        },
        "my_field": {
          "type": "text",
          "fields": {
            "prefix": { 
              "type": "text",
              "analyzer": "wildcard_prefix",
              "search_analyzer": "standard"
            }
          }
        }
      }
    }
  }
}

//原来的通配符查询当作文档放进index,不好
PUT /my_queries1/query/2?refresh
{
  "query": {
    "wildcard": {
      "my_field": "abc*"
    }
  }
}

//现在设置好应该用term查询当作文档放进index
PUT /my_queries1/query/1?refresh
{
  "query": {
    "term": {
      "my_field.prefix": "abc"
    }
  }
}

//查询,跟其他的一样,
GET /my_queries1/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "my_field": "abcd"
      }
    }
  }
}

关于后缀,主要比上面的前缀加一个reverse过滤器,搜索的时候也要带上

PUT my_queries2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "wildcard_suffix": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "reverse",
            "wildcard_edge_ngram"
          ]
        },
        "wildcard_suffix_search_time": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "reverse"
          ]
        }
      },
      "filter": {
        "wildcard_edge_ngram": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 32
        }
      }
    }
  },
  "mappings": {
    "query": {
      "properties": {
        "query": {
          "type": "percolator"
        },
        "my_field": {
          "type": "text",
          "fields": {
            "suffix": {
              "type": "text",
              "analyzer": "wildcard_suffix",
              "search_analyzer": "wildcard_suffix_search_time" 
            }
          }
        }
      }
    }
  }
}
//原来
PUT /my_queries2/query/2?refresh
{
  "query": {
    "wildcard": {
      "my_field": "*xyz"
    }
  }
}
//现在
PUT /my_queries2/query/2?refresh
{
  "query": {
    "match": { 
      "my_field.suffix": "xyz"
    }
  }
}
//搜索
GET /my_queries2/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "my_field": "wxyz"
      }
    }
  }
}

Percolator type的局限性

1.因为一次处理一个文档,所以不支持has_child 和 has_parent的文档。
2.因为很多查询都会使用GET去进行查询请求,percolator只能让存在索引的query语句自行GET一次。因此需要注意的是,每次对percolator查询在主碎片和副本碎片上进行索引时,都会同时获取这些查询所执行的术语,因此如果源索引在索引时发生变化,那么实际索引的术语在碎片副本之间可能不同
3.脚本查询只能访问doc values字段,in-memory索引不支持存储字段,_source字段和其他存储字段不会被存储。这就是为什么在脚本查询中,_source字段和其他存储字段不可用的原因。
doc values和倒排索引就是x->y到y->x的关系,倒排存取term->doc,doc values存取doc->value。
ES搜索使用倒排索引查找文档,而聚合操作、排序或者执行脚本就会用到Doc Values里的数据。所以如果没有聚合操作、排序分析或者执行脚本,可以禁用doc_values: false。
Doc Values 通过序列化把数据结构持久化到磁盘,我们可以充分利用操作系统的内存,而不是 JVM 的 Heap。 当 working set 远小于系统的可用内存,系统会自动将 Doc Values 保存在内存中,使得其读写十分高速; 不过,当其远大于可用内存时,操作系统会自动把 Doc Values 写入磁盘。很显然,这样性能会比在内存中差很多,但是它的大小就不再局限于服务器的内存了。如果是使用 JVM 的 Heap 来实现那么只能是因为 OutOfMemory 导致程序崩溃了。

join datatype

join类型主要是为了在同一个索引里创建父子结构。
Removal of mapping types这篇文章里我也简单说过这个事,不过当时着重点在于一个索引怎么办,现在重点是join类型。
简单的join怎么实现直接看上面那篇文章。

Parent-join and performance

join字段不应该像关系数据库中的join那样使用。在ES中,良好性能的关键是将数据归一化为文档。但是join字段的“has_child”或“has_parent”都会对查询性能增加了很大负担。
Join字段唯一有意义的情况是,如果您的数据包含一对多关系,其中一个实体显著超过另一个实体。这种情况的一个例子是产品和产品的各种价格。如果产品价格的数量远远超过产品的数量,那么将产品建模为parent文档,将报价建模成child文档是有意义的。

Parent-join restrictions

1.每个索引只允许一个join字段
2.父和子文档必须在同一分片上索引。这意味着在获取、删除或更新子文档时需要提供相同的路由值。
3.元素可以有多个子对象,但只有一个父节点。
4.可以向现有的join字段添加新的relation
5.只能在现有元素已经是父元素是,才可以添加子元素

Searching with parent-join

首先声明my_parent ⇒ [my_child, another_child]这种关系可以简化成my_join_field#my_parent。
Join查询共分为两类四种情况:

nested已经上面讲过了,下面顺带讲一下后三种(直接看例子,没什么东西),查询的都是用下面这个建的索引:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": { 
          "type": "join",
          "relations": {
            "question": "answer" 
          }
        }
      }
    }
  }
}

PUT my_index/_doc/1?refresh
{
  "text": "This is a question",
  "my_join_field": {
    "name": "question" 
  }
}

PUT my_index/_doc/2?refresh
{
  "text": "This is a another question",
  "my_join_field": {
    "name": "question"
  }
}

PUT my_index/_doc/3?routing=1&refresh 
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

PUT my_index/_doc/4?routing=1&refresh
{
  "text": "This is another answer",
  "my_join_field": {
    "name": "answer",
    "parent": "1"
  }
}
Has Child Query
GET my_index/_doc/_search
{
  "query": {
    "has_child": {
      "type": "answer",
      "query": {
        "match": {
          "text": "an"
        }
      }
    }
  }
}
Has Parent Query
GET my_index/_doc/_search
{
  "query": {
    "has_parent": {
      "parent_type": "question",
      "query": {
        "match_all": {}
      }
    }
  }
}
Parent_id Query
GET my_index/_search
{
  "query": {
    "parent_id": { 
      "type": "answer",
      "id": "1"
    }
  },
  //下面这些都可以不要
  "aggs": {
    "parents": {
      "terms": {
        "field": "my_join_field#question", 
        "size": 10
      }
    }
  },
  "script_fields": {
    "parent": {
      "script": {
         "source": "doc['my_join_field#question']" 
      }
    }
  }
}

Multiple children per parent--多个孩子

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": {
          "type": "join",
          "relations": {
            "question": ["answer", "comment"]  
          }
        }
      }
    }
  }
}

Multiple levels of parent join--孩子还有孩子

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": {
          "type": "join",
          "relations": {
            "question": ["answer", "comment"],  
            "answer": "vote" 
          }
        }
      }
    }
  }
}

Alias datatype

字段别名只能在具有单映射类型的索引上指定。

PUT trips
{
  "mappings": {
    "_doc": {
      "properties": {
        "distance": {
          "type": "long"
        },
        "route_length_miles": {
          "type": "alias",
          "path": "distance" // distance改名成route_length_miles
        },
        "transit_mode": {
          "type": "keyword"
        }
      }
    }
  }
}

Alias注意事项

1.alias必须是确定的,不能是object或者已经是alias
2.alias的那个字段必须确定存在。
3.nested字段,aliaa必须和它结构想通过。
4.不支持用alias命名多个field的。
5._source里是没有alias的

Multi-fields

多个type的主要是为了有些时候你既希望这个字段是text,有希望keyword,这时候就可以利用field属性

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

GET my_index/_search
{
  "query": {
    "match": {
      "city.aa": "aaa"//可以换成"city": "aaa"
    }
  }
}

上面的aa等于帮助他换了个type,上面的token_count,murmur3等是这样工作的

写了三天,终于写完了!!!
其实里面不仅只有关于mapping的东西,还有查询、自动补全、索引结构等等,写完还是很开心!!!
另外就是学会datatypes,在看各种api语句就会简单一些了!!!

“Field datatypes”的一个回复

发表评论

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