ElasticSearch

ElasticSearch基础

1、概述

Elasticsearch:官方分布式搜索和分析引擎 | Elastic

​ Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为 Elastic Stack 的核心,Elasticsearch 会集中存储您的数据,让您飞快完成搜索,微调相关性,进行强大的分析,并轻松缩放规模。

​ The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。 能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视 化。Elaticsearch,简称为 ES,==ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心。==它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上 百台服务器,处理 PB 级别的数据。

1.1、全文搜索引擎

​ 像谷歌,百度这样的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索时输入的关键字,它们会将该关键字即索引匹配到的所有网页返回。还有常见的项目中使用的日志搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。

​ 一般的传统数据库,全文检索都实现的很鸡肋,因为一般人也不会使用数据库去存文本字段。进行全文检索还需要扫描整个表,如果数据量很大,即使对SQL进行了语法优化和性能调优,也没有什么效果,就算建立了索引维护起来也非常麻烦,对于insert和update操作都会重构索引。所以说,在生产环境中使用常规的搜索方式性能很差劲。

  • 搜索的数据对象是大量的非结构化的文本数据;
  • 文件记录量达到数十万或数百万甚至更多;
  • 支持大量基于交互式文本的查询;
  • 需求非常灵活的全文搜索查询;
  • 对高度相关的搜索结果有特殊的需求,但是没有可用的关系数据库可用满足;
  • 对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况;

​ 为了解决结构化数据搜索和非结构化数据搜索性能问题,我们需要更专业、健壮更强大的全文搜索引擎,这里的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式,相当于查字典中检索字表查字的过程。

1.2、Elasticsearch和Solr

​ Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目,提供了一个简单却强大的 应用程式接口,能够做全文索引和搜寻。在 Java 开发环境里 Lucene 是一个成熟的免费开源 工具。就其本身而言,Lucene 是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。 但 Lucene 只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的 服务框架搭建起来进行应用。

​ 目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch 和 Solr,这两款都是基 于 Lucene 搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了 服务器安装、部署、管理、集群以外,对于数据的操作 修改、添加、保存、查询等等都十 分类似。

1.3、Elasticsearch应用案例

  • GitHub: 2013 年初,抛弃了 Solr,采取 Elasticsearch 来做 PB 级的搜索。“GitHub 使用 Elasticsearch 搜索 20TB 的数据,包括 13 亿文件和 1300 亿行代码”;
  • 维基百科:启动以 Elasticsearch 为基础的核心搜索架构;
  • SoundCloud:“SoundCloud 使用 Elasticsearch 为 1.8 亿用户提供即时而精准的音乐搜索 服务”;
  • 百度:目前广泛使用 Elasticsearch 作为文本数据分析,采集百度所有服务器上的各类指 标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常 或业务层面异常。目前覆盖百度内部 20 多个业务线(包括云分析、网盟、预测、文库、 直达号、钱包、风控等),单集群最大 100 台机器,200 个 ES 节点,每天导入 30TB+ 数据;
  • 新浪:使用 Elasticsearch 分析处理 32 亿条实时日志;
  • 阿里:使用 Elasticsearch 构建日志采集和分析体系;
  • Stack Overflow:解决 Bug 问题的网站,全英文,编程人员交流的网站;

2、安装Elasticsearch

7.8.0版本的下载地址:WINDOWS (elastic.co)

下载完成之后解压即可,找到bin目录双击elasticsearch.bat 文件启动 ES 服务

image-20240423102651368

注意:9300端口为Elasticsearch集群间组件的通讯端口,9200是浏览器访问的http协议RESTful端口

访问:localhost:9200非常明显的一个json串

image-20240423102924823

2.1、RESTful

​ REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就 是 RESTful。Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之 间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用 服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。

​ 在服务器端,应用程序状态和功能可用分为各种资源。资源是一个概念实体,它向客户端公开,资源的例子有:应用程序对象、数据库记录、算法等等。每一个资源都使用URI得到一个唯一的地址。所有资源都是共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的HTTP方法,比如GET POST PUT DELETE

