前言
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);
}
}
番外
多种图形关系查询
如果我们要查询 within
或 intersects
两部分数据该怎样构建查询语句呢?
这类似于我们在 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 的复杂查询。