ElasticSearch:SpringBoot整合 - 复杂图形的使用

郎家岭伯爵 2024年06月21日 258次浏览

前言

前面我们介绍了在 SpringBoot 中操作 ElasticSearch,进行单条批量数据的增删改查,ElasticSearch 还支持对地理图形的操作。

这里我们将介绍使用 SpringBoot 在 ElasticSearch 中对地理图形进行操作。

实现

准备工作

前面我们完成了 ELK 环境的搭建,这里我们会使用 Kibana 来观察我们创建出来的图形,以及对索引数据操作均在 Kibana 的可视化界面中进行

创建索引

命令行创建

在 ElasticSearch 的数据类型中,有两种与地理图形相关:

  • geo_point:地理坐标点;
  • geo_shape:地理图形。

使用如下语句创建索引:

PUT langjialing_index
{
  "mappings": {
    "properties": {
      "geo_point": {
        "type": "geo_point"
      },
      "geo_shape":{
        "type": "geo_shape"
      }
    }
  }
}

代码创建

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
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.RequestParam;
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;

    /**
     * 创建复杂结构的索引。
     * @param indexName indexName
     * @throws IOException IOException
     */
    @GetMapping("/createIndex")
    public void createIndex(@RequestParam String indexName) throws IOException {

        // 创建索引和映射
        // 判断索引是否存在
        GetIndexRequest getRequest = new GetIndexRequest(indexName);
        boolean exists = client.indices().exists(getRequest, RequestOptions.DEFAULT);
        if (!exists) {
            /**
             * -- 创建索引并指定字段映射
             * PUT langjialing_index
             * {
             *   "settings": {
             *     "number_of_replicas": 1, // 将副本数设置为1,一般为 节点数 × 1
             *     "number_of_shards": 2    // 将分片数设置为2,一般为 节点数 × 2
             *   },
             *   "mappings": {
             *     "properties": {
             *       "geo_point": {
             *         "type": "geo_point"
             *       },
             *       "geo_json":{
             *         "type": "geo_shape"
             *       }
             *     }
             *   }
             * }
             */
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
            createIndexRequest.settings(Settings
                    .builder()
                    .put("index.number_of_shards", 2)
                    .put("index.number_of_replicas", 1));
            String mapping = "{\n" +
                    "    \"properties\": {\n" +
                    "        \"geo_shape\": {\n" +
                    "            \"type\": \"geo_shape\"\n" +
                    "        },\n" +
                    "        \"geo_point\": {\n" +
                    "            \"type\": \"geo_point\"\n" +
                    "        }\n" +
                    "    }\n" +
                    "}";
            createIndexRequest.mapping(mapping, XContentType.JSON);
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        }
    }
}

查看创建出的索引的字段映射

GET langjialing_index/_mapping

查看创建出的索引的设置

GET langjialing_index/_settings

Kibana配置图形可视化

  1. 配置索引:

  2. 选择索引:

  3. 选择要展示的索引:

  4. 选择数据类型:

  5. 设置图形颜色并保存:

  6. 刷新展示的数据:

地理图形操作

Point

geo_shape 也可以存储坐标点数据,它的坐标信息由一个一维数组组成。

命令行创建

POST langjialing_index/_doc/10
{
  "geo_shape": {
  "type": "point",
  "coordinates": [ 100, 40 ]
  }
}

代码创建

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
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.RequestParam;
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;

    /**
     * 创建Point点
     * @param indexName 索引名称
     * @param docId 文档ID
     * @throws IOException IOException
     */
    @GetMapping("/createPoint")
    public void createPoint(@RequestParam String indexName, @RequestParam String docId) throws IOException {

        String s = "{\n" +
                "  \"geo_shape\": {\n" +
                "  \"type\": \"point\",\n" +
                "  \"coordinates\": [ 100, 40 ]\n" +
                "  }\n" +
                "}";

        // 索引文档
        IndexRequest indexRequest = new IndexRequest(indexName);
        indexRequest.id(docId);
        indexRequest.source(s, XContentType.JSON);
        client.index(indexRequest, RequestOptions.DEFAULT);

        System.out.println("Document indexed successfully.");
    }
}

Linestring

Linestring 由一个二维数组组成。

我们将创建如下线段:

image-1718963500182

命令行创建

POST langjialing_index/_doc/10
{
  "geo_shape": {
  "type": "LineString",
  "coordinates": [[ 100, 40 ], [100, 45]]
  }
}

代码创建

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
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.RequestParam;
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;

    /**
     * 创建Point点
     * @param indexName 索引名称
     * @param docId 文档ID
     * @throws IOException IOException
     */
    @GetMapping("/createLine")
    public void createLine(@RequestParam String indexName, @RequestParam String docId) throws IOException {

        String s = "{\n" +
                "  \"geo_shape\": {\n" +
                "  \"type\": \"LineString\",\n" +
                "  \"coordinates\": [[ 100, 40 ], [100, 45]]\n" +
                "  }\n" +
                "}";

        // 索引文档
        IndexRequest indexRequest = new IndexRequest(indexName);
        indexRequest.id(docId);
        indexRequest.source(s, XContentType.JSON);
        client.index(indexRequest, RequestOptions.DEFAULT);

        System.out.println("Document indexed successfully.");
    }
}

Polygon

Polygon 的边界信息由一个三维数组组成,可以在多边形中进行挖洞。

