使用查询解析器来解析其内容的查询。下面是一个例子:

1
2
3
4
5
6
7
8
9
GET /_search
{
"query": {
"query_string": {
"default_field": "content",
"query": "this AND that OR thus"
}
}
}

query_string 查询解析输入并在运算符周围分割文本。每个文本部分彼此独立地分析。例如以下查询:

1
2
3
4
5
6
7
8
9
GET /_search
{
"query": {
"query_string": {
"default_field": "content",
"query": "(new york city) OR (big apple)"
}
}
}

将分成 new york citybig apple,然后通过为该字段配置的分析器独立地分析每个部分。

空格不被视为运算符,这意味着 new york city 将 “按原样” 传递给为该字段配置的分析器。如果该字段是关键字字段,则分析器将创建单个术语 new york city,并且查询构建器将在查询中使用此术语。如果要分别查询每个术语,则需要在术语周围添加显式运算符(例如 new AND york AND city)。

当提供多个字段时,也可以修改如何使用类型参数在每个文本部分内组合不同字段查询。这里描述了可能的模式,默认是 Bestfield。query_string 顶级参数包括:

  • query: 要解析的实际查询。参见查询字符串语法
  • default_field: 如果未指定前缀字段,则查询字词的默认字段。默认为 index.query.default_field 索引设置,而索引设置默认为 *.* 提取映射中符合术语查询条件的所有字段,并过滤元数据字段。然后组合所有提取的字段以在没有提供前缀字段时构建查询。
  • default_operator: 如果未显式指定运算符,则使用默认运算符。例如,使用默认运算符 OR,查询 capital of Hungary 将转换为 capital OR of OR Hungary,并且使用默认运算符 AND,将相同的查询转换为 capital AND of AND Hungary。默认值为 OR。
  • analyzer:用于分析查询字符串的分析器名称。
  • quote_analyzer:分析器的名称,用于分析查询字符串中的引用短语。对于这些部件,它将覆盖使用 analyzer 参数或 search_quote_analyzer 设置设置的其他分析器。
  • allow_leading_wildcard: 设置时,* 或 ? 允许作为第一个字符。默认为 true。
  • enable_position_increments:设置为 true 在结果查询中启用位置增量。默认为 true。
  • fuzzy_max_expansions:控制模糊查询将扩展到的术语数。默认为 50
  • fuzziness:设置模糊查询的模糊性。默认为 AUTO。
  • fuzzy_prefix_length:设置模糊查询的前缀长度。默认是 0.
  • fuzzy_transpositions:设置为 false 禁用模糊转置(ab->ba)。默认是 true。
  • phrase_slop: 设置短语的默认斜率。如果为 0,则需要精确的短语匹配。默认值是 0。
  • boost:设置查询的提升值。默认为 1.0。
  • auto_generate_phrase_queries:默认为 false。
  • analyze_wildcard: 默认情况下,不分析查询字符串中的通配符。通过将此值设置为 true,将尽最大努力分析这些值。
  • max_determinized_states: 限制允许创建的 regexp 查询的自动机状态数。这可以防止太难(例如指数级)的 regexp。默认为 10000。
  • minimum_should_match: 一个值,用于控制生成的布尔查询中应该匹配的 "should" 子句的数量。它可以是绝对值(2),百分比(30%)或两者的组合。
  • lenient:如果设置为 true 将导致基于格式的失败(如向数字字段提供文本)将被忽略。
  • time_zone: 时区应用于与日期相关的任何范围查询。
  • quote_field_suffix: 附加到查询字符串的引用部分的字段的后缀。这允许使用具有不同分析链的字段进行精确匹配。
  • auto_generate_synonyms_phrase_query: 是否应为多项同义词自动生成短语查询。默认为 true。
  • all_fields:执行上可以查询映射检测到的所有字段的查询。

Default Field

如果未在查询字符串语法中明确指定要搜索的字段,index.query.default_field 则将使用该字段来派生要搜索的字段。如果 index.query.default_field 未指定,query_string 则将自动尝试确定索引映射中可查询的现有字段,并对这些字段执行搜索。请注意,这不包括嵌套文档,使用嵌套查询来搜索这些文档。

Multi Field

该 query_string 查询还可以指定查询的字段。可以通过 "fields" 参数提供字段:

1
field1: query_term OR field2: query_term | ...

query_string 针对多个字段运行查询的想法是将每个查询字词扩展为 OR 子句,如下所示:

例如,以下查询:

1
2
3
4
5
6
7
8
9
GET /_search
{
"query": {
"query_string": {
"fields": ["content", "name"],
"query": "this AND that"
}
}
}

