Elasticsearch logo

ElasticSearch 是基于 Apache Lucene 的分布式搜索和分析引擎,为所有类型的数据提供近乎实时的搜索和分析。

什么是 Elasticsearch?

数据输入:文档和索引

Elasticsearch 是一个分布式文档存储搜索引擎。Elasticsearch 不会将信息存储为列数据的行,而是存储已序列化为 JSON 文档的复杂数据结构。当集群中有多个 Elasticsearch 节点时,存储的文档会分布在整个集群中,并且可以从任何节点立即访问。

存储文档后,将在 1 秒钟内几乎实时地对其进行索引并可搜索。为了使文本可搜索,传统数据库每个字段存储一个值的方式不足以进行全文搜索。文本字段中的每个单词都需要可搜索,这意味着数据库需要能够在单个字段中索引多个值(在本例中为单词)。最能支持单字段多值要求的数据结构是倒排索引(Inverted index),该结构支持非常快速的全文本搜索。倒排索引列出了出现在任何文档中的每个唯一值,或者词项/单词(term),并标识了每个单词出现的所有文档。

**索引(Index)**可以认为是文档的优化集合,每个文档都是字段 (field) 的集合,这些字段是包含数据的键值对。默认情况下,Elasticsearch 对每个字段中的所有数据建立索引,并且每个索引字段都具有专用的优化数据结构。例如,文本字段存储在倒排索引中,数字字段和地理字段存储在 BKD 树中。使用按字段数据结构组合并返回搜索结果的能力使 Elasticsearch 如此之快。

Elasticsearch 还具有无模式的能力,这意味着无需显式指定如何处理文档中可能出现的每个不同字段即可对文档建立索引。启用动态映射后,Elasticsearch 自动检测并向索引添加新字段。这种默认行为使索引和浏览数据变得容易-只需开始建立索引文档,Elasticsearch 就会检测布尔值,浮点数和整数值,日期和字符串并将其映射到适当的 Elasticsearch 数据类型。

但是,最终,您比 Elasticsearch 更了解您的数据以及如何使用它们。您可以定义规则来控制动态映射,也可以显式定义映射以完全控制字段的存储和索引方式。

定义自己的映射使您能够:

  • 区分全文字符串字段和精确值字符串字段
  • 执行特定于语言的文本分析
  • 优化字段以进行部分匹配
  • 使用自定义日期格式
  • 使用无法自动检测到的数据类型,例如 geo_pointgeo_shape

为不同的目的以不同的方式对同一字段建立索引通常很有用。例如,您可能希望将一个字符串字段索引为全文搜索的文本字段和索引关键字,以便对数据进行排序或汇总(定义映射时对字段使用 fields 定义额外的类型)。或者,您可能选择使用多个语言分析器来处理包含用户输入的字符串字段的内容。

数据输出:搜索和分析

尽管您可以将 Elasticsearch 用作文档存储并检索文档及其元数据,但真正的强大之处在于能够轻松访问基于 Apache Lucene 搜索引擎库构建的全套搜索功能。

Elasticsearch 提供了一个简单、一致的 REST API,用于管理您的集群以及索引和搜索数据。为了进行测试,您可以轻松地直接从命令行或通过 Kibana 中的开发者控制台提交请求。在您的应用程序中,您可以为您选择的语言使用 Elasticsearch 客户端 :Java,JavaScript,Go,.NET,PHP,Perl,Python 或 Ruby。

搜索数据

Elasticsearch REST API 支持结构化查询全文查询结合了两者的复杂查询。结构化查询类似于您可以在 SQL 中构造的查询类型。例如,您可以搜索索引中的 genderage 字段,employee 然后按 hire_date 字段对匹配项进行排序。全文查询会找到所有与查询字符串匹配的文档,并按相关性对它们进行返回(它们与您的搜索词的匹配程度如何)。

除了搜索单个词项(term)外,您还可以执行短语搜索(phrase searches),相似性搜索(similarity searches)和前缀搜索(prefix searches),并获得自动完成建议。

是否要搜索地理空间或其他数字数据?Elasticsearch 在支持高性能地理和数字查询的优化数据结构中索引非文本数据。

