Elasticsearch (ES)

elasticsearch是一款非常强大的开源搜索引擎,可以从海量数据中快速找到需要的内容。

elasticsearch结合kibana,Logstash,Beats,构成啦elastic stack(ELK)。被广泛应用于日志数据分析,实时监控等领域。

elasticsearch是elastic stack的核心,负责存储搜索,分析数据。

elasticsearch底层是基于lucene实现的。

Lucene

Lucene是一个java语言的搜索引擎类库,提供啦搜索引擎的核心API。是Apache公司的项目。(逐渐被淘汰)

官网地址:Apache Lucene - Welcome to Apache Lucene

Lucene 的优点:易扩展,高性能(基于倒叙索引)

缺点:只限于java,不支持水平扩展(通过增加更多的服务器或者程序实例来分散负载),学习难度高

elasticsearch

支持分布式,可水平扩展。提供Restful接口,可被任何语言调用。

elasticsearch采用倒排索引:

正向索引:最传统的根据id索引的方式,再根据词条查询时,先逐条获取文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程

倒排索引:先找到用户要搜索的词条,根据词条得到包含词条的文档的id,再根据id获取文档。是根据词条找文档的过程

文档(document)用于搜索的数据,其中的每一条数据就是一个文档

词条 (term):文档按照语义分成的词语

插入数据时创建倒排索引就是对正向索引的一种特殊处理,流程如下:1.将文档插入到索引库中 2.插入时会根据字段的类型来决定分词得到词条  3. 将词条和文档ID的对应关系存储到倒排索引表中

查询数据时使用倒排索引:1.ES根据用户搜索的关键字找到对应字段的分词器来进行分词得到词条 2.根据词条从倒排索引表中查询ID  3.再根据文档ID从索引库中查询到具体文档数据

ES的一些概念

es是面向文档(Document)存储的,文档数据会被序列化为json格式后存储再es中

json文档中包含很多字段(Field)类似数据库中的表

索引(index)相同类型的文档的集合

映射(mapping)是索引中文档的字段约束信息,类似表的结构约束

ES不能替代MySQL:ES没有多表关联设计,没有事务管理

在企业中,往往是两者结合使用:

对安全要求较高的写操作,用mysql实现。对查询性能要求较高的搜索需求,用elasticsearch实现

两者基于某种方式,实现数据的同步,保证一致性

索引库的操作

索引库类似数据库,mapping映射类似表的结构

mapping映射属性:mapping是索引库中文档的约束

  • type:字段数据类型,常见的简单类型有:

    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

    • 数值:long、integer、short、byte、double、float、

    • 布尔:boolean

    • 日期:date

    • 对象:object

  • index:是否创建索引,默认为true

  • analyzer:使用哪种分词器

  • properties:该字段的子字段

下面的案例统一使用kibana编写的DSL的方式演示

创建索引库和映射:请求方式:PUT  请求路径:/索引库(自定义)    请求参数:mapping映射

PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

查询索引库

基本语法:请求方式GET  请求路径:/索引库名  请求参数:无

GET /索引库名

修改索引库:索引库一旦创建,无法修改mapping

无法修改mapping,但允许添加新的字段到mapping中

PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

删除索引库:请求方式:DELETE     请求路径:/索引库名    请求参数:无

DELETE /索引库名

文档操作

新增文档

POST /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子属性1": "值3",
        "子属性2": "值4"
    },
    // ...
}

查询文档

GET /{索引库名称}/_doc/{id}

删除文档

DELETE /{索引库名}/_doc/id值

修改文档两种方式:1.全量修改,直接覆盖原来的文档   2.增量修改,修改文档中的部分字段

1.全量修改:本质是根据指定id删除文档,新增一个相同id的文档

PUT /{索引库名}/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}

2.增量修改,只修改指定id匹配文档中的部分字段

POST /{索引库名}/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}

RestAPI

ES

es官方提供各种不同语言的客户端,用来操作es

官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.htm

选择Java HighLevel Rest Client客户端API

在idea中的应用

在elasticsearch提供的API中,与elasticsearch一切交互都封装在一个名为RestHighLevelClient的类中,必须先完成这个对象的初始化,建立与elasticsearch的连接。

1)引入es的RestHighLevelClient依赖:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

2)因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:

<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

3)初始化RestHighLevelClient:

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://虚拟机ip:9200")
));

3)初始化RestHighLevelClient:

初始化的代码如下:

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
));

