基本概念

  • Cluster 集群:由多个协同工作的 ES 实例组合成的集合,一个集群由一个唯一的名字标识

  • Node 节点:单个 ES 的服务实例叫做节点,本质上就是一个 Java 进程,存储集群的数据,参与集群的索引和搜索功能

    • 主节点(Master)。主节点在整个集群是唯一的,Master 从有资格进行选举的节点(Master Eligible)中选举出来。主节点主要负责管理集群变更、元数据的更改
    • 数据节点(Data Node)。其负责保存数据,要扩充存储时候需要扩展这类节点。数据节点还负责执行数据相关的操作,如:搜索、聚合、CURD 等。所以对节点机器的 CPU、内存、I/O 要求都比较高
    • 协调节点(Coordinating Node)。负责接受客户端的请求,将请求路由到对应的节点进行处理,并且把最终结果汇总到一起返回给客户端。因为需要处理结果集和对其进行排序,需要较高的 CPU 和内存资源
  • Index 索引: 一个索引是一个文档的集合。每个索引有唯一的名字,通过这个名字来操作它。一个集群中可以有任意多个索引

  • Type 类型:指在一个索引中,可以索引不同类型的文档,如用户数据、商品数据。从6.0.0 版本起已废弃,一个索引中只存放一类数据

  • Document 文档:被索引的一条数据,索引的基本信息单元,以JSON格式来表示

  • Shard 分片:在创建一个索引时可以指定分成多少个分片来存储。每个分片本身也是一个功能完善且独立的“索引”,可以被放置在集群的任意节点上。分片的好处是允许我们水平切分/扩展容量可在多个分片上进行分布式的、并行的操作,提高系统的性能和吞吐量

  • Replication 备份: 一个分片可以有多个备份(副本)

关系数据库和elasticsearch对比如下

索引操作

数据类型

简单数据类型

复杂数据类型

数据类型

  • 字符串数组:["tom","jerry"]
  • 整数数组:[1,2,3]
  • 由数组组成的数组:[1,[2,3]],等价于[1,2,3]
  • 对象数组:[{"name": "Tom", "age": 20}, {"name": "Jerry", "age": 18}]

注意:

  • 动态添加数据时, 数组中第一个值的类型决定整个数组的类型
  • 不支持混合数组类型, 比如[1, “abc”]
  • 数组可以包含null值, 空数组[]会被当做missing field —— 没有值的字段

对象类型

{
    "name": "tom",
    "address": {
        "region": "China",
        "location": {"province": "ZheJiang", "city": "HangZhou"}
    }
}

嵌套类型

嵌套类型是对象类型的特例,可以让array类型的对象被独立索引和搜索

如果需要对以对象进行索引, 且保留数组中每个对象的独立性, 就应该使用嵌套数据类型

# 创建文档
{
  "group": "stark",
	"performer": [
        {"first": "John", "last": "Snow"},
        {"first": "Sansa", "last": "Stark"}
    ]
}

搜索

{
    "query": {
        "nested": {
            "path": "performer",
            "query": {
                "bool": {
                    "must": [
                        { "match": { "performer.first": "John" }},
                        { "match": { "performer.last":  "Snow" }} 
                    ]
                }
            }
        }
    }
}

特殊类型

地理形状类型 、IP类型等等

创建索引

创建索引

PUT /my_index
{
  # settings
  # mappings
}

setting设置

