Elasticsearch总结

目录

1、正排索引和倒排索引

2、什么是Elasticsearch

3、es核心概念

        索引:

        文档:

        域:

4、安装es和可视化工具Kibana

5、原生操作es

        索引操作

                新增索引

                删除索引

        文档操作

                新增文档

                修改文档

                删除文档

                查询文档

         查询所有文档

        分词器

                默认分词器

                IK分词器

                IK拼音分词器

                自定义分词器

        复杂查询

                条件查询

                排序查询

               分页查询

                复合搜索

                高亮显示

                自动补全

6、SpringDataEs操作es

        项目搭建

        创建实体类

        Repository接口方法

        DSL查询文档

        按照规则命名查询

        分页查询

        结果排序

        template操作:

                操作索引

                        创建索引

                        删除文档

                增删改文档

                查询文档

                复杂查询

                分页排序

                结果排序

7、实战案例

        实现功能

        项目搭建

        编写Repository层

        编写Service层

        编写Controller层

        编写前端页面


部分图片来自百战程序员

1、正排索引和倒排索引

索引:索引是将数据中的一部分信息提取出来,重新组织成一定的数据结构,我们可以根据该结构进行快速搜索,这样的就够称之为索引。索引即目录,例如词典会将字的拼音提取出来做目录,通过目录可以快速找到字的位置,索引分为正排索引倒排索引 

正排索引:将文档id建立为索引,通过id快速查找数据,就像数据库中的主键就会建立正排索引

倒排索引:倒排索引就不是通过id建立索引了,而是通过提取数据中的关键字,然后将关键字建立为索引,通过匹配的关键字去查询数据

 

2、什么是Elasticsearch

  1. Elasticsearch 是一个免费且开放的分布式搜索和分析引擎,适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据
  2. Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。Elasticsearch 以其简单的 RESTFUL 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;
  3. Elastic Stack 是一套适用于数据采集、扩充、存储、分析和可视化的免费开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据

3、es核心概念

        索引:

索引对应的就是数据库中的表

        文档:

文档对应的就是表中的一条数据

        域:

域名对应的就是字段

4、安装es和可视化工具Kibana

es下载路径:

https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.8.2-linux-x86_64.tar.gz

kibana下载路径:

https://artifacts.elastic.co/downloads/kibana/kibana-8.8.2-linux-x86_64.tar.gz

大家自行下载上传到虚拟机中或者直接使用wget拉取(这里要提醒的是es和kibana的版本必须一致否则会有错误) 

Elasticsearch启动:

1、关闭防火墙

systemctl stop firewalld.service

2、配置最大可创建文件数大小

#打开系统文件:

vim /etc/sysctl.conf

#添加以下配置:

vm.max_map_count=655360

#配置生效:

sysctl -p

3、由于es不能以root运行,我们需要创建一个非root用户

#创建es用户

useradd es

4、解压es

#解压es

tar -zxvf 文件目录名 

#修改文件属主

chown -R es:es 文件目录路径

5、启动es

#切换用户

su es

#启动elasticsearch,进入到elasticsearch的bin目录下

./elasticsearch

 Kibana启动:

1、解压文件

tar -zxvf 文件目录名

2、配置kibana.yml

#进入kibana的config目录的kibana.yml文件,添加如下配置

#es的默认端口号是9200

server.host=虚拟机ip

elasticsearch.hosts=["http://运行es的虚拟机ip:端口号"]

3、运行kibana

#因为kibana不能以root用户运行,所以需要使用非root用户,我之前创建了一个es用户,

#所以我们直接使用es用户

#将文件所属改为es

chown -R es:es 文件目录路径

#切换用户

su es

#切换到kibana的bin目录,运行kibana

./kibana

4、运行之后访问http://kibana虚拟机ip:端口号

#kibana的默认端口是5601

5、访问 http://虚拟机ip:5601

访问成功过后我们就可以对es进行操作了

这个页面是通过restful风格api对es进行操作的 

 这个页面里的Index Management可以查看索引

5、原生操作es

        索引操作

                新增索引

1、建立没有结构的索引

PUT /索引名

示例:

PUT /student

2、建立有结构的索引

PUT /索引名

{

        "mappings":{

                "properties":{

                       "域名":{

                                "type":"字段类型",

                                "index":"是否创建索引",

                                "store":"是否存储",

                                "analyzer":"分词器"

                        },

                        "域名":{

                                ......

                        }

                }

        }

}

示例:

PUT /student1
{
  "mappings": {
    "properties": {
      "id":{
        "type": "integer"
      },
      "name":{
        "type":"text"
      }
    }
  }
}

                删除索引

DELETE /索引名

示例:

DELETE /student

        文档操作

文档存储数据类似于set集合其存储的id不可重复

如果id重复则直接覆盖id里面所对应的数据,如果不写id则自动生成

                新增文档

POST /索引名/_doc/[id]

示例:

