搜索 API

编辑

搜索包含一个或多个查询,这些查询被组合并发送到 Elasticsearch。与搜索查询匹配的文档在响应的 hits搜索结果 中返回。

搜索还可以包含用于更好地处理其查询的附加信息。例如,搜索可以被限制在特定索引或仅返回特定数量的结果。

您可以使用 搜索 API 来搜索和 聚合 存储在 Elasticsearch 数据流或索引中的数据。该 API 的 query 请求体参数接受用 查询 DSL 编写的查询。

运行搜索

编辑

以下请求使用 match 查询在 my-index-000001 中搜索。此查询匹配 user.id 值为 kimchy 的文档。

resp = client.search(
    index="my-index-000001",
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      match: {
        'user.id' => 'kimchy'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  }
}

API 响应在 hits.hits 属性中返回与查询匹配的前 10 个文档。

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.3862942,
    "hits": [
      {
        "_index": "my-index-000001",
        "_id": "kxWFcnMByiguvud1Z8vC",
        "_score": 1.3862942,
        "_source": {
          "@timestamp": "2099-11-15T14:12:12",
          "http": {
            "request": {
              "method": "get"
            },
            "response": {
              "bytes": 1070000,
              "status_code": 200
            },
            "version": "1.1"
          },
          "message": "GET /search HTTP/1.1 200 1070000",
          "source": {
            "ip": "127.0.0.1"
          },
          "user": {
            "id": "kimchy"
          }
        }
      }
    ]
  }
}

常用搜索选项

编辑

您可以使用以下选项自定义您的搜索。

查询 DSL
查询 DSL 支持您可以混合和匹配以获得所需结果的各种查询类型。查询类型包括

聚合
您可以使用 搜索聚合 来获取搜索结果的统计信息和其他分析。聚合可以帮助您回答诸如以下问题:

  • 我的服务器的平均响应时间是多少?
  • 我的网络上用户点击次数最多的 IP 地址是什么?
  • 每个客户的总交易收入是多少?

搜索多个数据流和索引
您可以使用逗号分隔的值和类似 grep 的索引模式在同一请求中搜索多个数据流和索引。您甚至可以提高来自特定索引的搜索结果。请参阅 搜索多个数据流和索引

分页搜索结果
默认情况下,搜索仅返回前 10 个匹配的命中。要检索更多或更少的文档,请参阅 分页搜索结果

检索选定的字段
搜索响应的 hits.hits 属性包括每个命中的完整文档 _source。要仅检索 _source 的子集或其他字段,请参阅 检索选定的字段

对搜索结果排序
默认情况下,搜索命中按 _score 排序,_score 是一个 相关性分数,用于衡量每个文档与查询的匹配程度。要自定义这些分数的计算,请使用 script_score 查询。要按其他字段值对搜索命中排序,请参阅 对搜索结果排序

运行异步搜索
Elasticsearch 搜索旨在快速处理大量数据,通常在几毫秒内返回结果。因此,搜索默认是同步的。搜索请求在返回响应之前等待完整的结果。

但是,对于跨大型数据集或 多个集群 的搜索,完整结果可能需要更长的时间。

为了避免长时间的等待,您可以改为运行异步async搜索。 异步搜索 让您可以立即检索长时间运行的搜索的部分结果,并在以后获取完整结果。

定义仅在查询中存在的字段

编辑

您可以定义仅作为搜索查询一部分存在的 运行时字段,而不是索引您的数据然后搜索它。您可以在搜索请求中指定一个 runtime_mappings 部分来定义运行时字段,该字段可以选择包含一个 Painless 脚本。

例如,以下查询定义一个名为 day_of_week 的运行时字段。包含的脚本根据 @timestamp 字段的值计算星期几,并使用 emit 返回计算的值。

该查询还包括一个在 day_of_week 上运行的 词项聚合