匹配相同的单词

1
2
3
4
5
6
GET /_search
{
"query": {
"query_string": "(content:this OR name:this) AND (content:that OR name:that)"
}
}

由于从单个搜索项生成了多个查询,因此使用 dis_max 带有 tie_breaker 的查询自动组合它们。例如(name 使用 ^5 符号表示增强 5):

1
2
3
4
5
6
7
8
9
10
GET /_search
{
"query": {
"query_string": {
"fields": ["content", "name^5"],
"query": "this AND that OR thus",
"tie_breaker": 0
}
}
}

简单通配符也可以用于搜索文档的特定内部元素。例如,如果我们有一个 city 包含多个字段(或带有字段的内部对象)的对象,我们可以自动搜索所有 “城市” 字段:

1
2
3
4
5
6
7
8
9
GET /_search
{
"query": {
"query_string": {
"fields": ["city.*"],
"query": "this AND that OR thus"
}
}
}

另一种选择是在查询字符串本身中提供通配符字段搜索(正确转义*符号),例如 city.\*:something

1
2
3
4
5
6
7
8
GET /_search
{
"query": {
"query_string": {
"query": "city.\\*:(this AND that OR thus)"
}
}
}

由于 (反斜杠)是json字符串中的特殊字符,因此需要对其进行转义,因此上面的两个反斜杠 query_string。

query_string 对多个字段运行查询时,允许使用以下附加参数:

  • type: 应如何组合字段以构建文本查询。

fields 参数还可以包括基于通配符字段名称,允许自动扩展到相关字段(包括动态引入的字段)。例如:

1
2
3
4
5
6
7
8
9
GET /_search
{
"query": {
"query_string": {
"fields": ["content", "name.*^5"],
"query": "this AND that OR thus"
}
}
}

Query stirng 语法

查询字符串被解析为一些列术语和运算符。术语可以是单个单词 - quick 或 brown - 或短语,由双引号括起来 - "quick brown" 以相同的顺序搜索短语中的所有单词。

operator 允许你自定义搜索 - 可用选项如下:

Field names

如 query_string 查询中所述 default_field,搜索搜索词,但可以在查询语法中指定其他字段:

  • 其中 status 字段包含 active:
    • status:active
  • 其中 title 字段包含 quick 或 brown。如果省略 OR 运算符,将使用默认运算符:
    • title:(quick OR brown)
    • title:(quick brown)
  • 其中 author 字段包含精确短语 "john smith"
    • author:"john smith"
  • book 中任何字段包含 quick 或 brown(注意我们需要对 * 使用反斜杠转义)
    • book.\*:(quick brown)
  • 该字段 title 具有任何非 null 值:
    • _exists_:title

Wildcards 通配符

通配符可以在单个术语上运行,使用 ? 替换单个字符,使用 * 替换零个或多个字符:

qu?ck bro *

  • 请注意,通配符查询可能使用大量的内存,并且执行得很糟糕,只要想想需要查询多少项来匹配查询字符串 a*b*c*
  • 纯通配符 * 被重写为 exists。因此,通配符 "field:*" 将匹配具有空值的文档,如下所示:{"field": ""},如果字段丢失或使用显式空值设置则不匹配:{"field": null}
  • 允许在单词的开头(例如 "*ing")使用通配符特别重,因为需要检查索引中的所有术语,以防它们匹配。可以通过设置 allow_leading_wildcard 为禁用前导通配符 false。

Regular expressions 正则表达式

正则表达式模式可以通过将它们包装在 forward-slashes("/") 中嵌入查询字符串中:

name:/jo?n(ath[oa]n)/

正则表达式语法中解释了受支持的正则表达式语法。

allow_leading_wildcard 参数对正则表达式没有任何控制权。如下所示的查询字符串将强制 Elasticsearch 访问索引中的每个术语:/.*n/ 谨慎使用!

Fuzziness 模糊

我们可以使用 "fuzzy" 运算符搜索与我们的搜索字词类似但不完全相同的字词:

1
quikc~ brwn~ foks~

Ranges 范围

可以为日期,数字或字符串字段指定范围。包含范围用方括号指定,[min TO max] 排他范围用大括号指定 {min TO max}

  • All days in 2012:
    • date:[2012-01-01 TO 2012-12-31]
  • Numbers 1..5
    • count:[1 TO 5]
  • Tags between alpha and omega,excluding alpha and omega:
    • tag:{alpha TO omega}
  • Numbers from 10 upwards
    • count:[10 TO *]
  • Dates before 2012
    • date:{* TO 2012-01-01}