为了单元测试方便,我们创建一个测试类HotelIndexTest,然后将初始化的代码编写在@BeforeEach方法中

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class HotelIndexTest {
     //初始化ES客户端连接实例
    private RestHighLevelClient client;
    //单元测试执行前创建客户端实例
    @BeforeEach
    void initClient() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://虚拟机ip:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

RestClient 实现操作es的索引库

1.创建客户端连接实例RestHighLevelClient

2.创建请求对象

索引库:创建CreateIndexRequest   删除DeleteIndexRequest   查询GetIndexRequest

文档:新增IndexRequest 查询 GetRequest 修改UpdateRequest 删除DeleteRequest

3.可能会设置数据

4.使用RestHighLevelClient实例执行请求获取响应

package cn.itcast.hotel;


import cn.itcast.hotel.util.HotelConstants;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
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.client.indices.GetIndexResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

/**
 * RestClient操作ES索引库相关测试
 */
public class HotelIndexTest {

    //初始化ES客户端连接实例
    private RestHighLevelClient client ;

  

    //单元测试执行前创建客户端实例
    @BeforeEach
    void initClient(){
        client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://虚拟机中ip:9200")));
    }

    //单元测试执行后关系客户端实例
    @AfterEach
    void closeClient(){
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//创建索引库hotel
    @Test
    void testCreateIndex(){
        //1.构建索引请求实例
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("hotel");

        //2.构造DSL语句中的JSON(索引库映射关系的结构)
        createIndexRequest.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);

        //3.发起请求执行创建索引库和映射关系
        try {
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);

            //4.获取响应信息
            System.out.println(createIndexResponse.isAcknowledged());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//查询索引库
    @Test
    void testGetIndex(){
        //1.定义获取索引的请求实例
        GetIndexRequest getIndexRequest = new GetIndexRequest("hotel");
        try {
            //2.发起请求,检查索引库是否存在
            boolean result = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);

            //3.输出结果
            System.out.println("索引库是否存在:" + result);

            //4.发起请求获取索引库映射信息
            GetIndexResponse getIndexResponse = client.indices().get(getIndexRequest, RequestOptions.DEFAULT);
            System.out.println("具体的映射信息:" + JSON.toJSONString(getIndexResponse.getMappings()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    void testDeleteIndex(){

        //1.构建删除索引请求对象
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("hotel");
        try {

            //2.发起请求执行删除索引库
            AcknowledgedResponse acknowledgedResponse = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);

            //3.输出响应结果
            System.out.println(acknowledgedResponse.isAcknowledged());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

RestClient操作文档

我们导入酒店数据,基本流程一致,但是需要考虑几点变化:

  • 酒店数据来自于数据库,我们需要先查询出来,得到hotel对象

  • hotel对象需要转为HotelDoc对象

  • HotelDoc需要序列化为json格式

因此,代码整体步骤如下:

  • 1)根据id查询酒店数据Hotel

  • 2)将Hotel封装为HotelDoc

  • 3)将HotelDoc序列化为JSON

  • 4)创建IndexRequest,指定索引库名和id

  • 5)准备请求参数,也就是JSON文档

  • 6)发送请求

编写单元测试

package cn.itcast.hotel;

import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.service.impl.HotelService;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.List;

/**
 * 酒店文档数据CRUD相关测试
 */
@SpringBootTest
public class HotelDocumentTest {

    //初始化ES客户端连接实例
    private RestHighLevelClient client ;

    

    //单元测试执行前创建客户端实例
    @BeforeEach
    void initClient(){
        client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.200.200:9200")));
    }

    //单元测试执行后关系客户端实例
    @AfterEach
    void closeClient(){
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Autowired
    private HotelService hotelService;

    /**
     * 创建文档
     */
    @Test
    void testAddDocument(){
        //1.从表中查询一条酒店数据
        Hotel hotel = hotelService.getById(36934);

        //2.将Hotel数据转为ES需要的HotelDoc数据
        HotelDoc hotelDoc = new HotelDoc(hotel);

        //3.将HotelDoc转为JSON数据
        String hotelDocJson = JSON.toJSONString(hotelDoc);

        //4.创建Index请求对象
        IndexRequest indexRequest = new IndexRequest("hotel");

        //5.设置JSON数据 (DSL的数据)   指定索引库名和id
        indexRequest.source(hotelDocJson, XContentType.JSON).id(hotelDoc.getId().toString());

        //6.使用client发起请求执行新增文档
        try {
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);

            //7.输出响应结果
            System.out.println(indexResponse.getResult().getLowercase());// created
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询文档
     */
    @Test
    void testGetDocument(){
        //1.创建查询文档请求实例
        GetRequest getRequest = new GetRequest("hotel","36934");

        //2.发起请求查询文档
        try {
            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);

            //输出文档的JSON数据
            String hotelDocJson = getResponse.getSourceAsString();

            System.out.println("文档数据:" + hotelDocJson);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新文档
     */
    @Test
    void testUpdateDocument(){
        //1.创建更新文档请求实例
        UpdateRequest updateRequest = new UpdateRequest("hotel","36934");
        //2.设置要更新的字段和值
        updateRequest.doc("address","静安交通路41号", "business", "四川南路商业区");
        try {
            //3.发起请求执行更新文档
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);

            //4.输出响应结果
            String result = updateResponse.getResult().getLowercase(); //updated
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除文档
     */
    @Test
    void testDeleteDocumnet(){

        //1.创建删除文档请求实例
        DeleteRequest deleteRequest = new DeleteRequest("hotel","36934");

        try {

            //2.发起请求执行删除文档
            DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

           //3.输出响应结果
            String result = deleteResponse.getResult().getLowercase(); //deleted
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 }

批量导入文档

  • 1)创建Request对象。这里是BulkRequest

  • 2)准备参数。批处理的参数,就是其它Request对象,这里就是多个IndexRequest

  • 3)发起请求。这里是批处理,调用的方法为client.bulk()方法

@Test
void testBulkRequest() throws IOException {
    // 批量查询酒店数据
    List<Hotel> hotels = hotelService.list();

    // 1.创建Request
    BulkRequest request = new BulkRequest();
    // 2.准备参数,添加多个新增的Request
    for (Hotel hotel : hotels) {
        // 2.1.转换为文档类型HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 2.2.创建新增文档的Request对象
        request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    }
    // 3.发送请求
    client.bulk(request, RequestOptions.DEFAULT);
}

文档操作的基本步骤:

  • 初始化RestHighLevelClient

  • 创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk

  • 准备参数(Index、Update、Bulk时需要)

  • 发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk

  • 解析结果(Get时需要)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值