{
  "settings": {
    # 主分片数
    "number_of_shards": 5,
    # 副本分片数
    "number_of_replicas": 1,
    # 设置一些过滤器
    "analysis": {
      # keyword类型搜索忽略大小写
      "normalizer": {
        "lowercase_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {}
}

mapping设置

Dynamic Mapping自动创建mapping

分词

常用分词器

  • Standard Analyzer : 这个是默认的分词器,使用 Unicode 文本分割算法,将文本按单词切分并且转为小写
  • Simple Analyzer : 按照非字母切分并且进行小写处理
  • Stop Analyzer : 与 Simple Analyzer 类似,但增加了停用词过滤(如 a、an、and、are、as、at、be、but 等)
  • Whitespace Analyzer : 使用空格对文本进行切分,并不进行小写转换
  • Patter n Analyzer : 使用正则表达式切分,默认使用 \W+ (非字符分隔)。支持小写转换和停用词删除
  • keyword Analyzer : 不进行分词
  • Language Analyzer : 提供了多种常见语言的分词器。如 Irish、Italian、Latvian 等
  • Customer Analyzer:自定义分词器

IK 分词器

IK 的算法是基于词典的,其支持自定义词典和词典热更新。IK 分词器插件一定要和es的版本相对应,具体安装参考 https://github.com/infinilabs/analysis-ik

IK 有两种模式:ik_max_word 和 ik_smart

  • ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、人、民、共和国、共和、和、国国、国歌”,会穷尽各种可能的组合
  • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国、国歌”,适合 Phrase 查询

测试分词器

POST _analyze
{  
    "analyzer": "ik_max_word",
    "text": "Linus 在90年代开发出了linux操作系统"  
}

POST _analyze
{  
    "analyzer": "ik_smart",
    "text": "Linus 在90年代开发出了linux操作系统"  
}

创建索引执行分词器

{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

分词工作流程

  • character filters: 对输入进行预处理,比如删除 html 元素、将表情符号映射为文本
  • tokenizer: 按照指定的规则对文本进行切分,比如按空格来切分单词,同时也负责标记出每个单词的顺序、位置以及单词在原文本中开始和结束的偏移量
  • token filters: 对切分后的单词进行处理,如转换为小写、删除停用词、增加同义词、词干化等。例如输入 Is this déja vu, 如果按照空格分词的话,会被分为 Is, this, déja, vu。我们可以设置 asciifolding token filters, 将 déja, 转换为 deja,lowercase将大写转换为小写
{
  "char_filter": ["html_strip",
    {
      "type": "mapping",
      "mappings": [
        "😊 => happy"
      ]
    }
  ],
  "tokenizer": {
    "type": "standard"
  },
  "filter": [],
  "text": [
    "Tom &amp; Jerrey &lt; <b>world</b> 😊"
  ]
}

{
  "mappings": {
    "properties": {
      "my_text": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "char_filter": [
            "html_strip",
            "emo_to_word"
          ],
          "tokenizer": "standard",
          "filter": [
            "lowercase"
          ]
        }
      },
      "char_filter": {
        "emo_to_word": {
          "type": "mapping",
          "mappings": [
            "😊 => happy"
          ]
        }
      }
    }
  }
}

什么是倒排索引

倒排索引指的是将每一个关键字映射到它出现的文档中。如下图所示

倒排索引数据结构

倒排索引分为 2 部分:一部分叫 term directory(term 词典),一部分叫 posting list(倒排列表)。如下图所示

  • term directory,term 字典,存放着每个单词到对应倒排列表的映射关系
  • posting list,Docs 是一个数组。其中 1:2:[2,6] 意思如下
    • 1:文档ID
    • 2:词频(term frequency)
    • [2,6]:出现在文档中的第 2,6 个 term

倒排索引的生成

这里涉及到我们之前讲过的分词器。工作流程如下图所示

大体就包含2部分,根据分词器将文本分词,然后根据分词生成倒排索引。

查询

例如我们查询:To do right 则该文本会先被分词为:to, do, right 对应的结果如下图所示:

TF、IDF

默认情况下,ES 会根据文档与搜索词的相关性得分对结果降序返回。相关性得分与以下 2 个概念有关

  • Term Frequency(TF):term 在文档中出现的频率,得分正相关。出现频率越高,得分越高
  • Inverted Document Frequency(IDF):term 在 所有文档 中出现的频率,得分负相关。出现频率越高,得分越低

ES5之前默认的算分采用TF-IDF,ES5之后采用BM25

BM25是在TF-DF基础上做了一个收敛,避免了TF无限增长时得分无限增长的问题

搜索语法

match_all 查询所有文档

{
  "query": {
    "match_all": {}
  }
}

match 单个字段匹配

单个字段的查询,先对搜索词进行分词,然后再搜索。

keyword:不分词精确匹配

text:分词后匹配

{
  "query": {
    "match": {
      "title": "数据库", # text的会匹配"数据库"、"数据"...
    }
  }
}

multi_match 多字段匹配

为能在多个字段上反复执行相同查询提供了一种便捷方式

keyword:不分词精确匹配

text:分词匹配

Type:

  • 最佳字段(best_fields):取fields中分数最高的,侧重于字段维度,单个字段的得分权重大,对于同一个query,单个field匹配更多的term,则优先排序。
  • 混合字段(cross_fields):类似于copy_to,就是类似于把所有字段文字都拼接起来,然后算评分
{
  "query": {
    "multi_match": {
      "type": "best_fields", # 取fields中最高的评分
      "query": "数据库",
      "fields": ["title","content"]
    }
  }
}


{
  "query": {
    "multi_match": {
      "type": "cross_fields", # fields中字段全部拼接到一起算分
      "operator": "and"
      "query": "数据库",
      "fields": ["title^5","content"], # 与copy_to相比,其优势是可以在搜索时为单个字段提升权重
    }
  }
}

match_phrase 短语匹配

短语匹配查询,slop表示分词的跨度,指分词和分词之间可以相隔多少个词,缺失了这些词仍然可以查到结果

{
  "query": {
    "match_phrase": {
      "title": {
        "query": "数据库",
        "slop": 4
      }
    }
  }
}

term 单值精确查询

单个字段的精确查询

keyword:不分词精确匹配

text:不分词匹配

如果是keyword类型查询的时候,不需要指定keyword关键字。如果是text类型的在全匹配的时候需要指定keyword关键字

{
  "query": {
    "term": {
      "title": "红楼梦"
    }
  }
}

{
  "query": {
    "term": {
      "content.keyword": "当时社会的阶级关系和权力结构"
    }
  }
}

# 大小写不敏感查询
{
  "query": {
    "term": {
      "title": {
        "value": "Romeo",
        "case_insensitive": true
      }
    }
  }
}

terms 多值精确匹配

精确匹配多个值,只要被查字段包含指定数组中任何一个值就算符合条件,不能设置大小写敏感, 如果需要大小写不敏感需要在创建索引时指定

{
  "query": {
    "terms": {
      "title": ["红楼梦","西游记"]
    }
  }
}

prefix 前缀匹配

匹配符合前缀要求的倒排索引数据。

注意:需要区分大小写,搜索词要是小写(或者定义mapping和settings的时候给这个字段设置忽略大小写),因为文档的词都是小写的

{
  "query": {
    "prefix":{
      "title":"rom"
    }
  }
}

wildcard 通配符匹配

通配符(*表示任何字符串,?表示任何单个字符)匹配倒排索引

注意:需要区分大小写,搜索词要是小写(或者定义mapping和settings的时候给这个字段设置忽略大小写),因为文档的词都是小写的

{
  "query": {
    "wildcard": {
      "title": "ro*"
    }
  }
}

regexp 正则匹配

正则表达式匹配倒排索引

注意:需要区分大小写,搜索词要是小写(或者定义mapping和settings的时候给这个字段设置忽略大小写),因为文档的词都是小写的

{
  "query": {
    "regexp": {
      "title": ".*iet"
    }
  }
}

fuzzy 近似匹配

fuzziness用于控制levenshtein距离,区分大小写

{
  "query": {
    "fuzzy": {
      "author": {
        "value": "Shakespea",
        "fuzziness": 2
      }
    }
  }
}

range 区间匹配

区间匹配,

可以使用:gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于)

{
  "query": {
    "range": {
      "count": {
        "gte": 30,
        "lte": 70
      }
    }
  }
}

exists 匹配指定字段非空的文档

查询指定字段值不为null的数据,用于把某个字段不为null的文档往前排

{
  "query": {
    "exists": {
      "field": "title"
    }
  }
}

ids id数值匹配

用于通过文档id数组匹配文档

{
  "query": {
    "ids": {
      "values": [1,3]
    }
  }
}

term、match 区别

  • term 完整匹配整个输入,正常 term 的查询性能会优于 match
  • match 对输入进行分词,然后匹配每个分词(需要注意的是,当在 keyword 类型下使用 match 时,他的表现和 term 一样)

bool 布尔查询

bool查询是一个或者多个查询子句的组合,支持must 、must not、should、filter四种子句。

  • must :必须匹配,参与算分
  • should :选择性匹配、参与算分
  • must not:必须不匹配,不参与算分
  • filter:必须匹配,不参与算分, 可以使用缓存

bool子查询可以任意顺序出现、可以同时多个子查询

should must联用问题

{
  "query": {
    "bool": {
      "must": {
        "term": {"gender": "男"}
      },
      "should": [
        {
          "term": {"score": 70}
        },
        {
          "term": {"score": 80}
        }
      ]
    }
  }
}

解决方法

{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "must": [
              {
                "term": {"gender": "男"}
              },
              {
                "term": {"score": "70"}
              }
            ]
          }
        },
        {
          "bool": {
            "must": [
              {
                "term": {"gender": "男"}
              },
              {
                "term": {"score": "80"}
              }
            ]
          }
        }
      ]
    }
  }
}

