假如我有必要用一句话来描绘 Elasticsearch,我会这样说:

当查找遇到大规模剖析时(近乎实时)

Elasticsearch 是目前最受欢迎的 10 大开源技能之一。 公平地说,它包括许多自身并不共同的要害功用,但是,结合运用它能够成为最好的查找引擎/剖析平台。
更精确地说,Elasticsearch 之所以如此受欢迎,是因为结合了以下特性:

  • 运用相关性评分进行查找
  • 全文查找
  • 剖析(聚合)
  • 无形式(shemaless 对数据形式没有约束)、NoSQL、面向文档
  • 丰富的数据类型挑选
  • 水平可扩展
  • 容错的

当咱们运用 Elasticsearch时,咱们很快意识到官方文档看起来更像是从应该称为文档的内容中 “挤压” 出来的,尽管官方的文档在许多方面都是很不错的。 咱们有时不得不处处查找和运用 stackowerflowing。

在本文中,我将首要介绍查询/查找 Elasticsearch 集群。 有许多不同的办法能够实现或多或少相同的成果,因而,我将测验解说每种办法的优缺点。
更重要的是,我将向你介绍两个重要的概念 —— 查询(query)和过滤(filter)上下文 —— 文档中没有很好地解说它们。 我会给你一套规矩,告诉你什么时候最好运用哪种办法。假如我希望您在阅览完本文后记住一件事,那就是:

你真的需求在查询时对文档进行评分吗?

Query 上下文与 filter 上下文

当咱们议论 Elasticsearch 时,总会有一个相关性分数。 相关性分数是一个严厉的正浮点数,表明每个文档满意查找条件的程度。 该分数是相关于分配的最高分数而言的,因而,分数越高,文档与查找规范的相关性越好。

但是,过滤器(filter)和查询(query)是两个不同的概念,你在编写查询之前应该能够理解它们。一般来说,过滤器上下文是一个是/否选项,其间每个文档都与查询匹配或不匹配。 一个很好的例子是 SQL WHERE 后跟一些条件。 SQL 查询总是回来与条件严厉匹配的行。 SQL 查询无法回来不明确的成果。

过滤器会主动缓存,不会影响相关性得分。

另一方面,Elastisearch 查询(query)上下文向你显示每个文档与你的要求的匹配程度。 为此,查询运用剖析器来查找最佳匹配。经历规律是:

将过滤器用于

  • 是/否查找
  • 查找精确值(数字、规模和要害字)

将查询用于:

  • 不置可否的成果(一些文档比其他文档更受关注)
  • 全文查找

除非你需求相关性分数或全文查找,否则请一直测验运用过滤器。 过滤器 “更便宜”。

此外,Elasticsearch 会主动缓存过滤器的成果。

在第 1 部分和第 2 部分中,我将讨论查询(能够转化为过滤器)。 请不要如下的将结构化与全文与查询与过滤器混淆 —— 这是两个不同的东西。

1)结构化查询

结构化查询也称为术语级(term-level)查询,是一组查看是否应挑选文档的查询办法。 因而,在许多状况下并不真实需求相关性分数 —— 文档要么匹配要么不匹配(尤其是数字)。

术语级查询仍然是查询,因而它们将回来分数。

术语查询(term query)

回来字段值与条件彻底匹配的文档。 术语查询在某种程度上是 SQL select * from table_name where column_name =…

术语查询直接进入倒排索引,这使得它很快。 在处理文本数据时,最好只对 keyword 字段运用术语(term)。


1.  GET /_search
2.  {
3.      "query": {
4.          "term": {
5.              "<field_name>": {
6.                  "value": "<your_value>"
7.              }
8.          }
9.      }
10.  }

默许状况下,术语查询在查询上下文中运转,因而,它将核算分数。 即便回来的一切文档的分数都相同,也会涉及额外的核算才能。

带过滤器的术语查询(term query)

假如咱们想加速术语查询并将其缓存起来,那么它应该包括在一个 constant_score 过滤器中。还记得经历规律吗? 假如你不关心相关性分数,请运用此办法。


1.  GET /_search
2.  {
3.      "query": {
4.          "constant_score" : {
5.              "filter" : {
6.                  "term" : {"<field_name>" : "<your_value>"}
7.              }
8.          }
9.      }
10.  }

现在,查询不核算任何相关性得分,因而速度更快。 此外,它会主动缓存。

快速主张 —— 在 text 字段中运用 match 而不是 term。

请记住,术语查询直接指向倒排索引。 Term query 获取你供给的值并按原样查找它,这就是为什么它非常合适查询未经任何转化而存储的 keyword 字段。

Terms query

正如你可能现已猜到的那样,术语查询答应你回来与至少一个确切术语匹配的文档。术语查询在某种程度上是 select * from table_name where column_name is in…

