ElasticSearch:SpringBoot整合 - 复杂查询的使用

郎家岭伯爵 2024年06月23日 216次浏览

前言

ElasticSearch 作为一个数据存储工具,有些场景下需要执行一些复杂的查询,此时若使用 SearchSourceBuilder 来构建查询条件就比较费劲,我们将使用 client.getLowLevelClient().performRequest() 来实现。

实现

理论部分

client.getLowLevelClient()

  • client 是一个 RestHighLevelClient 对象,它是 Elasticsearch 高级 REST 客户端。
  • getLowLevelClient() 方法返回一个低级别的客户端 RestClient低级客户端提供了更灵活和直接的 HTTP 请求接口,但没有高级客户端提供的高级功能

performRequest(request)

  • performRequest(Request request) 方法是 RestClient 提供的,用于执行同步 HTTP 请求。
  • request 是一个 Request 对象,代表了要发送的 HTTP 请求。

代码部分

例如我们需要在文档 ID 为 1,2,3,4,5 等 5 个文档内查找完全被包含在给定多边形内的文档,并且指定分页参数。

注:

  • 在 ElasticSearch 的图形关系中,常用的有三种关系——相交(intersects)包含(contains)被包含(within)。这里需要理解下:在查询语句里,我们构建的是条件,类似于 MySQL 中的 where。例如我们要查询 within 关系的数据(类比在 MySQL 中查询 age>20 的数据),查询到的数据应该是 within(被包含) 在条件图形内的数据(类比在 MySQL 中结果是 age>20 的数据)。这里 contains 和 within 关系比较绕,大家可搞一些测试数据跑一下,加深一下理解。

ElasticSearch命令行实现

POST /langjialing_index/_search
{
  "from": 0, 
  "size": 3,
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "_id": [1, 2, 3, 4, 5]
          }
        },
        {
          "geo_shape": {
            "geo_shape": {
              "shape": {
                "type": "multipolygon",
                "coordinates": [[
                  [
                    [90, 50],
                    [150, 50],
                    [150, 30],
                    [90, 30],
                    [90, 50]
                  ]
                ]]
              },
              "relation": "within"
            }
          }
        }
      ]
    }
  }
}

Java代码实现

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author 郎家岭伯爵
 * @time 2024年6月21日16:47:48
 */

@RestController
@RequestMapping("/es4")
@Slf4j
public class EsController4 {

    /**
     * Elasticsearch客户端
     */
    @Autowired
    private RestHighLevelClient client;

    @GetMapping("/polygonRelation")
    public void polygonRelation() throws IOException {

        /*
           POST /langjialing_index/_search
            {
              "from": 0,
              "size": 3,
              "query": {
                "bool": {
                  "must": [
                    {
                      "terms": {
                        "_id": [1, 2, 3, 4, 5]
                      }
                    },
                    {
                      "geo_shape": {
                        "geo_shape": {
                          "shape": {
                            "type": "multipolygon",
                            "coordinates": [[
                              [
                                [90, 50],
                                [150, 50],
                                [150, 30],
                                [90, 30],
                                [90, 50]
                              ]
                            ]]
                          },
                          "relation": "within"
                        }
                      }
                    }
                  ]
                }
              }
            }
         */
        String s = "{\n" +
                "  \"from\": 0, \n" +
                "  \"size\": 3,\n" +
                "  \"query\": {\n" +
                "    \"bool\": {\n" +
                "      \"must\": [\n" +
                "        {\n" +
                "          \"terms\": {\n" +
                "            \"_id\": [1, 2, 3, 4, 5]\n" +
                "          }\n" +
                "        },\n" +
                "        {\n" +
                "          \"geo_shape\": {\n" +
                "            \"geo_shape\": {\n" +
                "              \"shape\": {\n" +
                "                \"type\": \"multipolygon\",\n" +
                "                \"coordinates\": [[\n" +
                "                  [\n" +
                "                    [90, 50],\n" +
                "                    [150, 50],\n" +
                "                    [150, 30],\n" +
                "                    [90, 30],\n" +
                "                    [90, 50]\n" +
                "                  ]\n" +
                "                ]]\n" +
                "              },\n" +
                "              \"relation\": \"within\"\n" +
                "            }\n" +
                "          }\n" +
                "        }\n" +
                "      ]\n" +
                "    }\n" +
                "  }\n" +
                "}";
        Request request = new Request("POST","/langjialing_index/_search");
        request.setJsonEntity(s);
        Response response = client.getLowLevelClient().performRequest(request);
        String responseStr = EntityUtils.toString(response.getEntity());
        System.out.println(responseStr);
    }

}

