搜索操作

呃…​…​这个项目如果没有什么特别之处就不叫 elasticsearch 了!现在一起来聊聊客户端的搜索操作。

在命名方案规范的前提下,客户端拥有一切的查询权限,也拥有获取 REST API 公开的一切参数的权限。现在来看看一些示例,方便你熟悉这些语法规则。

Match查询

以下是 Match 查询的标准 curl 格式:

  1. curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
  2. "query" : {
  3. "match" : {
  4. "testField" : "abc"
  5. }
  6. }
  7. }'

而这里则是客户端构建的同样的查询:

  1. $params = [
  2. 'index' => 'my_index',
  3. 'type' => 'my_type',
  4. 'body' => [
  5. 'query' => [
  6. 'match' => [
  7. 'testField' => 'abc'
  8. ]
  9. ]
  10. ]
  11. ];
  12. $results = $client->search($params);

这里要注意 PHP 数组的结构与层次是怎样与 curl 中的 JSON 请求体格式相应对的。这种方式使得 JSON 的写法转换为 PHP 的写法变得十分简单。一个快速检测 PHP 数组是否为预期结果的方法,就是 encode 为 JSON 格式,然后进行检查:

  1. $params = [
  2. 'index' => 'my_index',
  3. 'type' => 'my_type',
  4. 'body' => [
  5. 'query' => [
  6. 'match' => [
  7. 'testField' => 'abc'
  8. ]
  9. ]
  10. ]
  11. ];
  12. print_r(json_encode($params['body']));
  13. {"query":{"match":{"testField":"abc"}}}

使用原生JSON

有时使用原生 JSON 来进行测试会十分方便,或者用原生 JSON 来进行不同系统的移植也同样方便。你可以在 body 中用原生 JSON 字符串,这样客户端会自动进行检查操作:

  1. $json = '{
  2. "query" : {
  3. "match" : {
  4. "testField" : "abc"
  5. }
  6. }
  7. }';
  8. $params = [
  9. 'index' => 'my_index',
  10. 'type' => 'my_type',
  11. 'body' => $json
  12. ];
  13. $results = $client->search($params);

搜索结果与 Elasticsearch 的响应结果一致,唯一不同的是 JSON 格式会转换成 PHP 数组。处理这些数据与数组迭代一样简单:

  1. $params = [
  2. 'index' => 'my_index',
  3. 'type' => 'my_type',
  4. 'body' => [
  5. 'query' => [
  6. 'match' => [
  7. 'testField' => 'abc'
  8. ]
  9. ]
  10. ]
  11. ];
  12. $results = $client->search($params);
  13. $milliseconds = $results['took'];
  14. $maxScore = $results['hits']['max_score'];
  15. $score = $results['hits']['hits'][0]['_score'];
  16. $doc = $results['hits']['hits'][0]['_source'];

Bool查询

利用客户端可以轻松构建 Bool 查询。例如以下查询:

  1. curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
  2. "query" : {
  3. "bool" : {
  4. "must": [
  5. {
  6. "match" : { "testField" : "abc" }
  7. },
  8. {
  9. "match" : { "testField2" : "xyz" }
  10. }
  11. ]
  12. }
  13. }
  14. }'

会构建为这样子(注意方括号位置):

  1. $params = [
  2. 'index' => 'my_index',
  3. 'type' => 'my_type',
  4. 'body' => [
  5. 'query' => [
  6. 'bool' => [
  7. 'must' => [
  8. [ 'match' => [ 'testField' => 'abc' ] ],
  9. [ 'match' => [ 'testField2' => 'xyz' ] ],
  10. ]
  11. ]
  12. ]
  13. ]
  14. ];
  15. $results = $client->search($params);

这里注意 must 语句接收的是数组。这里会转化为 JSON 数组,所以最后的响应结果与 curl 格式的响应结果一致。想了解 PHP 中数组和对象的转换,请查看用PHP处理JSON数组和JSON对象

更为复杂的示例

这里构建一个有点复杂的例子:一个 bool 查询包含一个 filter 过滤器和一个普通查询。这在 elasticsearch 的查询中非常普遍,所以这个例子会非常有用。

curl 格式的查询:

  1. curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
  2. "query" : {
  3. "bool" : {
  4. "filter" : {
  5. "term" : { "my_field" : "abc" }
  6. },
  7. "should" : {
  8. "match" : { "my_other_field" : "xyz" }
  9. }
  10. }
  11. }
  12. }'

而在 PHP 中:

  1. $params = [
  2. 'index' => 'my_index',
  3. 'type' => 'my_type',
  4. 'body' => [
  5. 'query' => [
  6. 'bool' => [
  7. 'filter' => [
  8. 'term' => [ 'my_field' => 'abc' ]
  9. ],
  10. 'should' => [
  11. 'match' => [ 'my_other_field' => 'xyz' ]
  12. ]
  13. ]
  14. ]
  15. ]
  16. ];
  17. $results = $client->search($params);

Scrolling(游标)查询

在用 bulk 时,经常要用 Scrolling 功能对文档进行分页处理,如输出一个用户的所有文档。这比常规的搜索要高效,因为这里不需要对文档执行性能消耗较大的排序操作。

Scrolling 会保留某个时间点的索引快照数据,然后用快照数据进行分页。游标查询窗口允许持续分页操作,即使后台正在执行索引文档、更新文档和删除文档。首先,你要在发送搜索请求时增加 scroll 参数。然后就会返回一个文档“页数”信息,还有一个用来获取 hits 分页数据的 scroll_id。

更多详情请查看​游标查询

以下代码更为深入的操作的示例:

  1. $client = ClientBuilder::create()->build();
  2. $params = [
  3. "scroll" => "30s", // how long between scroll requests. should be small!
  4. "size" => 50, // how many results *per shard* you want back
  5. "index" => "my_index",
  6. "body" => [
  7. "query" => [
  8. "match_all" => new \stdClass()
  9. ]
  10. ]
  11. ];
  12. // Execute the search
  13. // The response will contain the first batch of documents
  14. // and a scroll_id
  15. $response = $client->search($params);
  16. // Now we loop until the scroll "cursors" are exhausted
  17. while (isset($response['hits']['hits']) && count($response['hits']['hits']) > 0) {
  18. // **
  19. // Do your work here, on the $response['hits']['hits'] array
  20. // **
  21. // When done, get the new scroll_id
  22. // You must always refresh your _scroll_id! It can change sometimes
  23. $scroll_id = $response['_scroll_id'];
  24. // Execute a Scroll request and repeat
  25. $response = $client->scroll([
  26. "scroll_id" => $scroll_id, //...using our previously obtained _scroll_id
  27. "scroll" => "30s" // and the same timeout window
  28. ]
  29. );
  30. }