您可以使用 Elasticsearch 全面的 JSON 样式查询语言(Query DSL)访问所有这些搜索功能。您还可以构造 SQL样式的查询 以在 Elasticsearch 内部本地搜索和聚合数据,并且 JDBC 和 ODBC 驱动程序使范围广泛的第三方应用程序可以通过 SQL 与 Elasticsearch 进行交互。

可扩展性和弹性:集群、节点和分片

Elasticsearch 旨在始终可用并根据您的需求进行扩展。它通过自然分布来做到这一点。您可以将**服务器(节点 - node)添加到集群(Cluster)**以增加容量,Elasticsearch 会自动在所有可用节点之间分配您的数据和负载查询。无需大修您的应用程序,Elasticsearch 知道如何平衡多节点集群以提供可扩展性和高可用性。节点越多,能力越大。

这是如何运作的?在幕后,Elasticsearch 索引实际上只是一个或多个物理**分片(Shard)**的逻辑分组,其中每个分片实际上是一个独立的索引,它是一个 Lucene 索引的实例。通过将索引中的文档分布在多个分片中,并将这些分片分布在多个节点上,Elasticsearch 可以确保冗余,这既可以防止硬件故障,又可以在将节点添加到集群时增加查询能力。随着集群的增大(或缩小),Elasticsearch 会自动迁移分片以重新平衡集群。

当数据写入分片时,它会定期发布到磁盘上新的不可变 Lucene 片段(Segement)中,此时它可以用于查询。这称为刷新(refresh)。提交(Commiting)一个新的片段到磁盘需要一个 fsync 来确保片段被物理性地写入磁盘,这样在断电的时候就不会丢失数据。 但是 fsync 操作代价很大;如果每次索引一个文档都去执行一次的话会造成很大的性能问题。Elasticsearch 使用介于内存和磁盘之间的文件系统缓存(Page Cache)来临时部分存储提交,并在合适的时间将其冲刷(flush)到磁盘。并增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录,以防在完整提交之前丢失数据。 Elasticsearch:权威指南中更详细地描述了它的工作原理。

随着片段数量的增加,这些片段会定期合并为更大的片段的过程称为合并(merge)。由于所有片段都是不可变的,这意味着使用的磁盘空间通常会在索引期间波动,因为在删除用来替换它们的片段之前需要先创建新的合并段。合并可能会占用大量资源,尤其是在磁盘 I/O 方面。

img-0yzOUKaH-1598160536751

Elasticsearch 分别提供了不同的 API 来方便手动执行相应的操作以最大化优化搜索,refresh 对应于刷新到段中并可被搜索,flush 对应于将文件缓存持久化到磁盘,forcemerge 强制合并片段来减少片段数以加快搜索性能。

有两种类型的分片:主分片副本。索引中的每个文档都属于一个主分片。副本分片是主分片的副本。副本提供数据的冗余副本,以防止硬件故障并增加处理读取请求(如搜索或检索文档)的能力。

索引中的主分片数量在创建索引时是固定的,但副本分片的数量可以随时更改,而不会中断索引或查询操作。

分片是 Elasticsearch 在集群范围分布数据的单位。 Elasticsearch 在重新平衡数据时,例如发生故障后,其移动分片的速度,将取决于分片的大小和数量以及网络和磁盘性能。

分析数据

文本分析是将非结构化文本(例如电子邮件的正文或产品说明)转换为针对搜索优化的结构化格式的过程。当索引或搜索文本(text)字段类型时,Elasticsearch 执行文本分析,然后将其添加到倒排索引中以便进行搜索。

Elasticsearch 对每个分析的文本字段都要经过许多步骤,用来归一化和规范化次元,该过程由 分析器(analyzer) 执行,经历以下步骤:

  • 字符过滤 — 使用字符过滤器(Character filters)转换或过滤字符,单词过滤器有零个或多个,按顺序处理。

  • 分词 — 使用分词器(Tokenizer)将文本分成一个或多个词条(token),将这些令牌存储到索引中

  • 词条过滤 — 词条过滤器(Token filters)接收词条流,并可以添加,删除或更改词条。例如,小写(lowercase)词条过滤器会将所有词条转换为小写,停顿词(stop)元过滤器会从词条流中删除常见的词(停顿词),同义词(synonym)词条过滤器将同义词引入词条流,可以有零个或多个,按顺序处理

  • 词条索引 — 将这些词条存储到倒排索引中。