番外

多种图形关系查询

如果我们要查询 withinintersects 两部分数据该怎样构建查询语句呢?

这类似于我们在 MySQL 中查询 age>20 or age<25 的数据,在 ElasticSearch 中,用 should 来表示 的关系。

用 ElasticSearch 命令行我们这样来实现

GET /langjialing_index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "geo_shape": {
            "geo_shape": {
              "shape": {
                "type": "multipolygon",
                "coordinates": [[
                  [
                    [113.6, 39.3],
                    [114.5, 38.3],
                    [114.5, 38],
                    [113.5, 38],
                    [113.6, 39.3]
                  ]]
                ]
              },
              "relation": "intersects"
            }
          }
        },
        {
          "geo_shape": {
            "geo_shape": {
              "shape": {
                "type": "multipolygon",
                "coordinates": [[
                  [
                    [113.6, 39.3],
                    [114.5, 38.3],
                    [114.5, 38],
                    [113.5, 38],
                    [113.6, 39.3]
                  ]]
                ]
              },
              "relation": "within"
            }
          }
        }
      ]
    }
  }
}

Java 代码实现与上面示例完全一样,这里不再赘述。

常用的查询语句

-- 查看ElasticSearch信息
GET /

-- 创建索引并指定字段映射
PUT langjialing_index
{
  "settings": {
    "index": {
      "number_of_shards": 2,  // 将分片数设置为2,一般为 节点数 × 2
      "number_of_replicas": 1 // 将副本数设置为1,一般为 节点数 × 1
    }
  },
  "mappings": {
    "properties": {
      "geo_point": {
        "type": "geo_point"
      },
	  "geo_json": {
		"type": "geo_shape"
	  }
    }
  }
}

-- 查看索引字段映射
GET langjialing_index/_mapping

-- 查看索引设置
GET langjialing_index/_settings

-- 查看所有索引状态
GET _cat/indices

-- 查看节点状态,输出结果的每一行代表一个节点
GET /_cat/nodes?v

-- 解释索引分片的分配情况
GET _cluster/allocation/explain
{
  "index": "langjialing_index",
  "primary": true,
  "shard": 0
}

-- 新增文档
PUT /langjialing_index/_doc/1
{
  "name": "John Doe1"
}

-- 更新单个字段
POST langjialing_index/_update/1
{
  "doc": {
    "name":"langjialing"
  }
}

-- 根据ID获取单个文档
GET /langjialing_index/_doc/1


-- 分页获取数据并按照age字段排序
GET /langjialing_index/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "age": "asc" }
  ],
  "from": 10,
  "size": 10
}


-- 查询name为John Doe1的数据
GET /langjialing_index/_search
{
  "query": {
    "match_phrase": {
      "name": "John Doe1"
    }
  }
}

-- 查询name为langjialing,且age不为25的数据
GET /langjialing_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "langjialing" } }
      ],
      "must_not": [
        { "match": { "age": "25" } }
      ]
    }
  }
}

-- 根据name分组
GET /langjialing_index/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "name.keyword"
      }
    }
  }
}

总结

Java 代码使用 ElasticSearch 的复杂查询。

赞助页面示例