可以组合使用大括号和方括号:

  • 数字从 1 到 5 但不包括 5:
    • count:[1 TO 5}
  • 一边无范围的范围可以使用如下语法:
    • age:>10
    • age:>=10
    • age:<10
    • age:<=10

要将上限和下限与简化语法结合使用,你需要将两个子句与 AND 运算符连接:

1
2
age:(>=10 AND <20)
age:(+>=10 +<20)

使用 Boost 提升权重

使用 boost 运算符 ^ 使一个术语比另一个术语更相关。例如,如果我们想要找到关于 foxes 的所有文档,但我们对 quick foxes 特别感兴趣:

1
quick^2 fox

默认 boost 值为 1,但可以是任何正浮点数。0 到 1 之间的提升会降低相关性。

提升也可以应用于短语或群组:

1
"john smith"^2 (foo bar)^4

Boolean operators 布尔运算符

默认情况下,只要一个术语匹配,所有术语都是可选的。搜索 foo bar baz 将查找包含一个或多个 foo 或 bar 或 baz 的任何文档。我们已经讨论了 default_operator 上面的内容,它允许你强制要求所有的术语,但也有一些布尔运算符可以在查询字符串本身中使用,以提供更多的控制。

首选运算符+(此术语必须存在)和-(此术语不得出现)。所有其他条款都是可选的。例如,这个查询:

1
quick brown +fox -news

说明:

  • fox 必须存在
  • news 一定不能存在
  • quick 和 brown 是可选的 - 它们的存在增加了相关性

熟悉的布尔运算符 AND,OR 以及 NOT (也写作 &&, || 和 !)也支持,但要小心,他们不遵守通常的优先级规则,所以每当多个运算符一起使用时,应使用括号。例如,以前的查询可以重写为:

1
((quick AND fox) OR (brown AND fox) OR fox) AND NOT news

此表单现在可以正确复制原始查询中的逻辑,但相关性评分与原始查询几乎没有相似之处。

相反,使用查询 bool 查询重写相同 match 查询将如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"bool": {
"must": {
"match": "fox"
},
"should": {
"match": "quick brown"
},
"must_not": {
"match": "news"
}
}
}

Grouping 分组

可以将多个术语或子句与括号组合在一起,以形成子查询:

1
(quick OR brown) AND fox

组可用于定位特定字段,或用于提升子查询的结果:

1
status:(active OR pending) title:(full text search)^2

Reserved characters 保留字符

如果你需要在查询本身中使用任何作为运算符的字符(而不是运算符),那么你应该使用前导反斜杠来转义它们。例如,要搜索 (1+1)=2,你需要将查询编写为 \(1\+1\)\=2

保留的字符是:+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /

无法正确转义这些特殊字符可能会导致语法错误,从而阻止你的查询运行。

<and> 根本无法转义。阻止它们尝试创建范围查询的唯一方法是安全从查询字符串中删除它们。

Empty Query 空查询

如果查询字符串为空或仅包含空格,则查询将生成空结果集。

elasticsearch 中的查询请求有两种方式,一种是简易版的查询,另外一种是使用 JSON 完整的请求体,叫做结构化查询(DSL)。由于 DSL 查询更为直观也更为简易,所以大都使用这种方式。DSL 查询是 POST 过去一个 json,由于 post 的请求是 json 格式的,所以存在很多灵活性,也有很多形式。这里有一个地方注意的是官方文档里面给的例子的 json 结构只是一部分,并不是可以直接复制粘贴进去使用的。一般要在外面加个 query 为 key 的结构。

match

最简单的一个 match 例子:

查询和 “我的宝马多少马力” 这个查询语句匹配的文档。

1
2
3
4
5
6
7
8
9
{
"query": {
"match": {
"content": {
"query": "我的宝马多少马力"
}
}
}
}

上面的查询匹配就会进行分词,比如 “宝马多少马力” 会被分词为 “宝马 多少 马力”,所有有关 “宝马 多少 马力”,那么所有包含这三个词中的一个或多个的文档就会被搜索出来。并且根据 lucene 的评分机制(TF/IDF)来进行评分。

match_phrase

比如上面一个例子,一个文档 “我的保时捷马力也不错” 也会被搜索出来,那么想要精确匹配所有同时包含 “宝马 多少 马力” 的文档怎么做?就要使用 match_phrase 了。

1
2
3
4
5
6
7
8
9
{
"query": {
"match_phrase": {
"content": {
"query": "我的宝马多少马力"
}
}
}
}