​ 在REST样式的Web服务中,每一个资源都有一个地址。资源本身就是方法调用的目标,方法列表对所有资源都是一样的。这些都是标准方法。如果想要访问互联网上的资源,就必须向资源所在服务器发出请求,请求体中必须包含资源的网络路劲,以及对资源的操作,增删改查。

2.2、数据格式

​ ES是面向文档型数据库,一条数据在这里就是一个文档,下面是Elasticsearch里存储文档数据和关系型数据库MySQL存储数据的概念进行一个类比图:
image-20240423111911864

​ ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。 这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type,Elasticsearch 7.X 中, Type 的概念已经被删除了。

2.2.1、倒排索引

正排索引

idcontent
111my name is zhang san
222my name is lisi
333my name is wangwu

正常情况下,正向的索引,要找关键字zhang san你就得先找到文档的id然后才能匹配到。

但是,倒排索引主打的就是一个关键字和文档编号的一个关联,那么这里表的作用就没有了。

倒排索引

keywordid
name111,222,333
zhang111
li222

2.2.2、索引操作

创建索引

​ 对比于关系型数据库,创建索引就等同于创建数据库,使用Postman,向Elasticsearch发送PUT请求:http://localhost:9200/shopping

image-20240423164300559

{
 "acknowledged"【响应结果】: true, # true 操作成功
 "shards_acknowledged"【分片结果】: true, # 分片操作成功
 "index"【索引名称】: "shopping"
}

如果重复添加索引,会返回错误信息提示已经存在,幂等性原因,重复请求同一个结果是一样的。

image-20240423164752793

创建完成之后就可以查看它的信息直接还是原来的请求地址,把请求方式改成GET

image-20240423171148165

{
 "shopping"【索引名】: { 
 "aliases"【别名】: {},
 "mappings"【映射】: {},
 "settings"【设置】: {
 "index"【设置 - 索引】: {
 "creation_date"【设置 - 索引 - 创建时间】: "1614265373911",
 "number_of_shards"【设置 - 索引 - 主分片数量】: "1",
 "number_of_replicas"【设置 - 索引 - 副分片数量】: "1",
 "uuid"【设置 - 索引 - 唯一标识】: "eI5wemRERTumxGCc1bAk2A",
 "version"【设置 - 索引 - 版本】: {
 "created": "7080099"
 },
 "provided_name"【设置 - 索引 - 名称】: "shopping"
 }
 }
 }
}

查看所有索引

在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/_cat/indices?v

image-20240423173333900

​ 这里请求路径中的_cat 表示查看的意思,indices 表示索引,所以整体含义就是查看当前 ES 服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉.

各表头含义:

表头含义
health当前服务器健康状态:gree(集群完整)、yellow(单点正常、集群不完整)、red(单点不正常)
status索引打开、关闭状态
index索引名
uuid索引唯一标识
pri主分片数量
rep副本数量
docs.count可用文档数量
docs.deleted文档删除状态(逻辑删除)
store.size主分片和副分片整体占空间大小
pri.store.size主分片占空间大小

2.2.3、文档操作

创建文档

​ 索引创建完成之后创建文档,并添加数据。这里的文档就类比于关系型数据库中的表数据,添加的数据格式为JSON。在postman中向ES服务器发送POST请求:http://127.0.0.1:9200/shopping/_doc

​ 光发请求当然没有用,光有请求头不行还得有请求体,我们这里请求的时候没有指定它的id,ES服务器会自动生成一个的。

{
 "title":"小米手机",
 "category":"小米",
 "images":"http://www.gulixueyuan.com/xm.jpg",
 "price":3999.00
}

image-20240423174618628