elasticsearch-analyzer

对于不同的分析器具体的分词效果和结果,我们可以使用 Analyze API 来参考,例如查看某个索引对文本使用 hanlp 分词器的分词效果:

POST /index_name/_analyze
{
  "text": "美国阿拉斯加州发生8.0级地震",
  "tokenizer": "hanlp"
}

内置分析器

Elasticsearch 附带了广泛的内置分析器,无需进一步配置即可在任何索引中使用:

  • Standard Analyzer

    默认分词器,由 Unicode 文本分割算法定义边界拆分单词,它删除了大多数标点符号、小写单词并支持删除停用词。

  • Simple Analyzer

    只要遇到不是字母的字符,简单分析器就会将文本划分为单词,转小写。

  • Stop Analyzer

    和简单的分析器一样,但也支持去除停用词。

  • Whitespace Analyzer

    每当遇到任何空白字符时,空白分析器都会将文本分成单词。不会转小写。

  • Keyword Analyzer

    关键字分析器是一个“noop”分析器,它接受给定的任何文本并输出完全相同的文本作为单个词。

  • Pattern Analyzer

    模式分析器使用正则表达式拆分单词,默认 \W+ (非字符分割),支持小写和停用词。

  • Language Analyzers

    提供了 30 多种常见语言的分词器,不包括中文。

  • Fingerprint Analyzer

    fingerprint 指纹分析仪是一种专业分析仪,可创建可用于重复检测的指纹。

中文分析器

内置的语言分析器并不包含中文分析器,但官方提供了中文分析器 - smartCN 插件来提供中文或中英文混合的分析,但是分词效果却很糟糕。

除了官方的的中文分词器,还有一些开源常用的分词器:

  • IKAnalyzer

    免费开源的 Java 分词器,目前比较流行的中文分词器之一,简单、稳定,想要特别好的效果,需要自行维护词库,支持自定义词典。

  • jieba/结巴分词

    开源的 python 分词器,github 有对应的 Java 版本,有自行识别新词的功能,支持自定义词典。

  • Ansj 中文分词

    基于 n-Gram+CRF+HMM 的中文分词的 Java 实现,免费开源,支持应用自然语言处理,用户自定义词典、关键字提取、自动摘要、关键字标记等功能。

  • HanLP

    免费开源,使用自然语言处理,基于 PyTorch 和 TensorFlow 2.x 双引擎,支持新词发现、关键词短语提取、自动摘要、文本分类聚类、拼音简繁转换等多种特性。

对以上分词器进行了一个粗略对比:

分词器优势劣势
Smart Chinese Analysis官方插件中文分词效果惨不忍睹
IKAnalyzer简单易用,支持自定义词典和远程词典词库需要自行维护,不支持词性识别
结巴分词新词识别功能不支持词性识别
Ansj中文分词分词精准度不错,支持词性识别对标hanlp词库略少,学习成本高
Hanlp目前词库最完善,支持的特性非常多需要更优的分词效果,学习成本高

截止到目前为止,他们的分词准确性从高到低依次是:

hanlp > ansj > 结巴 > IK > Smart Chinese Analysis

结合准确性来看,选用中文分词器基于以下考虑:

官方的 Smart Chinese Analysis 直接可以不考虑了;

对搜索要求不高的建议选用 IK 学习成本低,使用教程多,还支持远程词典;

对新词识别要求高的选用结巴分词;

Ansj 和 HanLP 均基于自然处理语言,分词准确度高,活跃度来讲 HanLP 略胜一筹;

倒排索引

倒排索引的结构,允许非常快速的全文搜索。倒排索引由出现在任何文档中的所有唯一词的列表以及每个词出现的文档列表组成。