POST /student1/_doc/1
{
  "id":"1",
  "name":"zhangsan"
}

                修改文档

POST /索引名/_update/id值

示例:

POST /student1/_update/1
{
  "doc":{
    "name":"lisi"
  }
}
 

                删除文档

DELETE /索引名/_doc/id值

                查询文档

GET /索引名/_doc/id值

示例:

GET /student1/_doc/1

         查询所有文档

GET /索引名/_search

{

        "query":{

                "match_all":{}

        }

}

        分词器

                默认分词器

ES文档的数据拆分成一个个有完整含义的关键词,并将关键词与文档对应,这样就可以通过关键词查询文档。要想正确的分词,需要选择合适的分词器。

standard analyzer:Elasticsearch默认分词器,根据空格和标点符号对英文进行分词,会进行单词的大小写转换。

默认分词器是英文分词器,对中文的分词是一字一词。

 GET /_analyze

{

    "text":测试语句,

    "analyzer":分词器

}

                IK分词器

IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。提供了两种分词算法:

  • ik_smart:最少切分
  • ik_max_word:最细粒度划分

安装IK分词器

关闭es服务

使用rz命令将ik分词器上传至虚拟机
注:ik分词器的版本要和es版本保持一致。

解压ik分词器到elasticsearch的plugins目录下
unzip elasticsearch-analysis-ik-7.17.0.zip -d /usr/local/elasticsearch1/plugins/analysis-ik

#切换用户es

su es

#进入ES安装文件夹:

cd /usr/local/elasticsearch1/bin/

#启动ES服务:

./elasticsearch -d

GET /_analyze

{

    "text":"测试语句",

    "analyzer":"ik_smart/ik_max_word"

}

IK分词器词典

IK分词器根据词典进行分词,词典文件在IK分词器的config目录中。

  • main.dic:IK中内置的词典。记录了IK统计的所有中文单词。
  • IKAnalyzer.cfg.xml:用于配置自定义词库。

<properties>

    <comment>IK Analyzer 扩展配置</comment>

    <!--用户可以在这里配置自己的扩展字典 -->

    <entry key="ext_dict">ext_dict.dic</entry>

     <!--用户可以在这里配置自己的扩展停止词字典-->

    <entry key="ext_stopwords">ext_stopwords.dic</entry>

</properties>

1、编辑ext_dict.dic文件(只需要在里面添加想要的关键词就好了)

2、编辑ext_stopwords.dic文件(只需要在里面添加不想要的关键词就好了)

3、测试分词效果

GET /_analyze

{

  "text":"我爱英雄联盟",

  "analyzer":"ik_max_word"

}

没有自定义词典的分词效果:

添加了自定义词典的分词效果:

                IK拼音分词器

拼音分词器可以将中文分成对应的全拼,全拼首字母等。

 

安装拼音分词器

关闭es服务使用rz命令将拼音分词器上传至虚拟机

注:拼音分词器的版本要和es版本保持一致。

解压ik分词器到elasticsearch的plugins目录下

unzip elasticsearch-analysis-pinyin-7.17.0.zip -d /usr/local/elasticsearch1/plugins/analysis-pinyin

 

启动ES服务

su es

#进入ES安装文件夹:

cd /usr/local/elasticsearch1/bin/

#启动ES服务:

./elasticsearch

GET /_analyze

{

    "text":测试语句,

    "analyzer":pinyin

}

                自定义分词器

真实开发中我们往往需要对一段内容既进行文字分词,又进行拼音分词,此时我们需要自定义ik+pinyin分词器。

 

注意:两个分词器不是叠加的,而是各干各的,也就是说先对文档进行ik分词,然后再对文档进行pinyin分词。而不是对文档先ik分词,然后再对已经被ik分词的文档进行pinyin分词,也就是说倒排索引中会有两种不同的关键词一种是通过ik分词器分词的,一种是通过pinyin分词器分词的

 PUT /索引名
{
  "settings" : {
    "analysis" : {
      "analyzer" : {
        "ik_pinyin" : { //自定义分词器名
          "tokenizer":"ik_max_word", // 基本分词器
          "filter":"pinyin_filter" // 配置分词器过滤
         }
       },
      "filter" : { // 分词器过滤时配置另一个分词器,相当于同时使用两个分词器
        "pinyin_filter" : { 
          "type" : "pinyin", // 另一个分词器
          // 拼音分词器的配置
          "keep_separate_first_letter" : false, // 是否分词每个字的首字母
          "keep_full_pinyin" : true, // 是否分词全拼
          "keep_original" : true, // 是否保留原始输入
          "remove_duplicated_term" : true // 是否删除重复项
         }
       }
     }
   },
  "mappings":{
    "properties":{
      "域名1":{
        "type":域的类型,
        "store":是否单独存储,
        "index":是否创建索引,
             "analyzer":分词器
       },
      "域名2":{ 
        ...
       }
     }
   }
}

示例:

PUT /product2