完全匹配可能比较严,我们会希望有个可调节因子,少匹配一个也满足,那就需要使用到 slop。

1
2
3
4
5
6
7
8
9
10
{
"query": {
"match_phrase": {
"content": {
"query": "我的宝马多少马力",
"slop": 1
}
}
}
}

multi_match

如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用 multi_match

1
2
3
4
5
6
7
8
{
"query": {
"multi_match": {
"query": "我的宝马多少马力",
"fields": ["title", "content"]
}
}
}

但是 multi_match 就涉及到匹配评分的问题了。

我们希望完全匹配的文档占的评分比较高,则需要使用 best_fields

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"query": {
"multi_match": {
"query": "我的宝马发动机多少",
"type": "best_fields",
"fields": [
"tag",
"content"
],
"tie_breaker": 0.3
}
}
}

意思就是完全匹配 “宝马 发动机” 的文档评分会比较靠前,如果只匹配宝马的文档评分乘以 0.3 的系数。

我们希望越多字段匹配的文档评分越高,就要使用 most_fields

1
2
3
4
5
6
7
8
9
10
11
12
{
"query": {
"multi_match": {
"query": "我的宝马发动机多少",
"type": "most_fields",
"fields": [
"tag",
"content"
]
}
}
}

我们希望这个词条的分词词汇是分配到不同字段中的,那么就使用 cross_fields

1
2
3
4
5
6
7
8
9
10
11
12
{
"query": {
"multi_match": {
"query": "我的宝马发动机多少",
"type": "cross_fields",
"fields": [
"tag",
"content"
]
}
}
}

term

term 是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇

1
2
3
4
5
6
7
{
"query": {
"term": {
"content": "汽车保养"
}
}
}

查询出的所有文档都包含 “汽车保养” 这个词组的词汇。

使用 term 要确定的是这个字段是否 “被分析(analyzed)”,默认的字符串是被分析的。

拿官网上的例子举例:

mapping 是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"full_text": {
"type": "string"
},
"exact_value": {
"type": "string",
"index": "not_analyzed"
}
}
}
}
}

PUT my_index/my_type/1
{
"full_text": "Quick Foxes!",
"exact_value": "Quick Foxes!"
}

其中的 full_text 是被分析过的,所以 full_text 的索引中存的就是 [quick, foxes],而 extra_value 中存的是 [Quick Foxes!]。

那下面的几个请求:

1
2
3
4
5
6
7
8
GET my_index/my_type/_search
{
"query": {
"term": {
"exact_value": "Quick Foxes!"
}
}
}

能请求得的数据,因为完全匹配。

1
2
3
4
5
6
7
8
GET my_index/my_type/_search
{
"query": {
"term": {
"full_text": "Quick Foxes!"
}
}
}

请求不出数据,因为 full_text 分词后的结果中没有 [Quick Foxes!] 这个分词。

bool 联合查询:must,should,must_not

如果我们想要请求 “content 中带宝马,但是 tag 中不带宝马” 类似这样的需求,就需要用到 bool 联合查询。联合查询就会使用到 must,should,must_not 三种关键词。

这三个可以这么理解:

  • must:文档必须完全匹配条件
  • should:should 下面会带有一个以上的条件,至少满足一个条件,这个文档就符合 should
  • must_not:文档必须不匹配条件

比如上面那个需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"query": {
"bool": {
"must": {
"term": {
"content": "宝马"
}
},
"must_not": {
"term": {
"tags": "宝马"
}
}
}
}
}

运行搜索

你可以使用搜索API搜索 Elasticsearch 数据流或索引中存储的数据。

该 API 可以运行两种类型的搜索,具体取决于你如何提供查询

  • URI 搜索
    • 通过查询参数提供查询。URI 搜索往往更简单,最适合测试。
  • 请求体搜索
    • 通过 API 请求的 JSON body 提供查询。这些查询是用 查询DSL 编写的。我们建议在大多数生产用例中使用请求体搜索。

如果同时在 URI 和请求正文中指定查询,则搜索 API 请求仅运行 URI 查询。

运行 URI 搜索

你可以使用搜索 API 的 q查询字符串参数 在请求的 URI 中运行搜索。该 q 参数仅接受以 Lucene 的查询字符串语法编写的查询。

例子:

首先,向 Elasticsearch 索引添加一些数据。

以下 bulk API 将一些示例用户日志数据添加到 user_logs_00001 索引。

示例