重要的是要了解 Elasticsearch 中的查询字段可能是一个列表,例如 { “name” : [“Odin”, “Woden”, “Wodan”] }。 假如你执行包括以下称号之一的术语查询,那么这条记录将被匹配 —— 它不必匹配字段中的一切值,而只需匹配一个。


1.  GET /_search
2.  {
3.      "query" : {
4.          "terms" : {
5.              "name" : ["Frigg", "Odin", "Baldr"]
6.          }
7.      }
8.  }

Terms set query

与术语查询相同,但这次你能够指定查询字段中应包括多少个确切术语。

你指定有必要匹配的数量 —— 一个、两个、三个或悉数。 但是,这个数字是另一个数字字段。 因而,每个文档都应包括此编号(特定于此特定文档)。关于这个个查找,详细描绘请参阅文章 “开始运用 Elasticsearch (2)” 中的描绘。

Range query

回来查询字段值在界说规模内的文档。等效于 SQL select * from table_name where column_name is between…

规模查询有自己的语法:

  • gt 大于
  • gte 大于或等于
  • lt 小于
  • lte 小于或等于

字段值应 ≥ 4 且 ≤ 17 的示例:


1.  GET _search
2.  {
3.      "query": {
4.          "range" : {
5.              "<field_name>" : {
6.                  "gte" : 4,
7.                  "lte" : 17
8.              }
9.          }
10.      }
11.  }

规模查询也适用于日期。

正则表达式、通配符和前缀查询

Regexp 查询回来字段与你的正则表达式匹配的文档。假如你从未运用过正则表达式,那么我强烈主张你至少了解一下它是什么以及何时能够应用它

Elasticsearch 的正则表达式是 Lucene 的正则表达式。 它具有规范的保存字符和运算符。 假如你现已运用过 Python 的 re 包,那么在这里运用它应该不是问题。 唯一不同的是 Lucene 的引擎不支持 ^ 和 $ 等 anchor 操作符。你能够在官方文档中找到正则表达式的完好列表。

除了正则表达式查询之外,Elsticsearch 还有通配符和前缀查询。 从逻辑上讲,这两个仅仅正则表达式的特例。不幸的是,我找不到关于这 3 个查询的功用的任何信息。

Exists query

因为 Elasticsearch 是无形式的(或没有严厉的形式约束),当不同的文档具有不同的字段时,这是一种相当普遍的状况。 因而,了解文档是否具有任何特定字段有许多用处。

Exists query 回来包括字段索引值的文档


1.  GET /_search
2.  {
3.      "query": {
4.          "exists": {
5.              "field": "<your_field_name>"
6.          }
7.      }
8.  }

2)全文查询

全文查询适用于非结构化文本数据。 全文查询运用分词器。 因而,我将简要介绍一下 Elasticsearch 的剖析器,以便咱们更好地剖析全文查询。Elasticsearch 的分词器管道每次将文本类型数据刺进 Elasticsearch 索引时,都会对其进行剖析,然后存储在倒排索引中。 依据你配置分词器的方法将影响你的查找功用,因为分词器也适用于全文查找。

管道分词器由三个阶段组成:

Elasticsearch:深入理解 Elasticsearch 查询:过滤器查询 vs 全文搜索

总是有一个分词器和零个或多个字符和分词过滤器。

1)Character filter按原样接收文本数据,然后它可能会在数据被 tokenizer 之前对数据进行预处理。 Character filter 用于:

  • 替换匹配给定正则表达式的字符
  • 替换匹配给定字符串的字符
  • 净化 HTML 文本
  1. Tokenizer 将 character filter(假如有)后收到的文本数据分解为 token。 例如,whitespace tokenizer 仅仅经过空格来打断文本(它不是规范的)。 因而,Wednesday is called after Woden。 将拆分为 [Wednesday, is, called, after, Woden.]。 有许多内置分词器可用于创立自界说剖析器。

Standard analyzer 在删去标点符号后按空格分隔文本。 关于绝大多数言语来说,它是最中性的挑选。除了 tokenization之外,tokenizer 还执行以下操作:

  • 盯梢 token 次序,
  • 留意每个单词的开始和结束
  • 界说 token 的类型

3)Token filter 对 token 应用一些转化。 你能够挑选将许多不同的 token 过滤器添加到您的剖析器中。 一些最受欢迎的是:

  • lowercase 小写
  • stemmer 词干剖析器(存在于多种言语中!)
  • 删去重复项
  • 转化为 ASCII 等价物
  • 形式的解决办法
  • token 数量约束
  • token 的中止列表(从中止列表中删去令牌)

假如你对 analyzer 不是很熟的话,请阅览我之前的文章 “Elasticsearch: analyzer”。

现在,当咱们知道剖析器由什么组成时,咱们可能会考虑怎么处理咱们的数据。 然后,咱们能够经过挑选合适的组件来组成一个最合适咱们事例的剖析器。 能够在每个字段的基础上指定分词器。