{
  "query": {
    "bool": {
      "must": [
        {
          "term": {"gender": "男"}
        },
        {
          "bool": {
            "should": [
              {
                "term": {"score": 70}
              },
              {
                "term": {"score": 80}
              }
            ]
          }
        }
      ]
    }
  }
}

尽量在筛选的时候多使用不参与算分的must_not和filter,以保证性能良好

query_string 字符串匹配

字符串的专用匹配,可以多个条件、多个字段进行匹配

  • 逻辑运算符大写AND、OR、NOT
  • 默认会对字符串分词,如果不需要分词 需要加上”“
  • 默认运算符是OR
  • 严格按照逻辑关系加括号
{
  "query": {
    "query_string": {
      "query": "(truth AND good) OR Romeo",
      "fields": ["title","content"]
    }
  }
}

分页

ES 提供了 3 种分页方式:

  • from + size:最普通、简单的分页方式,但是会产生深分页的问题
  • search after:解决了深分页的问题,但只能一页一页地往下翻,不支持跳转到指定页数
  • scroll API/PIT:会创建数据快照,无法检索新写入的数据,适合对结果集进行遍历的时候使用

from + size 分页操作与深分页问题

在我们检索数据时,系统会对数据按照相关性算分进行排序,然后默认返回前 10 条数据。我们可以使用 from + size 来指定获取哪些数据。其使用示例如下:

{
  "from": 0, # 指定开始位置
  "size": 10, # 指定获取文档个数
  "query": {
    "match_all": {}
  }
}

如上示例,使用 "from" 指定获取数据的开始位置,使用 "size" 指定获取文档的个数。

但当我们将 from 设置大于 10000 或者 size 设置大于 10001 的时候,这个查询将会报错:

# 返回结果中的部分错误信息
......
"root_cause" : [
  {
    "type" : "illegal_argument_exception",
    "reason" : "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
  }
],
"type" : "search_phase_execution_exception",
"reason" : "all shards failed",
"phase" : "query",
"grouped" : true,
......

数据集合太大了,系统拒绝了我们的请求。我们可以使用 "index.max_result_window" 配置项设置这个上限:

PUT my_index/_settings
{
  "index": {
    "max_result_window": 20000
  }
}

这个配置有时候可以解决燃眉之急,但是这个上限设置过大的情况下会产生非常严重的后果。

如上图,ES把数据保存到3个主分片中,当使用from = 90和size = 10进行分页的时候,ES会先从每个分片中分别获取100个文档,然后把这300个文档再汇聚到协调节点中进行排序,最后选出排序后的前100个文档,返回第90到99的文档。

可以看到,当页数变大(发生了深分页)的时候,在每个分片中获取的数据就越多,消耗的资源就越多。并且如果分片越多,汇聚到协调节点的数据也越多,最终汇聚到协调节点的文档数为:shard_amount * (from + size)。

search after

使用 search after API 可以避免产生深分页的问题,不过 search after 不支持跳转到指定页数,只能一页页地往下翻。

使用 search after 接口分为两步:

  • 在 sort 中指定需要排序的字段,并且保证其值的唯一性(可以使用文档的 ID)
  • 在下一次查询时,带上返回结果中最后一个文档的 sort 值进行访问
# 第一次调用 search after
POST my_index/_search
{
  "size": 2,
  "query": { "match_all": {} },
  "sort": [
    { "_id": "asc" }
  ]
}

# 返回结果
"hits" : [
  {
    "_id" : "1",
    "_source" : {
      "book_id" : "4ee82467",
      "price" : 20.9
    },
    "sort" : ["1"]
  },
  {
    "_id" : "2",
    "_source" : {
      "book_id" : "4ee82462",
      "price" : 19.9
    },
    "sort" : ["2"]
  }
]