接下来我们将在 ElasticSearch 中创建如下 Polygon 图形:

命令行创建

POST langjialing_index/_doc/10
{
  "geo_shape": {
  "type": "Polygon",
  "coordinates": 
    [
        [
            [ 100, 40 ],
            [ 105, 40 ],
            [ 105, 35 ],
            [ 100, 35 ],
            [ 100, 40 ]
        ],
        [
            [ 102, 38 ],
            [ 104, 38 ],
            [ 104, 36 ],
            [ 102, 36 ],
            [ 102, 38 ]
        ]
    ]
  }
}

代码创建

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
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.RequestParam;
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;

    /**
     * 创建Polygon图形
     * @param indexName 索引名称
     * @param docId 文档ID
     * @throws IOException IOException
     */
    @GetMapping("/createPolygon")
    public void createPolygon(@RequestParam String indexName, @RequestParam String docId) throws IOException {

        String s = "{\n" +
                "  \"geo_shape\": {\n" +
                "  \"type\": \"Polygon\",\n" +
                "  \"coordinates\": \n" +
                "    [\n" +
                "        [\n" +
                "            [ 100, 40 ],\n" +
                "            [ 105, 40 ],\n" +
                "            [ 105, 35 ],\n" +
                "            [ 100, 35 ],\n" +
                "            [ 100, 40 ]\n" +
                "        ],\n" +
                "        [\n" +
                "            [ 102, 38 ],\n" +
                "            [ 104, 38 ],\n" +
                "            [ 104, 36 ],\n" +
                "            [ 102, 36 ],\n" +
                "            [ 102, 38 ]\n" +
                "        ]\n" +
                "    ]\n" +
                "  }\n" +
                "}";

        // 索引文档
        IndexRequest indexRequest = new IndexRequest(indexName);
        indexRequest.id(docId);
        indexRequest.source(s, XContentType.JSON);
        client.index(indexRequest, RequestOptions.DEFAULT);

        System.out.println("Document indexed successfully.");
    }
}

MultiPolygon

MutiPolygon 是 ElasticSearch 中最为复杂的图形,它的边界信息由一个四维数组组成,其中有多个挖洞、飞地等部分。

接下来我们将在 ElasticSearch 中创建如下 MultiPolygon 图形:

命令行创建

POST langjialing_index/_doc/10
{
  "geo_shape": {
  "type": "MultiPolygon",
  "coordinates": [
        [
            [
                [ 100, 40 ],
                [ 105, 40 ],
                [ 105, 35 ],
                [ 100, 35 ],
                [ 100, 40 ]
            ],
            [
                [ 102, 38 ],
                [ 104, 38 ],
                [ 104, 36 ],
                [ 102, 36 ],
                [ 102, 38 ]
            ]
        ],
        [
            [
                [ 120, 40 ],
                [ 123, 40 ],
                [ 123, 37 ],
                [ 120, 37 ],
                [ 120, 40 ]
            ]
        ]
    ]
  }
}

代码创建

package com.langjialing.helloworld.controlle1;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
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.RequestParam;
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;

    /**
     * 创建MultiPolygon图形
     * @param indexName 索引名称
     * @param docId 文档ID
     * @throws IOException IOException
     */
    @GetMapping("/createMultiPolygon")
    public void createMultiPolygon(@RequestParam String indexName, @RequestParam String docId) throws IOException {

        // 这里是一个JSON格式的字符串。
        String s = "{\n" +
                "    \"geo_shape\": {\n" +
                "        \"coordinates\": [\n" +
                "            [\n" +
                "                [\n" +
                "                    [\n" +
                "                        100.0,\n" +
                "                        40.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        105.0,\n" +
                "                        40.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        105.0,\n" +
                "                        35.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        100.0,\n" +
                "                        35.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        100.0,\n" +
                "                        40.0\n" +
                "                    ]\n" +
                "                ],\n" +
                "                [\n" +
                "                    [\n" +
                "                        102.0,\n" +
                "                        38.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        104.0,\n" +
                "                        38.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        104.0,\n" +
                "                        36.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        102.0,\n" +
                "                        36.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        102.0,\n" +
                "                        38.0\n" +
                "                    ]\n" +
                "                ]\n" +
                "            ],\n" +
                "            [\n" +
                "                [\n" +
                "                    [\n" +
                "                        120.0,\n" +
                "                        40.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        123.0,\n" +
                "                        40.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        123.0,\n" +
                "                        37.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        120.0,\n" +
                "                        37.0\n" +
                "                    ],\n" +
                "                    [\n" +
                "                        120.0,\n" +
                "                        40.0\n" +
                "                    ]\n" +
                "                ]\n" +
                "            ]\n" +
                "        ],\n" +
                "        \"type\": \"MultiPolygon\"\n" +
                "    }\n" +
                "}";
        // 索引文档
        IndexRequest indexRequest = new IndexRequest(indexName);
        indexRequest.id(docId);
        indexRequest.source(s, XContentType.JSON);
        client.index(indexRequest, RequestOptions.DEFAULT);

        System.out.println("Document indexed successfully.");
    }
}

总结

SpringBoot 整合 ElasticSearch,操作复杂图形

以上几种图形的操作,本质是一样的,都是在 ElasticSearch 中对 geo_shape 类型的数据进行的基本操作。