例如,假设我们有两个文档,每个文档都有一个包含以下内容的内容字段:

  1. The quick brown fox jumped over the lazy dog
  2. Quick brown foxes leap over lazy dogs in summer

要创建倒排索引,我们首先将每个文档的内容字段拆分为单独的单词(我们称之为词项-term词条-token),经过上面的文本分析归一化处理,创建得到所有唯一词条的排序列表,然后列出每个词项出现在哪个文档中。结果可能如下所示:

Term      Doc_1  Doc_2
-------------------------
quick   |       |  X
brown   |   X   |  X
dog     |   X   |  X
fox     |   X   |  X
in      |       |  X
jumpe   |   X   |
lazy    |   X   |  X
leap    |       |  X
over    |   X   |  X
quick   |   X   |
summer  |       |  X
------------------------

现在,如果我们想搜索 quick brown,我们只需要找到每个词出现的文档:

Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
quick   |   X   |
------------------------
Total   |   2   |  1

两个文档都匹配,但第一个文档比第二个文档有更多的匹配。如果我们应用一个简单的相似度算法,只计算匹配项的数量,那么我们可以说第一个文档比第二个文档更匹配——与我们的查询更相关 - relevant

映射 Mapping

在查询之前我们需要先了解 Elasticsearch 如何映射(Mapping)我们的文档结构。映射与数据库模式(Schema)类似,描述了该类型文档可能具有的字段或属性、每个字段的数据类型(例如字符串、整数或日期)以及 Lucene 应如何索引和存储这些字段。

例如获取 gb 索引中的 tweet 类型的映射(或模式定义):

GET /gb/_mapping/tweet

将得到如下结果:

{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "strict_date_optional_time||epoch_millis"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}

在使用无模式动态映射的行为时 Elasticsearch 会根据它对我们字段类型的猜测为我们动态生成了映射,不同类型的数据数据索引方式可能略有不同,最大的区别在于表示精确值的字段(可以包括字符串字段)和表示全文的字段之间,这种区别非常重要——它是将搜索引擎与所有其他数据库区分开来的东西。

或者我们可以自定义的对不同字段严格定义一种或多种类型和属性,自定义映射允许我们执行以下操作:

  • 区分全文字符串字段和精确值字符串字段
  • 使用特定于语言的分析器
  • 优化字段以进行部分匹配
  • 指定自定义日期格式
  • 以及更多

字段最重要的属性是类型(type)。对于字符串字段以外的字段,我们很少需要映射除类型以外的任何内容。

默认情况下,字符串类型的字段被认为包含全文。也就是说,它们的值将在被索引之前通过分析器,并且字段上的全文查询在搜索之前也会将查询字符串通过分析器。

string 字段的两个最重要的映射属性是 indexanalyzer

index

index 属性控制字符串的索引方式。它可以包含以下三个值之一:

analyzed

​ 首先分析字符串,然后对其进行索引。换句话说,将此字段索引为全文。

not_analyzed

​ 索引此字段,因此它是可搜索的,但索引值与指定值完全一致。不会分析它。

no

​ 根本不索引这个字段。该字段将不可搜索。

string 字段的 index 属性默认值为 analyzed。如果我们想将该字段映射为精确值,我们需要将其设置为 not_analyzed

{
    "tag": {
        "type":     "string",
        "index":    "not_analyzed"
    }
}

analyzer

对于 indexanalyzed 的字符串字段,使用 analyzer 属性来指定在搜索时和索引时应用哪个分析器。默认情况下,Elasticsearch 使用 standard 标准分析器,但可以通过指定其它内置分析器或自定义分析器来更改它,例如 whitespacesimple,或 english

搜索

Query DSL

Elasticsearch 提供了基于 JSON 的完整 Query DSL(Domain Specific Language)来定义查询。将查询 DSL 视为查询的 AST(抽象语法树),由两种类型的子句组成:

  • 叶查询子句

    叶查询子句在特定字段中查找特定值,例如 matchtermrange 查询。这些查询可以单独使用。

  • 复合查询子句

    复合查询子句包装其他叶复合查询,并用于以逻辑方式组合多个查询(例如 bool or dis_max 查询),或改变它们的行为(例如 constant_score 查询)。