{
    "_index"【索引】:: "shopping",
    "_type": 【类型-文档】:"_doc",
    "_id":【唯一标识】: "9wtYCo8B2nSVrWL727Lx",#可以类比为 MySQL 中的主键,随机生成
    "_version":【版本】: 1,
    "result":【结果】: "created",#这里的 create 表示创建成功
    "_shards": 【分片】:{
        "total":【分片的总数】: 2,
        "successful":【分片成功】: 1,
        "failed":【分片 - 失败】: 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

​ 由于PUT是幂等性的,如果用它就会报错,同样的请求结果是一致的。我们本次添加的数据中的id每次添加时的id肯定是不一样的,那么就是多次请求但结果不同,说明此操作不是幂等性的,所以不能使用PUT,使用POST无论是否为幂等性都可用。

自定义唯一标识

如果想要自定义唯一性标识,需要在创建时指定:http://127.0.0.1:9200/shopping/_doc/111

image-20240423175704587

​ 这里我增加数据的时候明确了主键,那么我就可以使用PUT请求方式。

查询文档

​ 查看文档时,需要指明文档的唯一性标识,类似于 MySQL 中数据的主键查询 在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping/_doc/111

image-20240423200854850

修改文档

​ 和新增文档一样,输入相同的 URL 地址请求,如果请求体变化,会将原有的数据内容覆盖 在 Postman 中,向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc/111

image-20240423203410695

result的状态变成了update

修改数据时也可以只修改某一个字段,也就是局部信息。在 Postman 中,向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_update/111

image-20240423204431929image-20240423204544539

删除数据

​ 删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。 在 Postman 中,向 ES 服务器发 DELETE 请求 :http://127.0.0.1:9200/shopping/_doc/111

image-20240423204717179

再查询一下就找不到了。

image-20240423204758727

条件查询

​ 查询类别是小米的,127.0.0.1:9200/shopping/_search?q=category:小米

image-20240424100337052

​ 中文在请求路劲中很容易乱码,所以尽量使用请求体来条件查询。http://127.0.0.1:9200/shopping/_search

{
    "query":{
        "match":{
            "title":"小米"
        }
    }
}

image-20240424101039256

全查询

​ 其实跟你什么都不加直接查询是一样的http://127.0.0.1:9200/shopping/_search

{
    "query":{
        "match_all":{     
        }
    }
}

image-20240424101355326

​ 直接全查询的数据条目太多,我们进行一个分页。

{
    "query":{
        "match_all":{
           
        }
    },
    "from":0,
    "size":2
}

image-20240424101854810

​ 如果想从第2个开始查,那就把起始位置改一下,遵循公式:(页码-1)*每页数据条数

​ 从第2个开始就是 (2-1)*2就是2,那么起始位置就填2.

{
    "query":{
        "match_all":{
           
        }
    },
    "from":2,
    "size":2
}

image-20240424102230079

​ 条件查询中我想查询指定字段的信息,我不需要全部的内容,可用在请求体中指定想要保留的字段名称。

{
    "query":{
        "match_all":{
           
        }
    },
    "from":2,
    "size":2,
    "_source":["title"]
}

image-20240424102652135

​ 对查询的结果进行排序,sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。desc 降序,asc 升序。

{
    "query":{
        "match_all":{
           
        }
    },
    "_source":["title"],
    "sort" : {
        "price" :{
            "order":"desc"
        }
    }
}

image-20240424104300476

条件查询复杂版

​ 多个条件查询方式,must相当于AND,而should相当于or

{
    "query":{
       "bool":{
        "must":[
            {
             "match":{
                "category":"华为"
                }
            },
            {
                "match":{
                "category":"OPPO"
                }
            },
             {
             "match":{
                "category":"VIVO"
                }
            }
        ]
       }
    }
}

image-20240424111153274

​ 再来一个价格区间,大于1200的,这里使用range 查询找出那些落在指定区间内的数字或者时间。range 查询允许以下字符:

操作符说明
gt大于>
gte大于等于>=
lt小于<
lte小于等于<=
{
    "query":{
       "bool":{
        "should":[
            {
             "match":{
                "category":"华为"
                }
            },
            {
                "match":{
                "category":"OPPO"
                }
            },
             {
             "match":{
                "category":"VIVO"
                }
            }
        ],
        "filter":{
            "range":{
                "price":{
                    "gt": 1200
                }
            }
        }
       }
    }
}

image-20240424111802613

match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系

我们查询的时候类别只打一个字他就能查出来

image-20240424112635183

​ 因为ES会把词条进行分词,使用倒叙索引进行匹配,只要有关键字就能查到对应的数据。

image-20240424112856048

​ 那么我们在百度中搜索关键词或字之后会得到高亮的显示到匹配的词怎么做到?

{
    "query":{
        "match":{
            "category":"OPPO华"
        }
    },
    "highlight":{
        "fields":{
            "category":{}
        }
    }
}

image-20240424113309479

聚合查询

​ 我们将根据价格分组并统计个数

{
    "aggs":{ //聚合操作
        "price_group":{  //名称随便起
            "terms":{  //分组
                "field":"price"  //分组字段
            }
        }
    }
}

image-20240424114155554

​ 来求个平均值

{
    "aggs":{ //聚合操作
        "price_avg":{  //名称随便起
            "avg":{  //平均值
                "field":"price"  //分组字段
            }
        }
    },
    "size":0  //不显示原数据了
}

image-20240424121759608

2.2.4、映射关系

​ 接下来就需要建索引库(index)中的映射了,类似于数据库(database)中的表结构(table)。 创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。

再创建一个索引,user,然后创建映射

image-20240424134449125

映射数据说明:

  • 字段名:任意填写,下面指定许多属性,例如:title、subtitle、images、price;

  • type:类型,Elasticsearch 中支持的数据类型非常丰富,说几个关键的:

    • String 类型,又分两种:
      • text:可分词;
      • keyword:不可分词,数据会作为完整字段进行匹配;
    • Numerical:数值类型,分两类:
      • 基本数据类型:long、integer、short、byte、double、float、half_float;
      • 浮点数的高精度类型:scaled_float;
    • Date:日期类型;
    • Array:数组类型;
    • Object:对象;
  • index:是否索引,默认为 true,也就是说你不进行任何配置,所有字段都会被索引。

    • true:字段会被索引,则可以用来进行搜索;
    • false:字段不会被索引,不能用来搜索;
  • store:是否将数据进行独立存储,默认为 false

    原始的文本会存储在source 里面,默认情况下其他提取出来的字段都不是独立存储 的,是从source 里面提取出来的。当然你也可以独立的存储某个字段,只要设置 “store”: true 即可,获取独立存储的字段要比从_source 中解析快得多,但是也会占用 更多的空间,所以要根据实际业务需求来设置。

  • analyzer:分词器,这里的 ik_max_word 即使用 ik 分词器;

在 Postman 中,向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/user/_mapping

{
    "properties":{
        "name":{
            "type":"text",
            "index":true
        },
        "sex":{
            "type":"keyword",
            "index":true
        },
        "tel":{
            "type":"keyword",
            "index":false
        }
    }
}

image-20240424135718142

​ 把put改成get就会把写的查询出来

image-20240424135845874

​ 在索引user下创建一个文档

{
    "name":"张三",
    "sex":"那男的",
    "tel":"1111"
}

image-20240424143312855

​ 现在查询一下,姓名我们是开了索引的,只写一个张字应该是能够查到的

image-20240424143654745

​ 那单独查性别,只查一个男是查不到的,当时就设置它的类型就是关键字不能进行分词,想查出来你得打全名。

image-20240424143746118

​ 现在打全,那男的就能查询出来了。

image-20240424143921302

​ 那么电话号码我们就没有给它索引查询,你查他是不能查出来的

image-20240424144515913

3、JavaAPI操作

3.1、索引基本操作

​ Elasticsearch软件就是使用Java 开发的,所以可以直接使用Java API的方式对Elasticsearch服务进行访问。

新建一个maven项目,esTest1,导入依赖

<dependencies>
 <dependency>
 <groupId>org.elasticsearch</groupId>
 <artifactId>elasticsearch</artifactId>
 <version>7.8.0</version>
 </dependency>
 <!-- elasticsearch 的客户端 -->
 <dependency>
 <groupId>org.elasticsearch.client</groupId>
 <artifactId>elasticsearch-rest-high-level-client</artifactId>
 <version>7.8.0</version>
 </dependency>
 <!-- elasticsearch 依赖 2.x 的 log4j -->
 <dependency>
 <groupId>org.apache.logging.log4j</groupId>
 <artifactId>log4j-api</artifactId>
 <version>2.8.2</version>
 </dependency>
 <dependency>
 <groupId>org.apache.logging.log4j</groupId>
 <artifactId>log4j-core</artifactId>
 <version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
 <!-- junit 单元测试 -->
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.12</version>
 </dependency>
</dependencies>

新建一个ESTestClint类

public class ESTestClint {
    public static void main(String[] args) throws IOException {
        // 创建ES客户端
        RestHighLevelClient esClint = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );

        // 关闭客户端释放连接
        esClint.close();
    }
}

启动一下看看能不能连接

image-20240424152301109

正常退出没有问题,这里的连接操作显然是一个重复的可以提取的公共部分,我们抽取出来单独做一个工具类作为连接使用。

新建工具类ESClintUtil

//ES连接客户端工具类
public class ESClintUtil {
    public RestHighLevelClient getClint() throws IOException {
        // 创建ES客户端
        RestHighLevelClient esClint = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
        return esClint;
    }
}

3.1.1、创建索引

新建创建索引类ESCreateIndex

//创建索引
public class ESCreateIndex {
    public static void main(String[] args) throws IOException {
        //从公共类中获取连接
        ESClintUtil esTestClint = new ESClintUtil();
        RestHighLevelClient clint = esTestClint.getClint();
        //创建索引
        //先创建索引请求,请求创建一个名称为student的索引
        CreateIndexRequest student = new CreateIndexRequest("student");
        //有了请求想必也有响应拿到响应
        CreateIndexResponse createIndexResponse = clint.indices().create(student, RequestOptions.DEFAULT);
        //有了响应就可以获得响应状态
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println("创建索引状态:"+acknowledged);
        clint.close();
    }
}

image-20240424154832450

回到Postman看看有没有这个索引

http://127.0.0.1:9200/_cat/indices?v

image-20240424200945344

索引已经创建

3.1.2、查询索引

public class IndexSearch {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //索引查询
        GetIndexRequest request = new GetIndexRequest("student");
        GetIndexResponse getIndexResponse = clint.indices().get(request, RequestOptions.DEFAULT);
        System.out.println("别名"+getIndexResponse.getAliases());
        System.out.println("映射"+getIndexResponse.getMappings());
        System.out.println("设置"+getIndexResponse.getSettings());
        clint.close();
    }
}

