原英文版地址: https://www.elastic.co/guide/en/elasticsearch/reference/7.7/run-a-search.html, 原文档版权归 www.elastic.co 所有
本地英文版地址: ../en/run-a-search.html

运行一个搜索

你可以使用 search API 来搜索存储在一个或多个 Elasticsearch 索引中的数据。

该 API 可以运行两种类型的搜索,具体取决于你提供的 查询(query)的方式:

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

如果在 URI 和请求主体中都指定了查询,则 search API 请求将仅运行 URI 查询。

运行一个 URI 搜索

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

例子

首先,在 Elasticsearch 索引中摄取或添加一些数据。

下面的 bulk API 请求将一些演示的用户日志数据添加到 user_logs_000001 索引中。

PUT /user_logs_000001/_bulk?refresh
{"index":{"_index" : "user_logs_000001", "_id" : "1"}}
{ "@timestamp": "2020-12-06T11:04:05.000Z", "user": { "id": "vlb44hny" }, "message": "Login attempt failed" }
{"index":{"_index" : "user_logs_000001", "_id" : "2"}}
{ "@timestamp": "2020-12-07T11:06:07.000Z", "user": { "id": "8a4f500d" }, "message": "Login successful" }
{"index":{"_index" : "user_logs_000001", "_id" : "3"}}
{ "@timestamp": "2020-12-07T11:07:08.000Z", "user": { "id": "l7gk7f82" }, "message": "Logout successful" }

现在,你可以使用 search API 在这个索引上运行 URI 搜索。

下面的 URI 搜索匹配 user.id 值为 l7gk7f82的文档。 注意,这个查询是使用 q 查询字符串参数指定的。

GET /user_logs_000001/_search?q=user.id:8a4f500d

API 返回以下响应。 注意 hits.hits 属性包含与查询匹配的文档。

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.9808291,
    "hits": [
      {
        "_index": "user_logs_000001",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.9808291,
        "_source": {
          "@timestamp": "2020-12-07T11:06:07.000Z",
          "user": {
            "id": "8a4f500d"
          },
          "message": "Login successful"
        }
      }
    ]
  }
}

运行一个请求体搜索

你可以使用 search API 的 query 请求主体参数来提供一个 JSON 对象形式的查询,用 Query DSL 编写。

例子

下面的 请求体搜索 使用 match 查询来匹配 message 值为 login successful 的文档。 注意,match 查询在 query 参数中被指定为 JSON 对象。

GET /user_logs_000001/_search
{
  "query": {
    "match": {
      "message": "login successful"
    }
  }
}

API返回以下响应。

hits.hits 属性中包含匹配的文档。 默认情况下,响应按照 _score 对这些匹配的文档进行排序,这是一个衡量每个文档与查询匹配程度的 相关性评分(relevance 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_000001",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.9983525,
        "_source": {
          "@timestamp": "2020-12-07T11:06:07.000Z",
          "user": {
            "id": "8a4f500d"
          },
          "message": "Login successful"
        }
      },
      {
        "_index": "user_logs_000001",
        "_type": "_doc",
        "_id": "3",
        "_score": 0.49917626,
        "_source": {
          "@timestamp": "2020-12-07T11:07:08.000Z",
          "user": {
            "id": "l7gk7f82"
          },
          "message": "Logout successful"
        }
      },
      {
        "_index": "user_logs_000001",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.42081726,
        "_source": {
          "@timestamp": "2020-12-06T11:04:05.000Z",
          "user": {
            "id": "vlb44hny"
          },
          "message": "Login attempt failed"
        }
      }
    ]
  }
}

搜索多个索引

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

例子

下面的请求在 user_logs_000001user_logs_000002 索引中进行搜索。

GET /user_logs_000001,user_logs_000002/_search
{
  "query": {
    "match": {
      "message": "login successful"
    }
  }
}

你还可以使用 索引 模式(pattern) 搜索多个索引。

例子

以下请求使用 索引模式 user_logs* 代替索引名。 该请求搜索集群中以 user_logs 开头的任何索引。

GET /user_logs*/_search
{
  "query": {
    "match": {
      "message": "login successful"
    }
  }
}

若要搜索集群中的所有索引,请在请求路径中忽略索引名称。 或者,你也可以使用 _all* 来代替索引名。

例子

下面几个请求是等效的,都搜索集群中的所有索引:

GET /_search
{
  "query": {
    "match": {
      "message": "login successful"
    }
  }
}

GET /_all/_search
{
  "query": {
    "match": {
      "message": "login successful"
    }
  }
}