查询子句的行为不同,具体取决于它们是在 查询上下文还是过滤器上下文中使用。

可以通过将设置的值 search.allow_expensive_queries 设置为 false(默认为 true)来阻止执行此类查询。

查询和过滤上下文

相关性分数 - Relevance scores

默认情况下,Elasticsearch 按相关性分数对匹配的搜索结果进行排序,相关性分数衡量每个文档与查询的匹配程度。

相关性分数是一个正浮点数,界于 0.0 和 1.0 之间,高于 1.0 代表相关性更高。 在 Search API 的 _score 元数据字段中返回。值越高 ,文档越相关。虽然每种查询类型可以不同地计算相关性分数,但分数计算还取决于查询子句是在查询还是过滤上下文中运行。

查询上下文 - Query context

在查询上下文中,查询子句回答了“该文档与该查询子句匹配程度如何?” 除了判断文档是否匹配之外,查询子句还计算 _score 元数据字段中的相关性分数

只要将查询子句传递给 query 参数(例如 Search API query 中的参数),查询上下文就会生效。

过滤上下文 - Filter context

在过滤器上下文中,查询子句回答“此文档是否匹配此查询子句?” 答案很简单,是或否 —— 不计算分数。过滤上下文主要用于过滤结构化数据,例如:

  • timestamp 是否属于 2015 年至 2016 年的范围?
  • status 字段是否设置为 "published"

Elasticsearch 会自动缓存常用的过滤器,以提高性能。

只要将查询子句传递给 filter 参数(例如 bool 查询中的 filtermust_not 参数,constant_score 查询中的 filter 参数或聚合中的 filter 参数),过滤上下文就会生效。

查询和过滤上下文示例

下面是在 search API 的查询和过滤上下文中使用的查询子句示例。此查询将匹配满足以下所有条件的文档:

  • title 字段包含单词 search
  • content 字段包含单词 elasticsearch
  • status 字段包含确切的单词 published
  • publish_date 字段包含从 2015 年 1 月 1 日起的日期。