image-20240427170521603

3.1.3、删除索引

public class DelIndex {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //删除索引
        DeleteIndexRequest request = new DeleteIndexRequest("student");
        AcknowledgedResponse delete = clint.indices().delete(request, RequestOptions.DEFAULT);
        boolean acknowledged = delete.isAcknowledged();
        System.out.println("删除:"+acknowledged);
        clint.close();
    }
}

image-20240427171314148

3.2、文档基本操作

先预设数据,建一个实体类student,get和set方法写上

public class Student {
    private String id;
    private String name;
    private String sex;
    private Integer age;
}

3.2.1、创建文档

//创建文档
public class DocCreate {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //创建文档
        IndexRequest request = new IndexRequest();
        Student student = new Student();
        student.setId("1001");
        student.setName("张三");
        student.setSex("男");
        student.setAge(21);
        //设置索引唯一对象
        request.index("student").id(student.getId());
        //将实体转化为json格式
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(student);
        //添加文档数据,指明格式为JSON
        request.source(json, XContentType.JSON);
        //客户端发请求,获取响应对象
        IndexResponse response = clint.index(request, RequestOptions.DEFAULT);
        System.out.println("id:"+response.getId());
        System.out.println("index:"+response.getIndex());
        System.out.println("状态:"+response.getResult());
        clint.close();
    }
}