GET /*/_search
{
    "query" : {
        "match" : { "message" : "login" }
    }
}

搜索结果分页

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

要对大量结果进行分页,可以使用 search API 的 sizefrom 参数。 参数 size 是要返回的匹配文档的数量。 参数 from 是从一个完整结果集开头开始的索引为 0 的偏移量,该偏移量指示你想要开始的文档。

例子

下面的 search API 请求将 from 偏移量设置为 5,这意味着请求会偏移或跳过前 5 个匹配的文档。

参数 size20,这意味着请求可以从偏移量开始返回多达 20 个文档。

GET /_search
{
  "from": 5,
  "size": 20,
  "query": {
    "term": {
      "user.id": "8a4f500d"
    }
  }
}

默认情况下,不能使用 fromsize 参数翻页超过 10,000 个文档。 此限制是使用 index.max_result_window 索引设置来设置的。

深度分页(deep paging) 或一次请求多个结果会导致搜索速度变慢。 结果会在返回之前进行排序。 因为搜索请求通常跨越多个分片,每个分片必须生成自己的排序结果。 然后,必须将这些单独的结果进行组合然后再次排序,以确保整体的排序是正确的。

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

Elasticsearch 使用 Lucene 的内部文档 ID 作为定因素。 在相同数据的副本之间,这些内部文档 ID 可能完全不同。 分页时,你可能偶尔会看到排序值相同的文档排序却不一致。

检索选定的字段 (retrieve selected fields)

默认情况下,搜索响应中的每个 命中(hit) 结果都包括文档的 _source,这是在索引文档时提供的整个 JSON 对象。 如果在搜索响应中只需要某些源字段,可以使用 源过滤(source filtering) 来限制返回源的哪些部分。

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

  • _source 字段不包括 多字段(multi-fields)字段别名(field aliases)。 同样,源中的字段不包含使用 mapping 参数 copy_to 复制的值。
  • 由于 _source 在 Lucene 中作为单个字段存储,所以必须加载和解析整个源对象,即使只需要少量字段。

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

  • 使用 docvalue_fields 参数去获取选定字段的值。 当返回相当少量的支持 文档值(doc values) 的字段(如 keyword 和 date类型的字段)时,这会是一个不错的选择。
  • 使用 stored_fields 参数获取特定存储字段的值(使用 store mapping 选项的字段)。

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

源过滤 (Source filtering)

你可以使用 _source 参数来选择返回源的哪些字段。这被称为源过滤(source filtering)

例子

下面的 search API 请求将 _source 请求主体参数设置为 false,响应中就不包括文档源了。

GET /_search
{
  "_source": false,
  "query": {
    "term": {
      "user.id": "8a4f500d"
    }
  }
}

要仅返回源字段的子集,请在 _source 参数中指定通配符(*)模式。 下面这个 search API 请求仅返回 obj字段及其属性的源。

GET /_search
{
  "_source": "obj.*",
  "query": {
    "term": {
      "user.id": "8a4f500d"
    }
  }
}

你也可以在 _source 字段中指定一组通配符模式。 下面这个 search API 请求仅返回 obj1obj2 字段及其属性的源。

GET /_search
{
  "_source": [ "obj1.*", "obj2.*" ],
  "query": {
    "term": {
      "user.id": "8a4f500d"
    }
  }
}

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

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

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

下面这个 search API 请求仅返回 obj1obj2 字段及其属性的源,不包括任何子 description 字段。

GET /_search
{
  "_source": {
    "includes": [ "obj1.*", "obj2.*" ],
    "excludes": [ "*.description" ]
  },
  "query": {
    "term": {
      "user.id": "8a4f500d"
    }
  }
}

文档值字段 (Doc value fields)

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

文档值(doc values) 中存储的值与 _source 相同,但是它们 存储在磁盘上的基于列的结构中,该结构针对排序和聚合进行了优化。 因为每个字段都是单独存储的,所以 Elasticsearch 只读取被请求的字段值,可以避免加载整个文档的 _source

默认情况下,为支持的字段存储 文档值(doc values)。 但是,texttext_annotated 类型的字段不支持 文档值(doc values)。

例子

下面这个搜索请求使用 docvalue_fields 参数检索以下字段的文档值(doc values):

  • 字段名以 my_ip 开头的字段
  • my_keyword_field
  • 字段名以 _date_field 结尾的字段
GET /_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "my_ip*",                     
    {
      "field": "my_keyword_field" 
    },
    {
      "field": "*_date_field",
      "format": "epoch_millis"    
    }
  ]
}

用于匹配字段名称的通配符模式,格式: 字符串。

用于匹配字段名称的通配符模式,格式: 对象(object)。

使用对象表示法,你以使用 format(格式) 参数来指定字段返回的文档值(doc values)的格式。 Date 字段 支持 date 格式(format)Numeric 字段 支持 十进制格式模式(DecimalFormat pattern). 其他字段类型不支持 format 参数。

不能使用 docvalue_fields 参数检索嵌套对象的文档值(doc values)。 如果你指定了一个嵌套对象,搜索将为该字段返回一个空数组([ ])。 若要访问嵌套字段,请使用 inner_hits 参数的 docvalue_fields 属性。

存储字段 (Stored fields)

还可以使用 store mapping 选项存储单个字段的值。 你可以使用 stored_fields 参数在搜索响应中包含这些存储的值。