咱们现在有足够的理论,让咱们看看默许分词器是怎么工作的。

Standard analyzer 是默许的。 它有 0 个字符过滤器、规范标记器、小写和中止标记过滤器。 你能够依据需求编写自界说剖析器,但也有一些内置分词器。
一些最高效的开箱即用剖析器是言语分词器,它们运用每种言语的细节来进行更高档的转化。 因而,假如你事先知道数据的言语,我主张你从 standard analyzer 切换到其间一种数据言语。

全文查询将运用与索引数据时运用的剖析器相同的分词器。 更精确地说,你的查询文本将与查找字段中的文本数据进行相同的转化,因而两者处于同一等级。

Match query

Match query 是查询文本字段的规范查询。咱们能够将 match query 称为 term query 的等效项,但用于 text 类型字段(而在处理文本数据时,术语应仅用于 keyword 类型字段)。


1.  GET /_search
2.  {
3.    "query" : {
4.      "match" : {
5.        "<text_field>" {
6.          "query" : "<your_value>"
7.        }
8.      }
9.    }
10.  }

传递到 query 参数(必填)的字符串,默许状况下,将由与应用于查找字段的剖析器相同的分词器进行处理。 除非你运用剖析器参数自己指定分词器。
当你指定要查找的短语时,系统会对它进行剖析,成果一直是一组 token。 默许状况下,Elasticsearch 将在一切这些标记之间运用 OR 运算符。 这意味着至少应该有一个匹配 —— 不过更多的匹配会得到更高的分数。 你能够在运算符参数中将其切换为 AND。 在这种状况下,有必要在文档中找到一切 token 才能回来。

假如你想在 OR 和 AND 之间有一些东西,你能够指定 minimum_should_match 参数,它指定应该匹配的子句数。 它能够指定为数字和百分比。

fuzziness 参数(可选)答应您省略拼写错误。 Levenshtein 间隔用于核算。

假如你将匹配查询应用于 keyword 字段,那么它将执行与术语查询相同的操作。 更有趣的是,假如将存储在倒排索引中的 token 的确切值传递给 term query,那么它将回来与 match query 彻底相同的成果,但速度更快,因为它会直接进入倒排索引。

Multi-match query

Multi-match query 与 match 的效果相同,唯一的区别是它应用于多个字段。


1.  GET /_search
2.  {
3.    "query": {
4.      "multi_match" : {
5.        "query":    "<your_value>", 
6.        "fields": [ "<text_field1>", "<text_field2>" ] 
7.      }
8.    }
9.  }
  • 能够运用 wildcard 指定字段称号
  • 默许状况下每个字段都具有相同的权重
  • 每个字段对分数的奉献都能够进步
  • 假如 fields 参数中没有指定字段,则将查找一切符合条件的字段

有不同类型的 multi_match。 我不打算在这篇文章中描绘它们,但我会解说最盛行的:

best_fields 类型(默许)更喜爱在一个字段中找到来自查找值的 token 的成果,而不是查找标记在不同字段之间拆分的成果。

phrase 类型的行为与 best_fields 类似,但查找与 match_phrase 类似的整个短语。

我强烈主张你阅览官方文档以查看每个字段的分数是怎么精确核算的。

Boolean query

Boolean query 将其他查询组合在一起。 它是最重要的复合查询。Boolean query 答应你将查询上下文中的查找与过滤上下文查找结合起来。
Boolean query 有四种能够组合在一起的出现(类型):

  • must 或 “有必要满意条款”
  • should 或 “假如满意子句,则对相关性分数加分”
  • filter 或 “有必要满意条款但不核算相关性分数”
  • must_not 或 “与 must 相反,对相关性分数没有奉献”

must 和 should → 查询上下文

filter 和 must_not → 过滤上下文

关于熟悉 SQL 的人来说,must 是 AND 而 should 是 OR 运算符。 因而,有必要满意 must 子句中的每个查询

Boosting query

Boosting query与大多数查询的提升参数类似,但并不相同。 Boosting query 回来匹配 positive 子句的文档并下降匹配 negative 子句的文档的分数。

Constant score query

正如咱们之前在 term query 示例中看到的,constant_score 查询将任何查询转化为相关性得分等于 boost 参数(默许为 1)的过滤器上下文。

总结

总而言之,Elasticsearch 现在合适许多用处,有时很难理解什么是最好的工具。我希望你记住的首要事情是,你并不总是需求运用最高档的功用来解决简略的问题。

假如你不需求相关性分数来检索你的数据,请测验切换到过滤器上下文。

此外,了解 Elasticsearch 的底层工作原理也很重要,因而我主张你充分了解分词器的功用。Elasticsearch 中有更多的查询类型。 我企图描绘最常用的。 我希望你喜爱它。

更多阅览:开始运用 Elasticsearch (2)