# 第二次调用 search after
POST books/_search
{
  "size": 2,
  "query": {
    "match_all": {}
  },
  "search_after":["2"], # 设置为上次返回结果中最后一个文档的 sort 值
  "sort": [
    { "_id": "asc" }
  ]
}

因为有了唯一的排序值做保证,所以每个分片只需要返回比 sort 中唯一值大的 size 个数据即可。例如,上一次的查询返回的最后一个文档的 sort 为 a,那么这一次查询只需要在分片 1、2、3 中返回 size 个排序比 a 大的文档,协调节点汇总这些数据进行排序后返回 size 个结果给客户端。

scroll API

当我们想对结果集进行遍历的时候,例如做全量数据导出时,可以使用 scroll API。scroll API 会创建数据快照,后续的访问将会基于这个快照来进行,所以无法检索新写入的数据。

# 第一次使用 scroll API
POST my_index/_search?scroll=10m
{
  "query": {
    "match_all": {}
  },
  "sort": { "price": "desc" }, 
  "size": 2
}

# 结果
{
  "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF9......==",
  "hits" : {
    "hits" : [
      {
        "_id" : "6",
        "_source" : {
          "book_id" : "4ee82467",
          "price" : 20.9
        }
      },
      ......
    ]
  }
}

如上示例,在第一次使用 scroll API 时需要初始化 scroll 搜索并且创建快照,使用 scroll 查询参数指定本次“查询上下文”(快照)的有效时间,本示例中为 10 分钟

# 进行翻页
POST /_search/scroll                                                    
{
  "scroll" : "5m",   
  "scroll_id" : "FGluY2x1ZGVfY29udGV4dF9......==" 
}

我们把上一次返回结果中的 _scroll_id 值放到本次请求的 scroll_id 字段中,并且指定“查询上下文”的有效时间为 5 分钟。同样此次的返回结果也会带有新的 _scroll_id。

scroll 问题

https://discuss.elastic.co/t/ridiculously-long-scroll-id/12913

PIT

ES 7.10 中引入了 Point In Time 后,scroll API 就不建议被使用了。Point In Time(PIT)是 ES 7.10 中引入的新特性,PIT 是一个轻量级的数据状态视图,用户可以利用这个视图反复查询某个索引,仿佛这个索引的数据集停留在某个时间点上。也就是说,在创建 PIT 之后更新的数据是无法被检索到的。

当我们想要获取、统计以当前时间节点为准的数据而不考虑后续数据更新的时候,PIT 就显得非常有用了。使用 PIT 前需要显式使用 _pit API 获取一个 PID ID:

# 使用 pit API 获取一个 PID ID
POST /my_index/_pit?keep_alive=20m

# 结果
{
  "id": "46ToAwMDaWR5BXV1aWQy......=="
}

如上示例,使用 _pit 接口获取了一个 PIT ID,keep_alive 参数设置了这个视图的有效时长。有了这个 PIT ID 后续的查询就可以结合它来进行了

PIT 可以结合 search after 进行查询,能有效保证数据的一致性。 PIT 结合 search after 的流程与前面介绍的 search after 差不多,主要区别是需要在请求 body 中带上 PIT ID,其示例如下

# 第一次调用 search after,因为使用了 PIT,这个时候搜索不需要指定 index 了。
POST _search
{
  "size": 2,
  "query": { "match_all": {} },
  "pit": {
    "id":  "46ToAwMDaWR5BXV1aWQy......==", # 添加 PIT id
    "keep_alive": "5m" # 视图的有效时长
  },
  "sort": [
    { "price": "desc" } # 按价格倒序排序
  ]
}

# 结果
{
  "pit_id" : "46ToAwMDaWR5BXV1aWQy......==",
  "hits" : {
    "hits" : [
      {
        "_id" : "6",
        "_source" : {
          "book_id" : "4ee82467",
          "price" : 20.9
        },
        "sort" : [20.9, 8589934593]
      },
      {
        "_id" : "1",
        "_source" : {
          "book_id" : "4ee82462"
          "price" : 19.9
        },
        "sort" : [19.9, 8589934592]
      }
    ]
  }
}

在 pit 字段中指定 PIT ID 和设置 keep_alive 来指定视图的有效时长。需要注意的是,使用了 PIT 后也不需要在路径中指定索引名称了