image-20240427174338216

在postman中查看一下:
image-20240427175613395

3.2.2、修改文档

public class UpdateDoc {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //修改文档
        UpdateRequest request = new UpdateRequest();
        //设置修改哪一个
        request.index("student").id("1001");
        request.doc(XContentType.JSON,"sex","女");
        //客户端发请求,获取响应对象
        UpdateResponse response = clint.update(request, RequestOptions.DEFAULT);
        System.out.println("id:"+response.getId());
        System.out.println("index:"+response.getIndex());
        System.out.println("状态:"+response.getResult());
        clint.close();
    }
}

image-20240427181912311

到postman中查询

image-20240427191130425

3.2.3、基本查询文档

public class GetDoc {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //查询文档
        GetRequest request = new GetRequest();
        //设置查询哪一个
        request.index("student").id("1001");
        //客户端发请求,获取响应对象
        GetResponse response = clint.get(request, RequestOptions.DEFAULT);
        System.out.println("id:"+response.getId());
        System.out.println("type:"+response.getType());
        System.out.println("index:"+response.getIndex());
        System.out.println("内容:"+response.getSourceAsString());
        clint.close();
    }
}

image-20240428105724959

3.2.4、删除文档

public class DelDoc {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //删除文档
        DeleteRequest request = new DeleteRequest();
        //删除指定id的文档
        request.index("student").id("1001");
        //客户端发请求,获取响应对象
         DeleteResponse response = clint.delete(request, RequestOptions.DEFAULT);
        System.out.println("删除操作"+response.getResult());
        clint.close();
    }
}