$response = $client->bulk([ 'body' => [ [ 'index' => [ '_index' => 'user_logs_00001', '_id' => 1, ], ], [ '@timestamp' => '2020-12-06T11:04:05.000Z', 'user' => [ 'id' => 'vlb44hny', ], 'message' => 'Login attempt failed', ],

    [
            'index' => [
                '_index' => 'user_logs_00001',
                '_id' => 2,
            ],
        ],
        [
            '@timestamp' => '2020-12-07T11:06:07.000Z',
            'user' => [
                'id' => '8a4f500d',
            ],
            'message' => 'Login successful',
        ],

        [
            'index' => [
                '_index' => 'user_logs_00001',
                '_id' => 3,
            ],
        ],
        [
            '@timestamp' => '2020-12-07T11:07:08.000Z',
            'user' => [
                'id' => 'l7gk7f82',
            ],
            'message' => 'Logout successful',
        ],
    ],
]);

echo json_encode($response, JSON_PRETTY_PRINT);

现在你可以使用 URI 搜索来匹配一个 user.idl7gk7f82 的文档,请注意查询通过 q 查询参数来指定。

1
2
3
4
5
6
$response = $client->search([
'index' => 'user_logs_00001',
'q' => 'user.id:8a4f500d',
]);

echo json_encode($response, JSON_PRETTY_PRINT);

响应结果的 hits.hits 属性包含了匹配到的文档。

运行一个请求体查询

你也可以通过 查询DSL 语法来传递一个 query 请求体参数进行查询。

示例

$response = $client->search([ 'index' => 'user_logs_00001', 'body' => [ 'query' => [ 'match' => [ 'message' => '登录成功', ], ], ] ]);

echo json_encode($response, JSON_PRETTY_PRINT);

API 返回以下响应:

hits.hits 属性包含匹配的文档。默认情况下,响应按这些匹配的文档 _score 的相关性得分排序,该得分衡量每个文档与查询的匹配程度。

响应 { "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 3, "relation": "eq" }, "max_score": 0.9983525, "hits": [ { "_index": "user_logs_00001", "_type": "_doc", "_id": "2", "_score": 0.9983525, "_source": { "@timestamp": "2020-12-07T11:06:07.000Z", "user": { "id": "8a4f500d" }, "message": "Login successful" } }, { "_index": "user_logs_00001", "_type": "_doc", "_id": "3", "_score": 0.49917626, "_source": { "@timestamp": "2020-12-07T11:07:08.000Z", "user": { "id": "l7gk7f82" }, "message": "Logout successful" } }, { "_index": "user_logs_00001", "_type": "_doc", "_id": "1", "_score": 0.42081726, "_source": { "@timestamp": "2020-12-06T11:04:05.000Z", "user": { "id": "vlb44hny" }, "message": "Login attempt failed" } } ] } }

搜索多个数据流和索引

要搜索多个数据流和索引,请将它们作为逗号分隔的值添加到搜索 API 请求路径中。

示例

以下请求搜索 user_logs_00001user_logs_00002 索引。

$response = $client->search([ 'index' => 'user_logs_00001,user_logs_00002', 'body' => [ 'query' => [ 'match' => [ 'message' => 'Login Successful', ], ], ] ]);

echo json_encode($response, JSON_PRETTY_PRINT);

你也可以使用通配符 * 模式搜索多个数据流和索引。

示例

$response = $client->search([ 'index' => 'user_logs*', 'body' => [ 'query' => [ 'match' => [ 'message' => 'Login Successful', ], ], ] ]);

echo json_encode($response, JSON_PRETTY_PRINT);

要搜索集群中的所有数据流和索引,请从请求路径中省略目标。或者,你可以使用 _all*

示例

$response = $client->search([ 'index' => '', // 搜索全部索引 // 'index' => '_all', // 搜索全部索引 // 'index' => '*', // 搜索全部索引 'body' => [ 'query' => [ 'match' => [ 'message' => 'Login Successful', ], ], ] ]);

echo json_encode($response, JSON_PRETTY_PRINT);

分页搜索结果

默认情况下,搜索 API 返回前 10 个匹配的文档。

要分页显示更多结果,可以使用搜索 API sizefrom 参数。该 size 参数是要返回匹配文档的数量。该 from 是从完整结果集开始的零索引偏移量,该偏移量指示要开始使用的文档。

示例

以下搜索 API 请求将 from 偏移量设置为 5,表示请求偏移量或跳过前五个匹配文档。 该 size 参数是 20,这意味着该请求最多可返回 20 个文档,开始偏移。

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'term' => [ 'user.id' => '8a4f500d', ], ], ], 'from' => 5, 'size' => 20, ]);

echo json_encode($response, JSON_PRETTY_PRINT);