在其返回结果中,sort 数组中包含了两个元素,其中第一个是我们用作排序的 price 的值,第二个值是一个隐含的排序值。所有的 PIT 请求都会自动加入一个隐式的用于排序的字段称为:_shard_doc,当然这个排序值可以显式指定。这个隐含的字段官方也称它为:tiebreaker(决胜字段),其代表的是文档的唯一值,保证了分页不会丢失或者分页结果的数据不会重复,其作用就好像原 search after 的 sort 字段中要指定的唯一值一样。

在进行翻页的时候和原 search after 一样,需要把上次结果中最后一个文档的 sort 值带上:

# 第二次调用 search after,因为使用了 PIT,这个时候搜索不需要指定 index 了。
POST _search
{
  "size": 2,
  "query": {
    "match_all": {}
  },
  "pit": {
    "id":  "46ToAwMDaWR5BXV1aWQy......==", # 添加 PIT id
    "keep_alive": "5m" # 视图的有效时长
  },
  "search_after": [19.9, 8589934592], # 上次结果中最后一个文档的 sort 值
  "sort": [
    { "price": "desc" }
  ]
}

常见问题

精确匹配

忽略大小写

text的创建文档时是会分词并且转成小写的,但是精确匹配keyword是不分词的,也不会转成小写。有时候我们想要忽略大小写的精确匹配

需要在创建索引的时候,设置忽略大小写

{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 0,
    # 1、指定忽略大小写
    "analysis": {
      "normalizer": {
        "lowercase_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "tag": {
        "type": "keyword",
        # 2、设置忽略大小写
        "normalizer": "lowercase_normalizer"
      }
  }
}

使用case_insensitive,仅支持term

{
  "query": {
    "term": {
      "title": {
        "value": "Romeo",
        "case_insensitive": true
      }
    }
  }
}

分词技巧

索引时用ik_max_word(最细粒度划分),在搜索时用ik_smart(最少切分) 即:索引时最大化的将文章内容分词,搜索时更精确的搜索到想要的结果。

{
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "ik_max_word",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

{
  "query": {
    "match": {
      "title": {
        "analyzer": "ik_smart",
        "query": "文章"
      }
    }
  }
}

返回数据具体总条数

track_total_hits参数默认为false,为false时,当搜索到的数据>10000条,则只显示10000条,为true时,可以显示详细条数

返回指定字段

不是所有字段都需要返回,可以指定需要返回的字段

可以使用通配符*

{
  "from": 0,
  "size": 1,
  "query": {},
  "_source": {
    # 指定返回字段
    "includes": [
      "title"
    ],
    # 指定不返回字段
    "excludes": ["name","bir*"]
  }
}

copy_to

Elasticsearch允许在映射中为某个字段定义copy_to参数,以实现复制多个其他字段的内容,这样在搜索一个字段时能够达到同时搜索多个字段的效果,使用字段复制比使用多字段匹配Multi_match性能更好。

{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "copy_to": "titleBrief" # 指定copy_to
      },
      "brief": {
        "type": "text",
        "copy_to": "titleBrief" # 指定copy_to
      }
    }
  }
}


{
    {
    "query": {
        "match": {
        "titleBrief": "红"
        }
    }
    }
}

执行性能

Profile API 用于查看 DSL 性能瓶颈。只需要添加 "profile": true 即可查看 DSL 的执行性能

{
  "profile": true,
  "query": {
    "query_string": {
      "query": "tag:(taga) AND tag_type:(A OR b) AND user:(173)"
    }
  }
}

Suggester API搜索提示

ES提供了suggseter api,提供了4种类型的suggseters:Term Suggester(基于单词的纠错补全)、Phrase Suggester(基于短语的纠错补全)、Complete Suggester(自动补全单词,输入词语的前半部分,自动补全单词)、Context Suggseter(基于上下文的补全提示,可以实现上下文感知推荐)

Term Suggester

将输入的文本分解为Token,然后在索引的字段里查找相似的term返回

{
  "query": {
    "match": {
      "database": "posgresql"
    }
  },
  "suggest": {
    "my-term-suggestion": {
      "text": "posgresql",
      "term": {
        "suggest_mode": "missing",
        "field": "database"
      }
    },
    "my-term-suggestion2": {
      "text": "oracla",
      "term": {
        "suggest_mode": "missing",
        "field": "database"
      }
    }
  }
}