image-20240428130615419

再查询一下看还有没有,没有就删除成功。

image-20240428130641305

3.3、文档批量操作

3.3.1、批量增加文档

//批量插入文档数据
public class DocInsertBatch {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.add(new IndexRequest().index("student").id("1001").source(XContentType.JSON,"name","张三","sex","男","age","20"));
        bulkRequest.add(new IndexRequest().index("student").id("1002").source(XContentType.JSON,"name","李四","sex","女","age","21"));
        bulkRequest.add(new IndexRequest().index("student").id("1003").source(XContentType.JSON,"name","王五","sex","男","age","22"));
        bulkRequest.add(new IndexRequest().index("student").id("1004").source(XContentType.JSON,"name","老刘","sex","女","age","23"));
        bulkRequest.add(new IndexRequest().index("student").id("1005").source(XContentType.JSON,"name","老八","sex","男","age","24"));
        BulkResponse bulk = clint.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println("took:"+bulk.getTook());
        System.out.println("items:"+ Arrays.toString(bulk.getItems()));
        clint.close();
    }
}

image-20240428153150345

到postman中查询出来

image-20240428153212253

3.3.2、批量查询所有文档

//批量查询
public class DocGetBatch {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("student");
        //构建查询的请求
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        SearchSourceBuilder query = searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequest.source(query);
        SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        System.out.println("total:"+hits.getTotalHits());
        for (SearchHit hit : hits) {
            //输出查询结果
            System.out.println("查询到的信息:"+hit.getSourceAsString());
        }
        clint.close();
    }
}

image-20240428155826804

3.4、高级查询

3.4.1、关键词查询

term 查询,查询条件为关键字

 public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //关键词查询
        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("student");
        //构建查询的请求
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        SearchSourceBuilder query = searchSourceBuilder.query(QueryBuilders.termQuery("sex","男"));
        searchRequest.source(query);
        SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        System.out.println("total:"+hits.getTotalHits());
        //输出查询结果
        for (SearchHit hit : hits) {
            System.out.println("查询到的信息:"+hit.getSourceAsString());
        }
        clint.close();
    }