默认情况下,你不能使用 fromsize 参数分页浏览超过 10000 个文档。使用 index.max_result_window 索引设置来设置此限制。

深度分页或一次请求许多结果可能会导致搜索缓慢。结果在返回之前先进行排序。由于搜索请求通常跨越多个分片,因此每个分片必须生成自己的排序结果。然后必须对这些单独的结果进行合并和排序,以确保总体排序顺序正确。

作为深度分页的替代方法,我们建议使用 scrollsearch_after 参数。

Elasticsearch 使用 Lucene 的内部文档 ID 作为平局。这些内部文档 ID 在相同数据的副本之间可能完全不同。在进行分页时,您可能偶尔会看到排序顺序相同的文档的顺序不一致。

检索选定的字段

默认情况下,搜索响应中的每个 _source 匹配都包括 document,这是对文档建立索引时提供的整个 JSON 对象。如果在搜索响应中仅需要某些源字段,则可以使用 source-filtering 来限制返回源的哪些部分。

仅使用文档源返回字段有一些限制:

  • _source 不包含多字段或字段别名。同样,源中的字段也不包含使用 copy_to 映射参数复制的值。
  • 由于 _source 在 Lucene 中存储为单个字段,因此即使只需要少量字段,也必须加载和解析整个源对象。

为了避免这些限制,你可以:

  • 使用 docvalue_fields 参数获取选定字段的值。当返回相当少量的支持 doc 值的字段(例如关键字和日期)时,这是一个不错的选择。
  • 使用 sorted_fields 参数获取特定存储字段的值。(使用 store 映射选项的字段。)

你可以在以下各节中找到有关这些方法的更多详细信息:

  • 源过滤
  • 文件值栏位
  • 储存栏位

源过滤

你可以使用该 _source 参数选择返回源的哪些字段。这称为源过滤。

如下的搜索请求设置 _source 请求体参数为 false,这样请求结果里就不会包含 _source 字段。

示例

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'term' => [ 'user.id' => '8a4f500d', ], ], ], '_source' => false, ]);

echo json_encode($response, JSON_PRETTY_PRINT);

也可以通过 * 通配符来让搜索 API 返回对应的字段,下面的请求返回的响应结果中的 _source 字段只会包含 obj 字段以及它的属性:

示例

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'term' => [ 'user.id' => '8a4f500d', ], ], ], '_source' => 'obj.*', ]);

echo json_encode($response, JSON_PRETTY_PRINT);

你也可以通过数组指定多个字段名匹配的模式,如下的请求会返回 obj1obj2 字段和它们的属性:

示例

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'term' => [ 'user.id' => '8a4f500d', ], ], ], '_source' => ['obj1.', 'obj2.'], ]);

echo json_encode($response, JSON_PRETTY_PRINT);

为了更好地控制,你可以指定一个对象,该对象在参数中包含 includesexcludes 模式的数组 _source

如果 includes 指定了属性,则仅返回与其模式之一匹配的源字段。你可以使用 excludes 属性从此子集中排除字段。

如果 includes 未指定该属性,则返回整个文档源,不包括与该 excludes 属性中的模式匹配的任何字段。

以下搜索 API 请求仅返回 obj1obj2 字段及属性的源,不包括任何子 description 字段。

示例

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'term' => [ 'user.id' => '8a4f500d', ], ], ], '_source' => [ 'includes' => [ 'obj1.', 'obj2.', ], 'excludes' => [ '*.description', ], ], ]);

echo json_encode($response, JSON_PRETTY_PRINT);

docvalue_fields

你可以使用 docvalue_fields 参数返回搜索响应中一个或多个字段的 doc-values

Doc 值存储与 _source 相同的值,但是在磁盘上基于列的结构中进行了优化,该结构针对排序和聚合进行了优化。由于每个字段都是单独存储的,因此 Elasticsearch 仅读取所请求的字段值,并且可以避免加载整个文档。

默认情况下,将为支持的字段存储文档值。但是,texttext_annotated 字段不支持 doc 值。

以下搜索请求使用该 docvalue_fields 参数来检索以下字段的 doc 值: * 名称以 my_ip 开头的字段 * my_keyword_field * 名称以 _date_field 结尾的字段

$response = $client->search([ 'index' => '*', 'body' => [ 'query' => [ 'match_all' => (object)[ ], ], 'docvalue_fields' => [ 'my_ip*', [ 'field' => 'my_keyword_field', ], [ 'field' => '*_date_field', 'format' => 'epoch_millis', ], ], ], ]);