GET /_search
{
  "query": { ①
    "bool": { ②
      "must": [
        { "match": { "title":   "Search"        }},
        { "match": { "content": "Elasticsearch" }}
      ],
      "filter": [ ③
        { "term":  { "status": "published" }},
        { "range": { "publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}

① 该 query 参数表示查询上下文。

bool 和两个 match 子句用于查询上下文,这意味着它们用于对每个文档的匹配程度进行评分。

filter 参数表示过滤上下文。它的 termrange 子句用于过滤上下文。他们将过滤掉不匹配的文档,但不会影响匹配文档的分数。

复合查询

复合查询包装其他复合查询或叶查询,以组合它们的结果和分数,改变它们的行为,或者从查询切换到过滤上下文。

该类型的查询包括:

  • bool 查询

    用于组合多个叶或复合查询子句的默认查询,如 mustshouldmust_notfilter 子句。

    mustshould 子句的分数相结合 — 匹配的子句越多越好,must 相当于 SQL 中的 AND,should 相当于 OR。

    must_notfilter 子句在过滤上下文中执行。

  • boosting 查询

    返回匹配 positive 查询的文档,如果匹配到 negative 查询的文档会降低相关性分数。

  • constant_score 查询

    包装另一个 filter 查询,在过滤器上下文中执行它的查询。所有匹配的文档都被赋予相同的“常量” _score

  • dis_max 查询

    接受多个查询并返回与任何查询子句匹配的任何文档的查询。bool 查询结合了所有匹配查询的分数,但 dis_max 查询使用单个最佳匹配查询子句的分数。

  • function_score 查询

    使用函数修改主查询返回的分数,以考虑流行度、新近度、距离或使用脚本实现的自定义算法等因素。

全文查询

全文查询使您能够搜索分析的文本字段,例如电子邮件正文。使用在索引期间应用于字段的相同分析器处理查询字符串。

该类型的查询包括:

  • intervals 查询

    允许对匹配项的排序和接近度进行细粒度控制的全文查询。

  • match 查询

    用于执行全文查询的标准查询,包括模糊匹配和短语或邻近查询。

  • match_bool_prefix 查询

    分析其输入并根据这些词构造一个 bool 查询。除了最后一个词之外的每个词都是用 term 查询,最后一个词使用 prefix 前缀查询。

  • match_phrase 查询

    match 查询类似,但用于匹配确切的短语或单词邻近匹配。

  • match_phrase_prefix 查询

    match_phrase 查询类似,但对最终单词进行通配符搜索。

  • multi_match 查询

    查询的多字段版本 match

  • combined_fields 查询

    匹配多个字段,就好像它们已被索引到一个组合字段中一样。

  • query_string 查询

    支持紧凑的 Lucene 查询字符串语法,允许您在单个查询字符串中指定 AND|OR|NOT 条件和多字段搜索。仅供专家用户使用。

  • simple_query_string 查询

    更简单、更健壮的 query_string 语法版本,适合直接向用户公开。

Term 词项级别查询

您可以使用词项级别查询根据结构化数据中的精确值查找文档。结构化数据的示例包括日期范围、IP 地址、价格或产品 ID。

全文查询不同,词级查询不分析搜索词。相反,词项级别的查询匹配存储在字段中的确切单次。

词项级别查询仍然使用 normalizer 属性对 keyword 字段的搜索词进行规范化。有关 normalizer 详细信息,请参阅 normalizer

该类型的查询包括:

对比 SQL

我们以 4.3.3 版本的 Spring Data Elasticsearch 中的方法查询关键字为例说明对应的 QueryDSL 语法:

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
OrfindByNameOrPrice{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
IsfindByName{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
NotfindByNameNot{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
BetweenfindByPriceBetween{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThanfindByPriceLessThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqualfindByPriceLessThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThanfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqualfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
BeforefindByPriceBefore{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
AfterfindByPriceAfter{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
LikefindByNameLike{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWithfindByNameStartingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWithfindByNameEndingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/ContainingfindByNameContaining{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
InfindByNameIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
InfindByNameIn(Collection<String>names){ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}
NotInfindByNameNotIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NotInfindByNameNotIn(Collection<String>names){"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}
TruefindByAvailableTrue{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
FalsefindByAvailableFalse{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderByfindByAvailableTrueOrderByNameDesc{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }
ExistsfindByNameExists{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsNullfindByNameIsNull{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}
IsNotNullfindByNameIsNotNull{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsEmptyfindByNameIsEmpty{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}
IsNotEmptyfindByNameIsNotEmpty{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}

上面的查询部分基于 query_string 查询,对应于简单查询如下:

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
OrfindByNameOrPrice{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
IsfindByName{"bool" : {"must" : {"field" : {"name" : "?"}}}}
NotfindByNameNot{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
LikefindByNameLike{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWithfindByNameStartingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWithfindByNameEndingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/ContainingfindByNameContaining{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
InfindByNameIn(Collection<String>names){"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotInfindByNameNotIn(Collection<String>names){"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
TruefindByAvailableTrue{"bool" : {"must" : {"field" : {"available" : true}}}}
FalsefindByAvailableFalse{"bool" : {"must" : {"field" : {"available" : false}}}}
OrderByfindByAvailableTrueOrderByNameDesc{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

使用 SQL 查询

X-Pack 包含 SQL 功能,可以针对 Elasticsearch 索引执行 SQL 查询并以表格格式返回结果。

它支持基本的 SQL 语法和函数,并支持通过命令行 CLI、REST 接口、JDBC、ODBC 查询数据并指定返回格式。我们也可以使用 Translate API 将 SQL 转换为 QueryDSL JSON 格式的本地查询。

更多信息参考官方文档:X-Pack SQL

监控和管理

Kibana

Elasticsearch 是称为 Elastic Stack 的产品集合中的主要数据源组件。该 Stack 中的每个组件都负责监控自身并将结果报告给负责监控的 Elasticsearch 集群。建议在不同的集群中收集指标,尤其是当您在生产环境中运行 Elasticsearch 集群时。

Elasticsearch 有一套很全面的 API,可以查询并接收有关集群、节点和任务的健康状况的信息,通过调用相应的端点来获取相关信息,在 Elasticsearch Reference 文档中有详细的示例和说明。

GET /_cluster/health

{
    "cluster_name": "elasticsearch",
    "status": "yellow",
    "timed_out": false,
    "number_of_nodes": 1,
    "number_of_data_nodes": 1,
    "active_primary_shards": 30,
    "active_shards": 30,
    "relocating_shards": 0,
    "initializing_shards": 0,
    "unassigned_shards": 30,
    "delayed_unassigned_shards": 0,
    "number_of_pending_tasks": 0,
    "number_of_in_flight_fetch": 0,
    "task_max_waiting_in_queue_millis": 0,
    "active_shards_percent_as_number": 50
}

如果不太习惯处理 JSON 响应,Elastic 团队还提供了一组 cat API,这些 API 提供了类似的数据,但格式更加用户友好。这些在这里都有很好的记录。

GET /_cat/allocation?v

shards disk.indices disk.used disk.avail disk.total disk.percent host       ip         node
    30       31.3mb    88.6gb    162.2gb    250.9gb           35 172.20.0.2 172.20.0.2 QUiFe7tUNASSIGNED                                                                                

Elastic Stack Features(以前称为 X-Pack)是当前的 Elastic 扩展,它处理 Stack 的监控以及安全、警报和报告等其他功能。X-Pack 最初是作为具有免费和付费功能组合的高级产品发布的。2018 年,Elastic 决定在Elastic 许可证下开放 X-Pack 的代码库,虽然监控等功能仍然免费,但其他功能仍然需要您在使用前购买许可证。或者,您也可以研究一些Elastic Stack 功能(以前称为 X-Pack)替代方案

在安装 X-Pack 之前,你需要拥有 Elasticsearch、Kibana 和你计划使用的任何其他 Elastic 组件。使用 Elasticsearch 安装 X-Pack 在Elastic 文档中有详细记录。所需的安装步骤包括:配置安全协议、重新启动集群和设置密码访问。

完全配置 X-Pack 后,你可以在 Kibana 仪表板上显示报告的指标。Kibana 提供了一个平台来构建清晰的可视化并设置警报和监视,以便让你知道你的指标何时开始指示集群存在问题。你可以在 Elastic 文档中了解有关设置仪表板的更多信息。

ElasticHQ

ElasticHQ 是一个开源的 Elasticsearch 监控解决方案,由 Elasticsearch in Action 的合著者 Roy Russo 发起。该工具基于 Python 项目可以从 GitHub 获得,也可以通过 Docker Hub 的 Docker Image 获得。

elasticsearch监控工具回顾

它可以一次管理多个集群,监控节点、索引、分片和一般集群指标,创建和维护索引,一键访问 ES API 和 cat API 端点,易于使用的查询功能, 复制映射和重新索引,重要指标的实时监控图表等等特性。

该项目从 19 年开始不再维护。

Cerebro

在早期与 Elasticsearch 相关的博客和文章中,Elasticsearch Kopf 作为开源监控有着突出特点。不幸的是,该项目仅支持 Elasticsearch 到 2.0 版本,并且自 2015 年底以来一直没有更新。

作为下一次迭代,Cerebro 在 Elasticsearch 上支持更高版本。该项目可从其 GitHub 中获得,该项目需要 Java 11 运行时,并且是使用 Scala 和 Angular 的组合编写的。也可以从 docker hub repo 获取 Docker Image。

Cerebro

该项目支持的特性和 ElasticHQ 差不多。

该项目的文档仅限于 README 页面,该页面指定如何运行该工具,以及如何使用该工具构建和运行 Docker 映像。在撰写本文时,最后一个稳定版本是在 2021 年 4 月。

ElasticSearch Head

该实用程序在 GitHub 上可见,本文撰写时,最后一个稳定版本是在 2018 年 4 月。该工具是用 Node.js 编写的,可以以以下形式运行:

  • Built-in server
  • Docker
  • Chrome 扩展

项目组织者还提供了一个基本网站,其中包含安装说明和各种屏幕截图。

best open source elasticsearch monitoring tools

参考