image-20240430085348635

3.4.2、分页查询

public static void main(String[] args) throws IOException {
    RestHighLevelClient clint = new ESClintUtil().getClint();
    //封分页查询
    //创建搜索请求对象
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices("student");
    //构建查询的请求
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    SearchSourceBuilder query = searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    query.from(0);//从头开始查询
    query.size(2);//查询两页
    searchRequest.source(query);
    SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    System.out.println("total:"+hits.getTotalHits());
    //输出查询结果
    for (SearchHit hit : hits) {
        System.out.println("查询到的信息:"+hit.getSourceAsString());
    }
    clint.close();
}

image-20240430090946372

分页查询从指定页数开始查,小公式:(当前页数-1)*查询的页数

public static void main(String[] args) throws IOException {
    RestHighLevelClient clint = new ESClintUtil().getClint();
    //封分页查询
    //创建搜索请求对象
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices("student");
    //构建查询的请求
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    SearchSourceBuilder query = searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    query.from(1);//从第几条数据开始查就填第几条,从第1条开始就是第2和第3两条
    query.size(2);//查询两页
    searchRequest.source(query);
    SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    System.out.println("total:"+hits.getTotalHits());
    //输出查询结果
    for (SearchHit hit : hits) {
        System.out.println("查询到的信息:"+hit.getSourceAsString());
    }
    clint.close();
}

image-20240430092315101

3.4.3、字段过滤

public static void main(String[] args) throws IOException {
    RestHighLevelClient clint = new ESClintUtil().getClint();
    //字段过滤查询
    //创建搜索请求对象
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices("student");
    //构建查询的请求
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    SearchSourceBuilder query = searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    //查询字段过滤
    String[] excludes ={};//这个是排除,排除关键字
    String[] includes ={"name", "age"};//这个就是保留的关键字
    query.fetchSource(includes,excludes);
    searchRequest.source(query);
    SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    System.out.println("total:"+hits.getTotalHits());
    //输出查询结果
    for (SearchHit hit : hits) {
        System.out.println("查询到的信息:"+hit.getSourceAsString());
    }
    clint.close();
}

image-20240430114820149

3.4.4、组合查询

Bool查询,must是必须包含,mustNot一定不包含,should是可能包含,这个时候的查询方式就不是全量查询了,而是bool查询。

public class DocBoolGet {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient clint = new ESClintUtil().getClint();
        //创建搜索请求对象
        //bool查询
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("student");
        //构建查询的请求体
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //必须包含的must
        //boolQueryBuilder.must(QueryBuilders.matchQuery("sex","男"));
        //一定不含的mustNot
        //boolQueryBuilder.mustNot(QueryBuilders.matchQuery("name","张三"));
        //可能包含should
        boolQueryBuilder.should(QueryBuilders.matchQuery("age","20"));
        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        //输出查询结果
        for (SearchHit hit : hits) {
            System.out.println("查询到的信息:"+hit.getSourceAsString());
        }
        clint.close();
    }
}

must必须性别是男

image-20240506092920683

mustNot一定不包含张三

image-20240506093210433

should可能包含年龄为20的

image-20240506093318979

3.4.5、范围查询

rangeQuery,大于等于用gte小于等于用lte

//创建搜索请求对象
//范围查询
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("student");
//构建查询的请求体
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
//大于等于gte
rangeQuery.gte("40");
//小于等于lte
rangeQuery.lte("70");
searchSourceBuilder.query(rangeQuery);
searchRequest.source(searchSourceBuilder);

image-20240506094424338

3.4.6、模糊查询

使用fuzzyQuery对名字模糊查询,fuzziness设置模糊度,这将会匹配到与要查的字段相差指定编辑距离的字符串,模糊度决定了查询时允许的编辑距离。