resp = client.search(
    index="my-index-000001",
    runtime_mappings={
        "day_of_week": {
            "type": "keyword",
            "script": {
                "source": "emit(doc['@timestamp'].value.dayOfWeekEnum\n        .getDisplayName(TextStyle.FULL, Locale.ENGLISH))"
            }
        }
    },
    aggs={
        "day_of_week": {
            "terms": {
                "field": "day_of_week"
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-000001",
  runtime_mappings: {
    day_of_week: {
      type: "keyword",
      script: {
        source:
          "emit(doc['@timestamp'].value.dayOfWeekEnum\n        .getDisplayName(TextStyle.FULL, Locale.ENGLISH))",
      },
    },
  },
  aggs: {
    day_of_week: {
      terms: {
        field: "day_of_week",
      },
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "runtime_mappings": {
    "day_of_week": {
      "type": "keyword",
      "script": {
        "source":
        """emit(doc['@timestamp'].value.dayOfWeekEnum
        .getDisplayName(TextStyle.FULL, Locale.ENGLISH))"""
      }
    }
  },
  "aggs": {
    "day_of_week": {
      "terms": {
        "field": "day_of_week"
      }
    }
  }
}

响应包括一个基于 day_of_week 运行时字段的聚合。在 buckets 下是 key,其值为 Sunday。该查询基于 day_of_week 运行时字段中定义的脚本动态计算此值,而无需索引该字段。

{
  ...
  ***
  "aggregations" : {
    "day_of_week" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "Sunday",
          "doc_count" : 5
        }
      ]
    }
  }
}

搜索超时

编辑

默认情况下,搜索请求不会超时。该请求等待每个分片返回完整结果,然后才返回响应。

虽然 异步搜索 专为长时间运行的搜索而设计,您也可以使用 timeout 参数来指定您希望每个分片完成的等待时长。每个分片在指定的时间段内收集命中。如果在此时间段结束时未完成收集,Elasticsearch 将仅使用到那时为止累积的命中。搜索请求的整体延迟取决于搜索所需的分片数量以及并发分片请求的数量。

resp = client.search(
    index="my-index-000001",
    timeout="2s",
    query={
        "match": {
            "user.id": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    timeout: '2s',
    query: {
      match: {
        'user.id' => 'kimchy'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  timeout: "2s",
  query: {
    match: {
      "user.id": "kimchy",
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "timeout": "2s",
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  }
}

要为所有搜索请求设置集群范围的默认超时时间,请使用 集群设置 API 配置 search.default_search_timeout。如果在请求中没有传递 timeout 参数,则使用此全局超时时长。如果在搜索请求完成之前全局搜索超时到期,则使用 任务取消 来取消该请求。search.default_search_timeout 设置默认为 -1(无超时)。

搜索取消

编辑

您可以使用 任务管理 API 取消搜索请求。当客户端的 HTTP 连接关闭时,Elasticsearch 也会自动取消搜索请求。我们建议您设置客户端,以便在搜索请求中止或超时时关闭 HTTP 连接。

跟踪总命中数

编辑

通常,如果不访问所有匹配项,则无法准确计算总命中数,这对于匹配大量文档的查询来说成本很高。 track_total_hits 参数允许您控制应如何跟踪命中总数。鉴于命中数的下限通常就足够了,例如“至少有 10000 次命中”,默认设置为 10,000。这意味着请求将准确计数总命中数,最多计数 10,000 次命中。如果您不需要超过特定阈值后的准确命中数,则这是一个加速搜索的很好的权衡。

当设置为 true 时,搜索响应将始终准确跟踪与查询匹配的命中数(例如,当 track_total_hits 设置为 true 时,total.relation 将始终等于 "eq")。否则,搜索响应中 "total" 对象中返回的 "total.relation" 决定了应如何解释 "total.value"。值为 "gte" 表示 "total.value" 是与查询匹配的总命中的下限,值为 "eq" 表示 "total.value" 是准确的计数。

resp = client.search(
    index="my-index-000001",
    track_total_hits=True,
    query={
        "match": {
            "user.id": "elkbee"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    track_total_hits: true,
    query: {
      match: {
        'user.id' => 'elkbee'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  track_total_hits: true,
  query: {
    match: {
      "user.id": "elkbee",
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "track_total_hits": true,
  "query": {
    "match" : {
      "user.id" : "elkbee"
    }
  }
}

... 返回

{
  "_shards": ...
  "timed_out": false,
  "took": 100,
  "hits": {
    "max_score": 1.0,
    "total" : {
      "value": 2048,    
      "relation": "eq"  
    },
    "hits": ...
  }
}

与查询匹配的总命中数。

计数是准确的(例如,"eq" 表示等于)。

也可以将 track_total_hits 设置为整数。例如,以下查询将准确跟踪与查询匹配的总命中数,最多 100 个文档

resp = client.search(
    index="my-index-000001",
    track_total_hits=100,
    query={
        "match": {
            "user.id": "elkbee"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    track_total_hits: 100,
    query: {
      match: {
        'user.id' => 'elkbee'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  track_total_hits: 100,
  query: {
    match: {
      "user.id": "elkbee",
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "track_total_hits": 100,
  "query": {
    "match": {
      "user.id": "elkbee"
    }
  }
}

响应中的 hits.total.relation 将指示 hits.total.value 中返回的值是准确的 ("eq") 还是总数的下限 ("gte")。

例如,以下响应

{
  "_shards": ...
  "timed_out": false,
  "took": 30,
  "hits": {
    "max_score": 1.0,
    "total": {
      "value": 42,         
      "relation": "eq"     
    },
    "hits": ...
  }
}

42 个文档与查询匹配

计数是准确的 ("eq")

... 表示在 total 中返回的命中数是准确的。

如果与查询匹配的总命中数大于 track_total_hits 中设置的值,则响应中的总命中数将指示返回的值是下限

{
  "_shards": ...
  "hits": {
    "max_score": 1.0,
    "total": {
      "value": 100,         
      "relation": "gte"     
    },
    "hits": ...
  }
}

至少有 100 个文档与查询匹配

这是一个下限 ("gte")。

如果您根本不需要跟踪总命中数,则可以通过将此选项设置为 false 来提高查询速度

resp = client.search(
    index="my-index-000001",
    track_total_hits=False,
    query={
        "match": {
            "user.id": "elkbee"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    track_total_hits: false,
    query: {
      match: {
        'user.id' => 'elkbee'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  track_total_hits: false,
  query: {
    match: {
      "user.id": "elkbee",
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "track_total_hits": false,
  "query": {
    "match": {
      "user.id": "elkbee"
    }
  }
}

... 返回

{
  "_shards": ...
  "timed_out": false,
  "took": 10,
  "hits": {             
    "max_score": 1.0,
    "hits": ...
  }
}

命中总数未知。

最后,您可以通过在请求中将 "track_total_hits" 设置为 true 来强制进行准确计数。

track_total_hits 参数允许您在命中数准确性和性能之间进行权衡。一般来说,track_total_hits 的值越低,查询速度就越快,false 返回最快的结果。将 track_total_hits 设置为 true 将导致 Elasticsearch 返回精确的命中数,这可能会损害查询性能,因为它禁用了 Max WAND 优化。

快速检查匹配文档

编辑

如果您只想知道是否有任何文档匹配特定查询,可以将 size 设置为 0,表示我们对搜索结果不感兴趣。您还可以将 terminate_after 设置为 1,表示只要在每个分片上找到第一个匹配的文档,查询执行就可以终止。

resp = client.search(
    q="user.id:elkbee",
    size="0",
    terminate_after="1",
)
print(resp)
response = client.search(
  q: 'user.id:elkbee',
  size: 0,
  terminate_after: 1
)
puts response
const response = await client.search({
  q: "user.id:elkbee",
  size: 0,
  terminate_after: 1,
});
console.log(response);
GET /_search?q=user.id:elkbee&size=0&terminate_after=1

terminate_after 始终在 后置过滤器 (post_filter) 之后 应用,并在分片上收集到足够数量的命中结果时停止查询以及聚合执行。尽管聚合上的文档计数可能无法反映响应中的 hits.total,因为聚合是在后置过滤 之前 应用的。

由于 size 设置为 0,响应将不包含任何命中结果。hits.total 将等于 0,表示没有匹配的文档;或者大于 0,表示在提前终止时至少有那么多文档匹配查询。此外,如果查询被提前终止,则响应中的 terminated_early 标志将设置为 true。某些查询可以直接从索引统计信息中检索命中计数,这速度更快,因为它不需要执行查询。在这些情况下,不会收集任何文档,返回的 total.hits 将高于 terminate_after,并且 terminated_early 将设置为 false

{
  "took": 3,
  "timed_out": false,
  "terminated_early": true,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped" : 0,
    "failed": 0
  },
  "hits": {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score": null,
    "hits": []
  }
}

响应中的 took 时间包含此请求处理所花费的毫秒数,从节点接收到查询后快速开始,直到所有与搜索相关的工作完成,并在上面的 JSON 返回给客户端之前。这意味着它包括在线程池中等待、在整个集群上执行分布式搜索以及收集所有结果所花费的时间。