echo json_encode($response, JSON_PRETTY_PRINT);
  • 通配符 patten,用于匹配以字符串形式指定的字段名称
  • 通配符 patten,用于匹配指定为对象的字段名称
  • 使用对象符号,可以使用 format 参数指定字段的返回 doc 值格式。日期字段支持日期格式,数值字段支持 DecimalFormat 模式。其他字段数据类型不支持该 format 参数。

储存栏位

也可以使用 store 映射选项存储单个字段的值。你可以使用 stored_fields 参数将这些存储的值包括在搜索响应中。

理想情况下,Elasticsearch 应该在服务器上单独运行并使用所有可用资源。为此,你需要配置你的系统,以允许运行 Elasticsearch 的用户访问比默认允许更多的资源。

进入生产之前,必须考虑以下设置:

  • 禁用交换分区
  • 增加文件描述符
  • 确保有足够的虚拟内存
  • 确保足够的线程
  • JVM DNS 缓存设置
  • 临时目录未安装 noexec

开发模式与生产模式

默认情况下,Elasticsearch 假定你正在开发模式下工作。如果以上任何设置的配置都不正确,将在日志文件中写入警告,但是你将能够启动和运行 Elasticsearch 节点。

一旦你配置了像这样的网络设置 network.host,Elasticsearch 就会假设你即将投入生产,并将上述警告升级为异常。这些异常将阻止你的 Elasticsearch 节点启动。这是一项重要的安全措施,以确保不会因服务器配置错误而丢失数据。

配置系统设置

在哪里配置系统设置取决于您用于安装 Elasticsearch 的软件包以及所使用的操作系统。

使用 .zip.tar.gz 软件包时,可以配置系统设置:

当使用 RPM 或 Debian 软件包,大部分系统设置在 系统配置文件。然而,使用 systemd 的系统要求在 systemd配置文件 中指定系统限制。

ulimit

在 Linux 系统上,ulimit 可用于临时更改资源限制。通常需要像 root 切换到将要运行 Elasticsearch 的用户之前那样设置限制。例如,要将打开的文件句柄(ulimit -n)的数量设置为 65536,可以执行以下操作:

1
2
3
sudo su
ulimit -n 65535
su elasticsearch
  1. 切换到 root 用户
  2. 修改打开文件的最大数量
  3. 切换为 elasticsearch 用户以启动 Elasticsearch

新限制仅适用于当前会话。

你可以通过查阅所有当前应用的限制 ulimit -a

/etc/security/limits.conf

在 Linux 系统上,可以通过编辑 /etc/security/limits.conf 文件来为特定用户设置永久限制。要将 elasticsearch 用户打开的最大文件数设置为 65535,请在 limits.conf 文件中添加以下行:

1
elasticsarch - nofile 65535

此更改仅在 elasticsearch 用户下次打开新会话时生效。

Sysconfig 文件

使用 RPM 或 Debian 软件包时,可以在系统配置文件中指定环境变量,该文件位于:

  • RPM: /etc/sysconfig/elasticsearch
  • Debian: /etc/default/elasticsearch

但是,需要通过更改 systemd 指定系统限制。

系统配置

在使用 systemd 的系统上使用 RPM 或 Debian 软件包时,必须通过 systemc 指定系统限制。

systemd 服务文件(/usr/lib/systemd/system/elasticsearch.service)包含默认情况下应用的限制。

要覆盖它们,请添加一个名为 /etc/systemd/system/elasticsearch.service.d/override.conf(或者,你可以运行 sudo systemctl edit elasticsearch 编辑该文件),设置该文件中的所有更改:

1
2
[Service]
LimitMEMLOCK=infinity

一旦修改完,运行下面的命令:

1
sudo systemctl daemon-reload

禁用交换分区

大多数操作系统尝试为文件系统高速缓存使用尽可能多的内存,并急切换出未使用的应用程序内存。这可能会导致部分 JVM 堆设置其可执行页面换出到磁盘上。

交换对性能,节点稳定性非常不利,应不惜一切代价避免交换。它可能会导致垃圾收集持续数分钟而不是毫秒,并且可能导致节点响应缓慢甚至断开与集群的连接。在弹性分布式系统中,让操作系统杀死该节点更为有效。

有三种禁用交换的方法。首选选项是完全禁用交换。如果这不是一个选择,则是否要减少交换性而不是内存锁定取决于你的环境。

禁用所有交换文件

通常,Elasticsearch 是在盒子上运行的唯一服务,其内存使用量由 JVM 选项控制。无需启用交换功能。

在 Linux 系统上,可以通过运行以下命令暂时禁用交换:

1
sudo swapoff -a

这不需要重启 Elasticsearch。

要永久禁用它,你将需要编辑 /etc/fstab 文件并注释掉所有包含单词 swap 的行。

配置 swappiness

Linux 系统上可用的另一个选项是确保 sysctlvm.swappiness 设置为 1。这减少了内核的交换趋势,并且在正常情况下不应导致交换,同时仍然允许整个系统在紧急情况下进行交换。

启用 bootstrap.memory_lock

另一种选择是在 Linux/Unix 系统上使用 mlockall 防止任何 Elasticsearch 内存被换出。

如果 mlockall 尝试分配的内存超过可用内存,则可能导致 JVM 或 Shell 会话退出。

文件描述符

这仅与 Linux 和 macOS 有关,如果在 Windows 上运行 Elasticsearch,则可以安全地忽略它。

Elasticsearch 使用许多文件描述符或文件句柄。文件描述符用尽是灾难性的,很可能导致数据丢失。确保将运行 Elasticsearch 的用户的打开文件描述符数限制增加到 65535 或更高。

虚拟内存

Elasticsearch mmapfs 默认使用目录来存储其索引。默认的操作系统对 mmap 计数的限制可能太低,这可能会导致内存不足异常。

在 Linux 上,你可以通过以下命令来增加限制:

1
sysctl -w vm.max_map_count=262144

要永久设置此值,请更新 /etc/sysctl.conf 里的 vm.max_map_count 设置。要在重启后进行验证,请运行 sysctl vm.max_map_count

RPM 和 Debian 软件包将自动配置此设置。不需要进一步配置。

线程数

Elasticsearch 对不同类型的操作使用许多线程池。能够在需要时创建新线程很重要。确保 Elasticsearch 用户可以创建的线程数至少为 4096。

这可以通过 ulimit -u 4096 在启动 Elasticsearch 之前设置,或者设置 /etc/security/limits.conf 里面的 nproc 为 4096。

尽管 Elasticsearch 需要很少的配置,但在投入生产之前,需要考虑许多设置。

进入生产之前,必须考虑以下设置:

  • 路径设定
  • 集群名称
  • 节点名称
  • 网络主机
  • 发现设置
  • 堆大小
  • 堆转储路径
  • GC 记录
  • 临时目录

path.datapath.logs

如果你使用 .zip.tar.gz 归档,则 datalogs 目录是 $ES_HOME 的子目录。如果这些重要的目录都保留在它们默认的位置,我们很有可能会在升级 Elasticsearch 版本的生活会将其删除。

在生产中使用时,几乎肯定会想要更改 data 和 log 文件夹的位置:

1
2
3
path
logs: /var/log/elasticsearch
data: /var/data/elasticsearch

RPM 和 Debian 发行版已经为 datalogs 使用了自定义路径。

path.data 设置可以设置多个路径,在这种情况下,所有的路径将被用于存储数据(虽然属于单个碎片文件将全部存储在相同的数据路径上):

1
2
3
4
5
path:
data:
- /mnt/elasticsearch_1
- /mnt/elasticsearch_2
- /mnt/elasticsearch_3

cluster.name

cluster.name 节点与集群中的所有其他节点共享节点时,该节点只能加入集群。默认名称为 elasticsearch,但您应将其更改为描述集群用途的适当名称。

1
cluster.name: logging-prod

确保不要在不同的环境中重复使用相同的集群名称,否则可能会导致节点加入了错误的集群。

node.name

Elasticsearch node.name 用作 Elasticsearch 特定实例的人类可读标识符,因此它包含在许多 API 的响应中。它默认为计算机在 Elasticsearch 启动时具有的主机名,但可以在 elasticsearch.yml 按以下方式显式配置:

1
node.name: prod-data-2

network.host

默认情况下,Elasticsearch 仅绑定到环回地址(例如 127.0.0.1[::1])。这足以在服务器上运行单个开发节点。

实际上,可以从 $ES_HOME 单个节点上的相同位置启动多个节点。这对于测试 Elasticsearch 形成集群的能力很有用,但不是建议用于生产的配置。

为了与其他服务器上的节点形成集群,您的节点将需要绑定到非环回地址。尽管网络设置很多,通常你需要配置的是 network.host

1
network.host: 192.168.1.10

network.host 设置也了解一些特殊的值,比如 _local__site__global_ 和诸如 :ip4:ip6 这种修饰符。

一旦提供了自定义的 network.host,Elasticsearch 就会假设你正在从开发模式过渡到生产模式,并将许多系统启动检查从警告升级为异常。

0%