{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_pinyin":{
          "tokenizer":"ik_max_word",
          "filter":"pinyin_filter"
        }
      },
      "filter": {
        "pinyin_filter":{
          "type":"pinyin",
          "keep_separate_first_letter" : false, 
          "keep_full_pinyin" : true, 
          "keep_original" : true, 
          "remove_duplicated_term" : true 
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":{
        "type": "integer",
        "store": true
      },
      "name":{
        "type": "text",
        "store": true,
        "index": true,
        "analyzer": "ik_pinyin"
      }
    }
  }
}

测试自定义分词器

GET /索引名/_analyze

{

 "text": "你好百战程序员",

 "analyzer": "ik_pinyin"

}

        复杂查询

通过前面的操作我们学会了基本的es操作,但是在查询的时候我们的查询操作不可能会这么简单,我们肯定需要对于查询添加一些条件,所以我们这里就来学一下复杂查询

                条件查询

 1、match_all查询所有文档

GET /索引名/_search

{

        "query":{

                "match_all":{}

        }

}

2、match全文检索(想要全文检索需要再创建索引的时候域的index设置为true)

这里需要注意的是如果使用match方式查询的话会先对关键字进行分词,然后最用分词后的数据进行查询

GET /索引名/_search

{

        "query":{

                "match":{

                        "域名":"关键字"

                }

        }

}

示例:

3、range范围查询

GET /索引名/_search

{

        "query":{

                "range":{

                        "域名":{

                                //大于等于1,小于等于2

                               "gte":1,

                                "lte":2

                        }

                }

        }

}

GET /索引名/_search

{

        "query":{

                "range":{

                        "域名":{

                                //大于1,小于3

                               "gt":1,

                                "lt":3

                        }

                }

        }

}

示例:

4、fuzziness纠错(最多只能纠错2位)

GET /索引名/_search

{

        "query":{

                "match":{

                        "域名":{

                                "query":"关键词",

                                "fuzziness":纠错个数

                        }

                }

        }

}

示例: 

5、match_phrase短语检索,关键字不做任何分词,在搜索字段对应的倒排索引中精准匹配

GET /索引名/_search

{

        "query":{

                "match_phrase":{

                        "域名":"关键词"

                }

        }

}

示例:

6、terms词组检索,关键词不做任何分词,在搜索字段对应的倒排索引中精准匹配

GET /索引名/_search
{

        "query":{

                "terms":{

                        "域名":[

                               "关键词",

                                "关键词",

                                ...

                        ]

                }

        }

}

                排序查询

GET /索引名/_search