​ Fuzziness.ONE是一个预定义的模糊度值,它表示查询时允许的最大编辑距离为1。换句话说,它将匹配与"zhangsan"只有一个字符差异的字符串,如"zhangdan"、"zhangasn"或者“zhangsan1”等。

//创建搜索请求对象
//范围、模糊、高亮、聚合、分组统计
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("student");
//构建查询的请求体
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.fuzzyQuery("name","zhangsan").fuzziness(Fuzziness.ONE));
searchRequest.source(searchSourceBuilder);

image-20240506100018329

3.4.7、高亮查询

public static void main(String[] args) throws IOException {
    RestHighLevelClient clint = new ESClintUtil().getClint();
    //创建搜索请求对象
    //高亮查询
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.indices("student");
    //构建查询的请求体
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //构建查询方法高亮查询
    searchSourceBuilder.query(QueryBuilders.termsQuery("name","zhangsan"));
    //构建高亮字段
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags("<font color='red'>");//设置标签前缀
    highlightBuilder.postTags("</font>");//设置标签后缀
    highlightBuilder.field("name");//设置高亮字段
    //设置高亮构建对象
    searchSourceBuilder.highlighter(highlightBuilder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    System.out.println("took:" + response.getTook());
    System.out.println("timeout:" + response.isTimedOut());
    System.out.println("total:" + hits.getTotalHits());
    System.out.println("MaxScore:" + hits.getMaxScore());
    //输出查询结果
    for (SearchHit hit : hits) {
        System.out.println("查询到的信息:"+hit.getSourceAsString());
        //打印高亮结果
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        System.out.println("高亮:"+highlightFields);
    }
    clint.close();
}

image-20240506110104874

3.4.8、聚合查询aggregation

使用aggregation查询最大年龄

//创建搜索请求对象
//聚合、分组统计
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("student");
//构建查询的请求体
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//聚合查询最大年龄
searchSourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));

searchRequest.source(searchSourceBuilder);
SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
System.out.println(response);

image-20240506110806423

分组统计AggregationBuilders.terms

//创建搜索请求对象
//聚合、分组统计
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("student");
//构建查询的请求体
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//聚合查询最大年龄
//searchSourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));
//分组统计年龄
searchSourceBuilder.aggregation(AggregationBuilders.terms("age_groupby").field("age"));
searchRequest.source(searchSourceBuilder);
SearchResponse response = clint.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
System.out.println(response);

image-20240506111234419

  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
WuKong 全文搜索引擎。功能特性:高效索引和搜索(1M条微博500M数据28秒索引完,1.65毫秒搜索响应时间,19K搜索QPS)支持中文分词(使用sego分词包并发分词,速度27MB/秒)支持计算关键词在文本中的紧邻距离(token proximity)支持计算BM25相关度支持自定义评分字段和评分规则支持在线添加、删除索引支持持久存储可实现分布式索引和搜索采用对商业应用友好的Apache License v2发布示例代码:package main import (     "github.com/huichen/wukong/engine"     "github.com/huichen/wukong/types"     "log" ) var (     // searcher是协程安全的     searcher = engine.Engine{} ) func main() {     // 初始化     searcher.Init(types.EngineInitOptions{         SegmenterDictionaries: "github.com/huichen/wukong/data/dictionary.txt"})     defer searcher.Close()     // 将文档加入索引     searcher.IndexDocument(0, types.DocumentIndexData{Content: "此次百度收购将成中国互联网最大并购"})     searcher.IndexDocument(1, types.DocumentIndexData{Content: "百度宣布拟全资收购91无线业务"})     searcher.IndexDocument(2, types.DocumentIndexData{Content: "百度是中国最大的搜索引擎"})     // 等待索引刷新完毕     searcher.FlushIndex()     // 搜索输出格式见types.SearchResponse结构体     log.Print(searcher.Search(types.SearchRequest{Text:"百度中国"})) } 标签:WuKongSearch

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fiercezm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值