{

        "query":{

                "match":{

                        "域名":"关键词"

                }

        },

        "sort":[

                "域名":{

                        "order":"排序方式"

                }

        ]

示例:

               分页查询

 GET /索引名/_search

{

        "query":{

                "match":{

                        "域名":"关键词"

                }

        },

        "from":起始下标,

        "size":每页个数

}

示例:

                复合搜索

GET /索引名/_search

{

        "query":{

                "bool":{

                         // 必须满足的条件

                        "must":{

                                {

                                        "搜索方式":{

                                               "域名":"关键字"

                                        }

                                },

                                {...}

                        },

                        //任意一个条件满足即可

                        "should":{    

                                {

                                        "搜索方式":{

                                               "域名":"关键字"

                                        }

                                },

                                {...}

                        },

                        //必须不满足的条件

                        "must_not":{

                                {

                                        "搜索方式":{

                                               "域名":"关键字"

                                        }

                                },

                                {...}

                        }

                }

        }

}

示例:

                高亮显示

GET /索引名/_search

{

        "query":{

                "match":{

                        "域名":"关键词"

                }

        },

        "highlight":{

               "fields":{

                        "高亮字段名":{

                              // 返回高亮数据的最大长度

                             "fragment_size":100,

                              // 返回结果最多可以包含几段不连续的文字

                             "number_of_fragments":5

                        }

                },

               "pre_tags":["前缀"],

               "post_tags":["后缀"]

        }

}

 示例:

                自动补全

自动补全的字段的类型必须是completion,所以我们需要新建一个索引,将对应的域的类型设置为completion

PUT /product
{
  "mappings": {
    "properties": {
      "id":{
        "type": "integer",
        "index": true,
        "store": true
      },
      "productName":{
        "type": "completion"
      },
      "productDesc":{
        "type": "text",
        "index": true,
        "store": true
      }
    }
  }
}

然后我们需要新增几条数据

POST /product/_doc
{
  "id":1,
  "productName":"elasticsearch1",
  "productDesc":"elasticsearch1 is a good search engine"
}


POST /product/_doc
{
  "id":2,
  "productName":"elasticsearch2",
  "productDesc":"elasticsearch2 is a good search engine"
}


POST /product/_doc
{
  "id":3,
  "productName":"elasticsearch3",
  "productDesc":"elasticsearch3 is a good search engine"
}

自动补全

GET /索引名/_search

{

        "suggest":{

                "自定义名字":{

                        "prefix":"被补全的关键字",

                        "completion":{

                                "fields":"补全字段",

                                "skip_duplicates": true, // 忽略重复结果

                                "size": 10 //最多查询到的结果数

                        }

                }

        }

}

6、SpringDataEs操作es

        项目搭建

创建SpringBoot项目,引入SpringDataEs依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置yml文件

spring:elasticsearch:uris: http://运行es的虚拟机IP:9200

        创建实体类

一个实体类的所有对象都会存入ES的一个索引中,所以我们在创建实体类时关联ES索引。

//索引名叫product,启动SpringBoot的时候是否自动创建索引
@Document(indexName = "product",createIndex = true)@Datapublic class Product {@Id @Field(type = FieldType.Integer,store = true,index = true)private Integer id;@Field(type = FieldType.Text,store = true,index = true,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")private String productName;@Field(type = FieldType.Text,store = true,index = true,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")private String productDesc;}

@Document:标记在类上,标记实体类为文档对象,一般有如下属性:

indexName:对应索引的名称

createIndex:是否自动创建索引

 

@Id:标记在成员变量上,标记一个字段为主键,该字段的值会同步到ES该文档的id值。

 

@Field:标记在成员变量上x`标记为文档中的域,一般有如下属性:

type:域的类型

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

store:是否单独存储,默认是 false

analyzer:分词器

searchAnalyzer:搜索时的分词器

        Repository接口方法

创建Repository接口继承ElasticsearchRepository,接口提供了增删改查方法

(该接口有两个泛型,第一个泛型是实体类类型,第二个泛型是实体类的主键的类型)

import com.itbaizhan.esblog.pojo.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;public interface ProductRepository extends ElasticsearchRepository<Product,Integer> {
}

测试接口:

import com.itbaizhan.esblog.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.Optional;@SpringBootTest
public class ProductRepositoryTest {@Autowiredprivate ProductRepository repository;@Testpublic void t1(){//新增Product product = new Product(1,"HUAWEI MATE 30","照亮你的美");repository.save(product);}@Testpublic void t2(){//修改Product product = new Product(1,"OPPO RENO 6 PRO","充电两分钟通话两小时");repository.save(product);//因为id相同所以是修改,就像map集合一样}@Testpublic void t3(){//根据id获取文档Optional<Product> optional = repository.findById(1);Product product = optional.get();System.out.println(product);}@Testpublic void t4(){//查询所有Iterable<Product> products = repository.findAll();products.forEach(System.out::println);}@Testpublic void t5(){//删除Product product = new Product(1);repository.delete(product);}}

        DSL查询文档

接下来我们讲解SpringDataES支持的查询方式,首先准备一些文档数据:

// 添加一些数据

repository.save(new Product(2, "三体1", "三体1是优秀的科幻小说"));

repository.save(new Product(3, "三体2", "三体2是优秀的科幻小说"));

repository.save(new Product(4, "三体3", "三体3是优秀的科幻小说"));

repository.save(new Product(5, "elasticsearch", "elasticsearch是基于lucene开发的优秀的搜索引擎"));

使用Repository继承的方法查询文档

该方式我们之前已经讲解过了

 

使用DSL语句查询文档

ES通过json类型的请求体查询文档,方法如下:

GET /索引/_search

{

  "query":{

    搜索方式:搜索参数

   }

}

 

query后的json对象称为DSL语句,我们可以在接口方法上使用@Query注解自定义DSL语句查询。

?0代表占位符,一个?0匹配一个方法形式参数

@Query("{" +

    "   \"match\": {" +

    "    \"productDesc\": \"?0\"" +

    "   }" +

    "  }")

List<Product> findByProductDescMatch(String keyword);

示例:

@Query("{" +

    " \"match\": {" +

    "  \"productDesc\": {" +

    "   \"query\": \"?0\"," +

    "   \"fuzziness\": 1" +

    "  }" +

    " }" +

    "}")

List<Product> findByProductDescFuzzy(String keyword);

        按照规则命名查询

按照规则命名方法进行查询

关键字

命名规则

解释

示例

and

FindByField1AndField2

根据Field1和Field2 获得数据

FindByTitleAndContent

or

FindByField1OrField2

根据Field1或Field2 获得数据

FindByTitleOrContent

is

FindByField

根据Field获得数据

FindByTitle

not

FindByFieldNot

根据Field获得补集数据

FindByTitleNot

between

FindByFieldBetween

获得指定范围的数据

FindByPriceBetween

List<Product> findByProductName(String productName);

List<Product> findByProductNameOrProductDesc(String productName,String productDesc);

List<Product> findByIdBetween(Integer startId,Integer endId);

  • 只需在Repository接口中按照SpringDataES的规则命名方法,该方法就能完成相应的查询。
  • 规则:查询方法以findBy开头,涉及查询条件时,条件的属性用条件关键字连接。

        分页查询

使用继承或自定义的方法时,在方法中添加Pageable类型的参数

返回值为Page类型即可进行分页查询。

// 测试继承的方法:@Testpublic void testFindPage(){// 参数1:页数,//  参数2:每页条数Pageable pageable = PageRequest.of(1, 3);Page<Product> page = repository.findAll(pageable);System.out.println("总条数"+page.getTotalElements());System.out.println("总页数"+page.getTotalPages());System.out.println("数据"+page.getContent());}// 自定义方法Page<Product> findByProductDesc(String productDesc, Pageable pageable);// 测试自定义方法@Testpublic void testFindPage2(){Pageable pageable = PageRequest.of(1, 2);Page<Product> page = repository.findByProductDescMatch("我喜欢三体", pageable);System.out.println("总条数"+page.getTotalElements());System.out.println("总页数"+page.getTotalPages());System.out.println("数据"+page.getContent());}

        结果排序

使用继承或自定义的方法时,在方法中添加Sort类型的参数即可进行结果排序

// 结果排序@Testpublic void testFindSort(){//第一个参数:排序的类型    第二个参数:根据哪个字段进行排序Sort sort = Sort.by(Sort.Direction.DESC, "id");Iterable<Product> all = repository.findAll(sort);for (Product product : all) {System.out.println(product);}}// 测试分页加排序@Testpublic void testFindPage2(){Sort sort = Sort.by(Sort.Direction.DESC,"id");Pageable pageable = PageRequest.of(0, 2,sort);Page<Product> page = repository.findByProductDescMatch("我喜欢三体", pageable);System.out.println("总条数"+page.getTotalElements());System.out.println("总页数"+page.getTotalPages());System.out.println("数据"+page.getContent());}

        template操作:

通过继承ElasticsearchRepository类我们可以很方便的进行增删改查,但是使用这种方式查询文档,无法复杂查询,也就是说只能通过id查询或者查询所有,无法通过关键字匹配查询,这就使得查询收到了很大的局限性,而使用SpringDataEs提供的工具类ElasticsearchRestTemplate操作es则可以解决该问题

                操作索引

                        创建索引

ElasticsearchRestTemplate创建索引无法设置索引结构,所以并不推荐使用该方法创建索引

使用template操作索引,首先我们需要获取到该索引的操作对象,然后通过该操作对象进行索引操作

@SpringBootTest
public class TestTemplate {@Autowiredprivate ElasticsearchRestTemplate template;@Testpublic void addIndex(){IndexOperations ops = template.indexOps(Product.class);ops.create();}
}

                        删除文档

删除文档也是一样的只是调用的方法不同

@SpringBootTest
public class TestTemplate {@Autowiredprivate ElasticsearchRestTemplate template;@Testpublic void delete(){IndexOperations ops = template.indexOps(Product.class);ops.delete();}}

                增删改文档

template增删改文档和ElasticsearchRepository差不多,只是引用不同

@SpringBootTest
public class TestTemplate {@Autowiredprivate ElasticsearchRestTemplate template;@Testpublic void addIndex(){//新增索引IndexOperations ops = template.indexOps(Product.class);ops.create();}@Testpublic void delete(){//删除索引IndexOperations ops = template.indexOps(Product.class);ops.delete();}@Testpublic void save(){//新增文档Product product = new Product(1,"HUAWEI MATE 30","照亮你的美");template.save(product);}@Testpublic void update(){//修改文档Product product = new Product(1,"OPPO RENO 6 PRO","充电五分钟,通话两小时");template.save(product);}@Testpublic void testDelete(){//删除文档template.delete("1",Product.class);}}

                查询文档

使用template查询文档主要分为四步:

  1. 确定查询方式
  2. 构建查询条件
  3. 查询
  4. 处理查询结果
@Testpublic void searchDocument(){//查询文档//1、确定查询方式MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();//2、构建查询条件NativeSearchQuery request = new NativeSearchQueryBuilder().withQuery(builder).build();//3、查询//参数一:查询条件//参数二:索引对应的类对象SearchHits<Product> hits = template.search(request, Product.class);//4、处理查询结果for (SearchHit<Product> hit : hits) {System.out.println(hit.getContent());}}

                复杂查询

通过template复杂条件查询可以实现动态的查询,根据是否传递某个参数动态修改dsl语句,查询数据

@Testpublic void boolSearch(){String productName = null;String productDesc = null;//1、确定查询方式BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();if (productName == null && productDesc == null){//参数一:匹配的字段//参数二:关键字MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();boolQueryBuilder.must(queryBuilder);//必须匹配}else if (productDesc != null && productDesc.length() > 0){//参数一:匹配的字段//参数二:关键字MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("productDesc", productDesc);boolQueryBuilder.must(queryBuilder);//必须匹配}else if (productName != null && productName.length() > 0){//参数一:匹配的字段//参数二:关键字MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("productName", productName);boolQueryBuilder.must(queryBuilder);//必须匹配}//2、构建查询条件NativeSearchQuery request = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder).build();//3、查询SearchHits<Product> searchHits = template.search(request, Product.class);//4、处理查询结果for (SearchHit<Product> hit : searchHits) {System.out.println(hit.getContent());}}

                分页排序

template分页排序通过构建分页对象Pageable,设置第几页以及每页条数,然后将分页条件放入查询条件中,查询完成之后需要自己手动构造分页条件PageImpl,然后通过返回的Page对象就可以使用分页数据了

@Testpublic void limit(){//查询文档//1、确定查询方式MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
//        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("productName", "oppo");//2、构建查询条件//构建分页条件Pageable pageable = PageRequest.of(0, 1);NativeSearchQuery request = new NativeSearchQueryBuilder().withQuery(queryBuilder).withPageable(pageable).build();//3、查询SearchHits<Product> hits = template.search(request, Product.class);//4、处理查询结果//将结果封装为Page对象List<Product> list = new ArrayList<>();for (SearchHit<Product> hit : hits) {Product product = hit.getContent();list.add(product);}//参数一:具体数据//参数二:分页条件//参数三:总条数Page<Product> page = new PageImpl<>(list, pageable, hits.getTotalHits());System.out.println("每页条数:"+page.getTotalElements());System.out.println("总页数:"+page.getTotalPages());System.out.println("数据:"+page.getContent());}

                结果排序

template结果排序是通过构建结果排序对象,指定排序字段和排序方式,然后再构建查询条件的时候传入

@Testpublic void sort(){//查询文档//1、确定查询方式MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
//        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("productName", "oppo");//2、构建查询条件//构建结果排序对象FieldSortBuilder sortBuilder = SortBuilders.fieldSort("id").order(SortOrder.DESC);NativeSearchQuery request = new NativeSearchQueryBuilder().withQuery(builder).withSorts(sortBuilder).build();//3、查询SearchHits<Product> hits = template.search(request, Product.class);//4、处理查询结果for (SearchHit<Product> hit : hits) {System.out.println(hit.getContent());}}

7、实战案例

        实现功能

本次案例我们要实现的是高亮字段以及自动补全的功能

        项目搭建

创建索引

这一段DSL语句的意思是:创建一个分词器,既有pinyin分词器的作用又有ik分词器的作用。为索引添加结构,创建域以及指定各个域的类型,并且给tags域的类型设置为completion,因为后面我们要给该字段进行自动补全功能的实现

PUT /news

{

  "settings": {

    "number_of_shards": 5,

    "number_of_replicas": 1,

    "analysis": {

      "analyzer": {

        "ik_pinyin": {

          "tokenizer": "ik_smart",

          "filter": "pinyin_filter"

         },

        "tag_pinyin": {

          "tokenizer": "keyword",

          "filter": "pinyin_filter"

         }

       },

      "filter": {

        "pinyin_filter": {

          "type": "pinyin",

          "keep_joined_full_pinyin": true,

          "keep_original": true,

          "remove_duplicated_term": true

         }

       }

     }

   },

  "mappings": {

    "properties": {

      "id": {

        "type": "integer",

        "index": true

       },

      "title": {

        "type": "text",

        "index": true,

        "analyzer": "ik_pinyin",

        "search_analyzer": "ik_smart"

       },

      "content": {

        "type": "text",

        "index": true,

        "analyzer": "ik_pinyin",

        "search_analyzer": "ik_smart"

       },

      "url": {

        "type": "keyword",

        "index": true

       },

      "tags": {

        "type": "completion",

        "analyzer": "tag_pinyin",

        "search_analyzer": "tag_pinyin"

       }

     }

   }

}

将mysql的数据同步到es中

先在mysql中添加news表的数据:

百度网盘链接
提取码:7bqy

自行下载logstash文件,该文件可以将mysql中的数据同步到es中

1、解压logstash-7.17.0-windows-x86_64.zip
logstash要和elastisearch版本一致

 

2、在解压路径下的/config中创建mysql.conf文件,文件写入以下脚本内容:

input {

   jdbc {

     jdbc_driver_library => "E:\新课\Elasticsearch\软件\案例\mysql-connector-java-5.1.37-bin.jar"   //找到jdbc驱动包

     jdbc_driver_class => "com.mysql.jdbc.Driver"     //找到驱动类

     "jdbc:mysql:///news?useUnicode=true&characterEncoding=utf-8&useSSL=false"      //找到指定的数据库,这后面的参数是一定要加的,不加会报错

     jdbc_user => "root"    //数据库用户名

     jdbc_password => "root"      //数据库密码

     schedule => "* * * * *"       //多长时间同步一次(这里是每分钟同步一次,如果logstash一直运行则每分钟一直同步)

     jdbc_default_timezone => "Asia/Shanghai"      //时区

     statement => "SELECT * FROM news;"      //执行的sql

   }

}

filter { //查到数据库后的操作

    mutate {

        split => {"tags" => ","}  //针对tags字段进行操作,通过逗号分割字段内容转为一个数组

    }

}

output {

   elasticsearch {

        hosts => ["192.168.0.187:9200","192.168.0.187:9201","192.168.0.187:9202"]  //es集群

     index => "news"  //索引名字

     document_id => "%{id}"  //从数据库查询到的id列作为索引里的文档id

   }

}

3、在解压路径下打开cmd黑窗口,运行命令:

bin\logstash -f config\mysql.conf

4、测试自动补齐

GET /news/_search

{

  "suggest": {

    "my_suggest": {

      "prefix": "li",

      "completion": {

        "field": "tags",

        "skip_duplicates": true,

        "size": 10

       }

     }

   }

}

一张表对应的就是一个对象,一个索引就是一张表,那么一个索引就对应了一个对象,所以我们需要创建News对象,属性对应的就是索引中的各个域

@Document(indexName = "news",createIndex = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class News {@Id@Fieldprivate Integer id;@Fieldprivate String title;@Fieldprivate String content;@Fieldprivate String keyword;@CompletionField    //Completion属性比较特殊需要使用@CompletionField修饰@Transientprivate Completion tags;}

        编写Repository层

@Repository
public interface NewsRepository extends ElasticsearchRepository<News,Integer> {
}

        编写Service层

实现自动补全功能

1、创建补全请求

2、创建补全条件并且添加补全条件

3、查询

4、处理查询结果

@Service
public class NewsService {@Autowiredprivate ElasticsearchRestTemplate template;public List<String> suggestion(String keyword){//1、创建补全请求SuggestBuilder suggestBuilder = new SuggestBuilder();//2、创建补全条件SuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("tags") //补全字段.prefix(keyword) //补全前缀.skipDuplicates(true) //去除重复.size(10); //最大补全大小//添加补全条件suggestBuilder.addSuggestion("my_suggestion",suggestionBuilder);//3、查询SearchResponse response = template.suggest(suggestBuilder, IndexCoordinates.of("news"));//4、处理查询结果List<String> collect = response.getSuggest().getSuggestion("my_suggestion").getEntries().get(0).getOptions().stream().map(Suggest.Suggestion.Entry.Option::getText).map(Text::toString).collect(Collectors.toList());return collect;}
}

实现高亮字段功能

1、添加高亮查询方法

2、高亮查询

3、处理查询结果,将高亮字段设置到原始数据中

实现这个高亮字段功能需要先在repository接口中添加以findBy规则命名的方法,并且添加@HighLight注解

//该注解的作用就是设置需要高亮查询的字段

//该方法是按照SpringDataES规则命名,该方法可以通过对上传的titleContent参数进行match查询

//该方法的返回值是SearchHit的容器,具体看图

//SearchHit里的结构,如下图

@Repository
public interface NewsRepository extends ElasticsearchRepository<News,Integer> {@Highlight(fields = {@HighlightField(name = "title"),@HighlightField(name = "content")})public List<SearchHit<News>> findByTitleMatchesOrContentMatches(String title, String content);
}

实现功能

public List<News> highLight(String keyword){//构建新的news集合,该集合是有高亮字段的List<News> news = new ArrayList<>();//高亮查询List<SearchHit<News>> hits = repository.findByTitleMatchesOrContentMatches(keyword, keyword);//处理查询结果for (SearchHit<News> hit : hits) {News content = hit.getContent();//返回的是没有高亮字段的News//获取高亮字段Map<String, List<String>> highlightFields = hit.getHighlightFields();//如果有高亮字段匹配则将高亮字段设置进去if (highlightFields.get("title") != null){content.setTitle(highlightFields.get("title").get(0));}//如果有高亮字段匹配则将高亮字段设置进去if (highlightFields.get("content") != null){content.setContent(highlightFields.get("content").get(0));}news.add(content);}return news;}

//这里的get(0)只是为了方便测试,但是如果一条文档的域的值有多个高亮字段匹配的话,则会分开存入集合中

//这是因为es在进行高亮搜索的时候,如果一条文档啊的域的值有多个高亮字段匹配,会以数组的方式分开存放,所以在使用

//SpringDataEs操作es的时候就会以集合的方式存入

        编写Controller层

@RequestMapping所写的路径和方法参数名必须与我的匹配,因为后面提供的前端页面需要使用到

@RestController
public class NewsController {@Autowiredprivate NewsService newsService;@RequestMapping("/autoSuggest")public List<String> suggest(String term){return newsService.suggestion(term);}@RequestMapping("/highLightSearch")public List<News> highlight(String term){return newsService.highLight(term);}}

        编写前端页面

通过该链接获取前端资源,获取之后将该资源放到static目录中

百度网盘链接:https://pan.baidu.com/s/1SgcdqzdWC_530nWjJReLFA?pwd=tt5r 
提取码:tt5r

启动项目访问localhost:8080/news.html

测试自动补全:

 测试高亮字段:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/9199.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win2008下使用IIS+URL重写+Server Farms在同一台服务器实现混合 多域名网站与多个负载平衡集群网站 共存,配置及域名绑定笔记

因公司要在现有云服务器增加负载平衡集群网站&#xff0c;但要保证现有在用网站不动&#xff0c;操作系统为Win2008。 以前没有配置过IIS的负载平衡&#xff0c;只能百度一下&#xff0c;实现也简单&#xff1a;只要安装URL重写Server Farms就能实现负载平衡集群网站。于是根据…

MongoDB存储引擎

1、前言 存储引擎是数据库的组成部分&#xff0c;负责管理数据存储。 MongoDB支持的以下存储引擎&#xff1a; 存储引擎 描述 WiredTiger存储引擎 从MongoDB 3.2开始默认的存储引擎&#xff0c;新的版本MongoDB推荐使用WiredTiger存储引擎。 MMAPv1存储引擎 MMAPv1是Mon…

Go语言MinGW的安装

Go语言MinGW的安装 相比在 Linux 平台上安装 GCC 编译环境&#xff0c;在 Windows 平台上安装 MinGW 是比较简单的&#xff0c;只需经历以下几个过 程。 1、MINGW32位安装 1、打开 [MinGW 官网] https://osdn.net/projects/mingw/&#xff0c;下载 MinGW 安装包。 点击即可…

Linux 信号

文章目录 1. 信号1.1 前言1.2 信号的位置1.3 接口1.3.1 sigset_t1.3.2 信号集操作接口1.3.3 signal1.3.4 sigprocmask1.3.5 sigpending 2. 信号的处理2.1 内核态和用户态2.2 信号的监测和处理 1. 信号 1.1 前言 在 Linux 中&#xff0c;信号是一种用于进程之间的通信机制&…

MapstructPlus的快速集成

https://www.mapstruct.plus/https://www.mapstruct.plus/ # 博主技术栈如下 springboot:2.4.5 lombok:1.8.20 mapstruct-plus:1.3.4 knife4j:4.0.0目录 一、添加依赖&#xff08;谨防依赖冲突&#xff09; 二、如果依赖下不下来&#xff0c;要在maven的setting文件中加入腾讯…

Vue面试题整理

vue修饰符&适用场景 vue修饰符大致分为5类&#xff1a;表单修饰符&#xff0c;事件修饰符&#xff0c;鼠标修饰符&#xff0c;键盘修饰符&#xff0c;v-bind修饰符 路由守卫 全局路由守卫&#xff1a;beforeEach&#xff08;全局前置守卫&#xff09;&#xff0c;afterEa…

wsl子系统Ubuntu18.04,cuDNN安装

如果觉得本篇文章对您的学习起到帮助作用&#xff0c;请 点赞 关注 评论 &#xff0c;留下您的足迹&#x1f4aa;&#x1f4aa;&#x1f4aa; 本文主要wls子系统Ubuntu18.04安装cuDNN&#xff0c;安装cudnn坑巨多&#xff0c;因此记录以备日后查看&#xff0c;同时&#xff0…

PC市场寒冬,大众还需要PC吗?

PC市场寒冬&#xff0c;大众还需要PC吗&#xff1f; PC&#xff08;个人电脑&#xff09;市场从2016年智能手机兴起之时便进入下滑态势&#xff0c;到2020年疫情发生后&#xff0c;居家办公、在线教育等需求曾给PC市场带来连续六个季度的增长。⁴ 好景不长&#xff0c;进入202…

A Survey on Multimodal Large Language Models

本文是LLM系列的文章之一&#xff0c;主要是讲解多模态的LLM。针对《A Survey on Multimodal Large Language Models》的翻译。 多模态大语言模型的综述 摘要1 引言2 概述3 方法3.1 多模态指令调整3.1.1 引言3.1.2 前言3.1.3 模态对齐3.1.4 数据3.1.5 模态桥接3.1.6 评估 3.2 …

Android Studio实现内容丰富的安卓美食管理发布平台

如需源码可以添加q-------3290510686&#xff0c;也有演示视频演示具体功能&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动。 项目编号079 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看公告 3.查…

【网络进阶】Posix API与网络协议栈(三)

文章目录 1. 网络攻击和POSIX API与网络协议栈的关系1.1 网络攻击的基本概念和它们对协议栈的影响1.2 分布式拒绝服务&#xff08;DDoS&#xff09;攻击和网络协议栈1.3 地址解析协议&#xff08;ARP&#xff09;欺骗和POSIX API 2. 网络协议栈的理解和划分2.1 OSI七层模型2.2 …

PostgreSQL使用localhost可以连接,使用IP无法连接

问题描述&#xff1a;PostgreSQL使用localhost可以连接&#xff0c;使用IP无法连接 默认情况下&#xff0c;刚安装完成的 postgresSQL12 无法使用 数据库连接工具&#xff08;如postman&#xff09;连接。需要为其修改配置&#xff0c;开放连接权限。 修改pg_hba.conf 增加…