Py学习  »  Elasticsearch

从零玩转 Elasticsearch:日志分析,看这一篇就够了

Linux就该这么学 • 5 月前 • 183 次点击  

Elasticsearch(看这一篇就够了)

目录:

  • • Elasticsearch
    • • 介绍
      • • 正排索引和倒排索引
      • • Elasticsearch安装
        • • 安装ES服务
          • • 安装服务
          • • 安装kibana
      • • 索引操作
        • • 创建索引
          • • 查询索引库
          • • 修改索引库
          • • 删除索引库
      • • Elasticsearch常用操作
        • • 文档操作
            • • 新增文档
              • • 查询文档
              • • 删除文档
              • • 根据id批量查询文档
              • • 查询所有文档
              • • 修改文档部分字段
      • • 域的属性
      • • 分词器
        • • 默认分词器
          • • IK分词器
          • • 拼音分词器
          • • 自定义分词器
      • • Elasticsearch搜索文档
        • • 搜索方式
      • • SpringDaraES案例
        • • 使用Repository继承的方法查询文档
          • • 使用DSL语句查询文档
          • • 按照规则命名方法查询文档
          • • 分页查询
          • • 结果排序
          • • template工具类
            • • 操作索引
              • • 操作文档
              • • 查询文档
              • • 复杂条件查询
              • • 分页查询
              • • 结果排序
      • • Elasticsearch集群
        • • 搭建集群
          • • 测试集群状态
          • • 故障应对和水平扩容
          • • 内存设置
          • • 磁盘选择
          • • 分片策略
      • • Elasticsearch案例
        • • 需求说明
          • • ES自动补全
          • • 创建索引
          • • mysql数据导入es
          • • 项目搭建
          • • 创建实体类
          • • 创建Repository接口
          • • 自动补全功能
          • • 搜索关键字功能
          • • 创建Controller类
          • •  前端页面
在这里插入图片描述
在这里插入图片描述

Elasticsearch

介绍

在这里插入图片描述
在这里插入图片描述

Elasticsearch是一个全文检索服务器

全文检索是一种非结构化数据的搜索方式

  • • 结构化数据:指具有固定格式固定长度的数据,如数据库中的字段。
  • • 非结构化数据:指格式和长度不固定的数据,如电商网站的商品详情。

结构化数据一般存入数据库,使用sql语句即可快速查询。但由于非结构化数据的数据量大且格式不固定,我们需要采用全文检索的方式进行搜索。全文检索通过建立倒排索引加快搜索效率。

正排索引和倒排索引

索引

将数据中的一部分信息提取出来,重新组织成一定的数据结构,我们可以根据该结构进行快速搜索,这样的结构称之为索引。

索引即目录,例如字典会将字的拼音提取出来做成目录,通过目录即可快速找到字的位置。

索引分为正排索引倒排索引

正排索引(正向索引)
将文档id建立为索引,通过id快速可以快速查找数据。如数据库中的主键就会创建正排索引。
在这里插入图片描述
在这里插入图片描述
倒排索引(反向索引)
非结构化数据中我们往往会根据关键词查询数据。此时我们将数据中的关键词建立为索引,指向文档数据,这样的索引称为倒排索引。
在这里插入图片描述
在这里插入图片描述

Elasticsearch安装

安装ES服务

准备工作
1.准备一台搭载有CentOS7系统的虚拟机,使用XShell连接虚拟机

2.关闭防火墙,方便访问ES

#关闭防火墙:
systemctl stop firewalld.service


#禁止防火墙自启动:
systemctl disable firewalld.service





3.配置最大可创建文件数大小

#打开系统文件:
vim /etc/sysctl.conf


#添加以下配置:
vm.max_map_count=655360


#配置生效:
sysctl -p





  1. 4. 由于ES不能以root用户运行,我们需要创建一个非root用户,此处创建一个名为es的用户:
#创建用户:
useradd es





安装服务

  1. 1. 使用rz命令将linux版的ES上传至虚拟机
  2. 2. 解压ES
#解压:
tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz


#重命名:
mv elasticsearch-8.10.4 elasticsearch1


#移动文件夹:
mv elasticsearch1 /usr/local/


#es用户取得该文件夹权限:
chown -R es:es /usr/local/elasticsearch1





  1. 3. 启动ES服务:
#切换为es用户:
su es


#进入ES安装文件夹:
cd /usr/local/elasticsearch1/bin/


#启动ES服务:
./elasticsearch 





  1. 4. 当启动成功,可以看到类似以下的日志输出。首次启动Elasticsearch,默认会启用安全配置功能,启用身份认证和授权,内置超级用户elastic,并生成默认密码,此时要注意保存,否则之后启动不会再显示。
    在这里插入图片描述
    在这里插入图片描述
# 重置默认密码:
cd /usr/local/elasticsearch1/bin/
./elasticsearch-reset-password -u elastic


# 自定义密码:
cd /usr/local/elasticsearch1/bin/
./elasticsearch-reset-password --username elastic -i





  1. 5. 连接ES,查询ES服务是否启动成功
# 参数 --cacert指定了证书
curl --cacert /usr/local/elasticsearch1/config/certs/http_ca.crt -u elastic https://localhost:9200





安装kibana

Kibana是一款开源的数据分析和可视化平台,设计用于和Elasticsearch协作。我们可以使用Kibana对Elasticsearch索引中的数据进行搜索、查看、交互操作。

  1. 1. 使用rz工具将Kibana压缩文件上传到Linux虚拟机
  2. 2. 解压
tar -zxvf kibana-8.10.4-linux-x86_64.tar.gz -C /usr/local/





  1. 3. 修改配置
# 进入Kibana解压路径
cd /usr/local/kibana-8.10.4/config

# 修改配置文件
vim kibana.yml

# 加入以下内容
# 主机IP,服务名
server.host: "虚拟机IP"
server.name: "kibana"





  1. 4. 启动:

kibana不能以root用户运行,我们给es用户设置kibana目录的权限,并使用es用户运行kibana

# 给es用户设置kibana目录权限
chown -R es:es /usr/local/kibana-8.10.4/


# 切换为es用户
su es


# 启动kibana
cd /usr/local/kibana-8.10.4/bin/
./kibana





  1. 5. 访问kibana:http://虚拟机IP:5601
    首次访问Kibana管理台会提示输入ES生成的token秘钥,可以在ES首次启动日志中找。
在这里插入图片描述
在这里插入图片描述

如果token已失效或不正确,你也可以重新生成token。
# 进入es安装目录
cd /usr/local/elasticsearch1/bin

# 重新生成kibana的token
.elasticsearch-create-enrollment-token --scope kibana





紧接着输入登录账号 elastic,密码也同样是从ES首次启动日志中找。

  1. 6. 点击Management => Index Management可以查看es索引信息。

索引操作

创建索引

Elasticsearch是使用RESTful风格的http请求访问操作的,请求参数和返回值都是Json格式的,我们可以使用kibana发送http请求操作ES。

创建没有结构的索引
路径:ip地址:端口号/索引名

注:在kibana中所有的请求都会省略ip地址:端口号,之后的路径我们省略写ip地址:端口号

格式:

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









基本语法:

  • • 请求方式:PUT
  • • 请求路径:/索引库名,可以自定义
  • • 请求参数:mapping映射

格式

为索引添加结构

POST /索引名/_mapping
{
    "properties":{
        "域名1":{
            "type":域的类型,
            "store":是否存储,
            "index":是否创建索引,
      "analyzer":分词器
       },
       "域名2":{ 
            ...
        }
    }
}





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 /索引库名





Elasticsearch常用操作

文档操作

新增文档



    
POST /索引/_doc/[id值]
{
    "field名":field值
}

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





示例:

POST /jjy/_doc/1
{
    "info""jjy最牛啦",
    "email""zy@itcast.cn",
    "name": {
        "firstName""云",
        "lastName""赵"
    }
}





注:id值不写时自动生成文档id,id和已有id重复时修改文档

查询文档
GET /索引/_doc/id值





示例:

GET /jjy/_doc/1





删除文档
DELETE /索引/_doc/id值





示例:

DELETE /jjy/_doc/1





根据id批量查询文档
GET /索引/_mget
{
  "docs":[
     {"_id":id值},
     {"_id":id值}
   ] 
}





示例:

GET /jjy/_mget
{
   "docs":[
     {"_id":1},
     {"_id":2}
   ] 
}





查询所有文档
GET /索引/_search
{
   "query": {
     "match_all": {}
   }
}





示例:

GET /jjy/_search
{
   "query": {
     "match_all": {}
   }
}





修改文档部分字段
POST /索引/_update/1/

  "doc":{ 
    域名:值
    } 
}





示例:




    
POST /jjy/_update/id值/

  "doc":{ 
    info:"jjy好厉害哦"
    } 
}





注:

Elasticsearch执行删除操作时,ES先标记文档为deleted状态,而不是直接物理删除。当ES存储空间不足或工作空闲时,才会执行物理删除操作。

Elasticsearch执行修改操作时,ES不会真的修改Document中的数据,而是标记ES中原有的文档为deleted状态,再创建一个新的文档来存储数据。

域的属性

index

该域是否创建索引。只有值设置为true,才能根据该域的关键词查询文档。

// 根据关键词查询文档
GET /索引名/_search
{
    "query":{
    "term":{ 
            搜索字段: 关键字
        } 
   }
}





type

域的类型

核心类型具体类型
字符串类型
text
整数类型
long, integer, short, byte
浮点类型
double, float
日期类型
date
布尔类型
boolean
数组类型
array
对象类型
object
不分词的字符串
keyword

store
是否单独存储。如果设置为true,则该域能够单独查询。

// 单独查询某个域:
GET /索引名/_search
{
"stored_fields": ["域名"]
}





分词器

默认分词器

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

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

  • • 查看分词效果



    
GET /_analyze
{
    "text":测试语句, 
    "analyzer":分词器
}





IK分词器

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

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

安装IK分词器

  1. 1. 关闭es服务
  2. 2. 使用rz命令将ik分词器上传至虚拟机

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

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

unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/elasticsearch1/plugins/analysis-ik

  1. 4. 启动ES服务
su es


#进入ES安装文件夹:
cd /usr/local/elasticsearch1/bin/


#启动ES服务:
./elasticsearch





测试分词器效果

GET /_analyze
{
    "text":"测试语句"
    "analyzer":"ik_smart/ik_max_word"
}





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

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

    IK Analyzer 扩展配置
    --用户可以在这里配置自己的扩展字典 -->
    "ext_dict">ext_dict.dic
     --用户可以在这里配置自己的扩展停止词字典-->
    "ext_stopwords">ext_stopwords.dic
    --用户可以在这里配置远程扩展字典 -->
    -- "remote_ext_dict">words_location -->
    --用户可以在这里配置远程扩展停止词字典-->
    -- "remote_ext_stopwords">words_location -->






拼音分词器

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

安装拼音分词器

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

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

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

unzip elasticsearch-analysis-pinyin-8.10.4 -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分词器。

创建自定义分词器
在创建索引时自定义分词器

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":{ 
        ...
       }
     }
   }
}









测试自定义分词器

GET /索引/_analyze
{
"text""你好程序员",
"analyzer""ik_pinyin"
}





Elasticsearch搜索文档

添加一些文档数据

PUT /students
{
"mappings":{
    "properties":{
      "id": {
            "type""integer",
        "index": true
       },
         "name": {
            "type""text",
            "store": true,
        "index": true,
            "analyzer""ik_smart"
         },
      "info": {
            "type""text",
            "store": true,
        "index": true,
            "analyzer""ik_smart"
         }
     }
   }
}


POST /students/_doc/
{
"id":1,
"name":"程序员",
"info":"I love baizhan"
}


POST /students/_doc/
{
"id":2,
"name":"美羊羊",
"info":"美羊羊是羊村最漂亮的人"
}


POST /students/_doc/
{
"id":3,
"name":"懒羊羊",
"info":"懒羊羊的成绩不是很好"
}


POST /students/_doc/
{
"id":4,
"name":"小灰灰",
"info":"小灰灰的年纪比较小"
}


POST /students/_doc/
{
"id":5,
"name":"沸羊羊",
"info":"沸羊羊喜欢美羊羊"
}


POST /students/_doc/
{
"id":6,
"name":"灰太狼",
"info":"灰太狼是小灰灰的父亲,每次都会说我一定会回来的"
}









搜索方式

match_all:查询所有文档

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





match:全文检索。将查询条件分词后再进行搜索。

{
    "query":{
    "match":{
      搜索字段:搜索条件
     }
   }
}





注:在搜索时关键词有可能会输入错误,ES搜索提供了自动纠错功能,即ES的模糊查询。使用match方式可以实现模糊查询。模糊查询对中文的支持效果一般,我们使用英文数据测试模糊查询。

{
    "query": {
        "match": {
            "域名": {
            "query": 搜索条件,
            "fuzziness": 最多错误字符数,不能超过2
            }
        }
    }
}





range:范围搜索。对数字类型的字段进行范围搜索

{
    "query":{
    "range":{
      搜索字段:{ 
        "gte":最小值,
        "lte":最大值
       } 
     }
   }
}





gt/lt:大于/小于
gte/lte:大于等于/小于等于
match_phrase:短语检索。搜索条件不做任何分词解析,在搜索字段对应的倒排索引中精确匹配。

{
    "query":{
    "match_phrase":{
      搜索字段:搜索条件
     }
   }
}





term/terms:单词/词组搜索。搜索条件不做任何分词解析,在搜索字段对应的倒排索引中精确匹配

{
    "query":{
    "term":{ 
            搜索字段: 搜索条件
     }
   }
}

{
    "query":{
    "terms":{ 
            搜索字段: [搜索条件1,搜索条件2]
     }
   }
}





复合搜索

GET /索引/_search

    "query": { 
    "bool": { 
      // 必须满足的条件 
      "must": [ 
                搜索方式:搜索参数,
                搜索方式:搜索参数
       ],
      // 多个条件有任意一个满足即可
      "should": [
                搜索方式:搜索参数,
               搜索方式:搜索参数
           ],
            // 必须不满足的条件
           "must_not":[
               搜索方式:搜索参数,
               搜索方式:搜索参数
           ]
       } 
   } 
}









结果排序

ES中默认使用相关度分数实现排序,可以通过搜索语法定制化排序。

GET /索引/_search

"query": 搜索条件,
"sort": [
       {
           "字段1":{
               "order":"asc"
           } 
       },
       { 
           "字段2":{ 
               "order":"desc"
           } 
       }
   ] 
}









由于ES对text类型字段数据会做分词处理,使用哪一个单词做排序都是不合理的,所以 ES中默认不允许对text类型的字段做排序。如果需要使用字符串做结果排序,可以使用 keyword类型的字段作为排序依据,因为keyword字段不做分词处理。

高亮查询

在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。

我们可以在关键字左右加入标签字符串,数据传入前端即可完成高亮显示,ES可以对查询出的内容中关键字部分进行标签和样式的设置。

GET /索引/_search

    "query":搜索条件,
    "highlight":{
        "fields": { 
         "高亮显示的字段名": {
        // 返回高亮数据的最大长度
             "fragment_size":100,
                // 返回结果最多可以包含几段不连续的文字
             "number_of_fragments":5
         } 
     },
    "pre_tags":["前缀"], 
    "post_tags":["后缀"]
   } 
}









SQL查询

在ES7之后,支持SQL语句查询文档:

GET /_sql?format=txt
{
    "query": SQL语句
}





开源版本的ES并不支持通过Java操作SQL进行查询,如果需要操作 SQL查询,则需要氪金(购买白金版)

SpringDaraES案例

Java原生代码可以操作Elasticsearch,但操作比较繁琐,类似于数据库中的JDBC,我们还需要将ES文档手动封装为Java对象。所以开发中我们一般使用框架操作Elasticsearch。

Spring Data ElasticSearch是JAVA操作Elasticsearch的框架。它通过对原生API的封装,使得JAVA程序员可以简单的对Elasticsearch进行操作。

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

  1. 1. 创建SpringBoot项目,加入Spring Data Elasticsearch起步依赖:
<dependency>
  <groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<scope>provided scope>
dependency>





  1. 2. 编写配置文件,连接elasticsearch
spring:
  elasticsearch:
   uris:https://192.168.0.187:9200
   username:elastic
   password:elastic

#日志格式
logging:
pattern:
    console:'%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'





  1. 3. 创建配置类,跳过SSL证书检查。

从ES8开始,访问ES的协议从http变成了https,访问https请求需要SSL证书,在开发环境下我们不需要配置该证书,在项目中添加一个配置类,跳过SSL证书检查即可。

@Component
publicclassRestClientBuilderCustomizerImplimplementsRestClientBuilderCustomizer {
@Override
publicvoidcustomize(RestClientBuilder builder) {
   }


/**
   * 跳过SSL的证书检查
   */

@Override
publicvoidcustomize(HttpAsyncClientBuilder builder) {
    SSLContextBuildersscb= SSLContexts.custom();
    try {
      sscb.loadTrustMaterial((chain, authType) -> {
        returntrue;
       });
     } catch (NoSuchAlgorithmException e) {
      thrownewRuntimeException(e);
     } catch (KeyStoreException e) {
      thrownewRuntimeException(e);
     }


    try {
      builder.setSSLContext(sscb.build());
     } catch (KeyManagementException | NoSuchAlgorithmException e) {
      e.printStackTrace();
     }
   }
}









  1. 4. 创建实体类:
// 一个实体类的所有对象都会存入ES的一个索引中,所以我们在创建实体类时关联ES索引
@Document(indexName = "product",createIndex = true)
publicclassProduct {
@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:标记在成员变量上,标记为文档中的域,一般有如下属性:

type:域的类型

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

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

analyzer:分词器

searchAnalyzer:搜索时的分词器

  1. 5. 创建Repository接口
// Repository接口继承ElasticsearchRepository,该接口提供了文档的增删改查方法
@Repository
publicinterfaceProductRepositoryextendsElasticsearchRepository {
}





  1. 6. 测试Repository接口的方法
@SpringBootTest
publicclassProductRepositoryTest {
@Autowired
private ProductRepository repository;

@Test
publicvoidaddProduct(){
    Productproduct=newProduct(1"iphone30""iphone30是苹果最新手机");
    repository.save(product);
   }

@Test
publicvoidupdateProduct(){
    Productproduct=newProduct(1"iphone31""iphone31是苹果最新手机");
    repository.save(product);
   }

@Test
publicvoidfindAllProduct(){
    Iterable all = repository.findAll();
    for (Product product : all) {
      System.out.println(product);
     }
   }

@Test
public voidfindProductById(){
    Optional product = repository.findById(1);
    System.out.println(product.get());
   }

@Test
publicvoiddeleteProduct(){
    repository.deleteById(1);
   }
}









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

// 添加一些数据
repository.save(newProduct(2"三体1""三体1是优秀的科幻小说"));
repository.save(newProduct(3"三体2""三体2是优秀的科幻小说"));
repository.save(newProduct(4"三体3""三体3是优秀的科幻小说"));
repository.save(newProduct(5"elasticsearch""elasticsearch是基于lucene开发的优秀的搜索引擎"));





使用DSL语句查询文档

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




    
GET /索引/_search
{
  "query":{
    搜索方式:搜索参数
   }
}





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

@Query("{" +
    "   \"match\": {" +
    "    \"productDesc\": \"?0\"" +
    "   }" +
    "  }")

List findByProductDescMatch(String keyword);


@Query("{" +
    " \"match\": {" +
    "  \"productDesc\": {" +
    "   \"query\": \"?0\"," +
    "   \"fuzziness\": 1" +
    "  }" +
    " }" +
    "}")

List findByProductDescFuzzy(String keyword);









按照规则命名方法查询文档

  • • 只需在Repository接口中按照一定的规则命名方法,该方法就能完成相应的查询。
  • • 规则:查询方法以findBy开头,涉及查询条件时,条件的属性用条件关键字连接。
关键字
命名规则
解释
示例
and
findByField1AndField2
根据Field1和Field2 获得数据
findByTitleAndContent
or
findByField1OrField2
根据Field1或Field2 获得数据
findByTitleOrContent
is
findByField
根据Field获得数据
findByTitle
not
findByFieldNot
根据Field获得补集数据
findByTitleNot
between
findByFieldBetween
获得指定范围的数据
findByPriceBetween
List findByProductName(String productName);
List findByProductNameOrProductDesc(String productName,String productDesc);
List findByIdBetween(Integer startId,Integer endId);





分页查询

在使用继承或自定义的方法时,在方法中添加Pageable类型的参数,返回值为Page类型即可进行分页查询。

// 测试继承的方法:
@Test
public voidtestFindPage(){
// 参数1:页数,参数2:每页条数
Pageablepageable= PageRequest.of(13);
  Page page = repository.findAll(pageable);
  System.out.println("总条数"+page.getTotalElements());
  System.out.println("总页数"+page.getTotalPages());
  System.out.println("数据"+page.getContent());
}


// 自定义方法
Page findByProductDescMatch(String keyword, Pageable pageable);


// 测试自定义方法
@Test
publicvoidtestFindPage2(){
Pageablepageable= PageRequest.of(12);
  Page page = repository.findByProductDescMatch("我喜欢三体", pageable);
  System.out.println("总条数"+page.getTotalElements());
  System.out.println("总页数"+page.getTotalPages());
  System.out.println("数据"+page.getContent());
}









结果排序

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

// 结果排序
@Test
publicvoidtestFindSort(){
Sortsort = Sort.by(Sort.Direction.DESC, "id");
  Iterable all = repository.findAll(sort);
for (Product product : all) {
    System.out.println(product);
   }
}


// 测试分页加排序
@Test
publicvoidtestFindPage2(){
Sortsort= Sort.by(Sort.Direction.DESC,"id");
Pageablepageable= PageRequest.of(02,sort);
  Page page = repository.findByProductDescMatch("我喜欢三体", pageable);
  System.out.println("总条数"+page.getTotalElements());
  System.out.println("总页数"+page.getTotalPages());
  System.out.println("数据"+page.getContent());
}









template工具类

SpringDataElasticsearch提供了一个工具类ElasticsearchTemplate,我们使用该类对象也可以对ES进行操作。

操作索引
@SpringBootTest
publicclassTemplateTest {
@Autowired
private ElasticsearchTemplate template;


// 新增索引
@Test
publicvoidaddIndex() {
    // 获得索引操作对象
    IndexOperationsindexOperations= template.indexOps(Product.class);
    // 创建索引,注:该方法无法设置索引结构,不推荐使用
    indexOperations.create();
   }


// 删除索引
@Test
publicvoiddelIndex() {
    // 获得索引操作对象
    IndexOperationsindexOperations= template.indexOps(Product.class);
    // 删除索引
    indexOperations.delete();
   }
}











操作文档
// 新增/修改文档
@Test
publicvoidaddDocument() {
Productproduct=newProduct(7"es1""es是一款优秀的搜索引擎");
  template.save(product);
}


// 删除文档
@Test
publicvoiddelDocument() {
  template.delete("7", Product.class);
}


// 根据id查询
@Test
publicvoid findAllDocument() {
Productproduct= template.get("1", Product.class);
  System.out.println(product);
}











查询文档

template的search方法可以查询文档:

SearchHits search(Query query, Class clazz):查询文档,query是查询条件对象,clazz是结果类型。





用法如下:

// 查询文档2
@Test
publicvoidsearchDocument2() {
StringproductName="三体";
StringproductDesc="优秀";


// 1.构建查询条件
NativeQueryBuildernativeQueryBuilder=newNativeQueryBuilder();
// 如果没有传入参数,查询所有
if (productName == null && productDesc == null) {
    nativeQueryBuilder.withQuery(Queries.matchAllQueryAsQuery());
   } else {
    BoolQuery.BuilderboolQuery= QueryBuilders.bool();
    if (productName != null) {
      boolQuery.must(Queries.matchQueryAsQuery("productName", productName, nullnull));
     }
    if (productDesc != null) {
      boolQuery.must(Queries.matchQueryAsQuery("productDesc", productDesc, nullnull));
     }
    nativeQueryBuilder.withQuery(boolQuery.build()._toQuery());
   }
NativeQueryquery= nativeQueryBuilder.build();


// 2.查询
  SearchHits result = template.search(query, Product.class);
// 3.处理查询结果
for (SearchHit productSearchHit : result) {
    Productproduct= productSearchHit.getContent();
    System.out.println(product);
   }
}









复杂条件查询
// 查询文档2
@Test
publicvoidsearchDocument2(){
StringproductName="三体";
StringproductDesc="优秀";


// 1.构造查询条件
NativeQueryBuildernativeQueryBuilder=newNativeQueryBuilder();
// 如果没有传入参数,查询所有
if (productName == null && productDesc == null){
    nativeQueryBuilder.withQuery(Queries.matchAllQueryAsQuery());
   }else {
    BoolQuery.BuilderboolQuery= QueryBuilders.bool();
    if (productName != null){
      boolQuery.must(Queries.matchQueryAsQuery("productName",productName,null,null));
     }
    if (productDesc != null){
      boolQuery.must(Queries.matchQueryAsQuery("productDesc",productDesc,null,null));
     }
    nativeQueryBuilder.withQuery(boolQuery.build()._toQuery());
   }
NativeQueryquery= nativeQueryBuilder.build();


// 2.查询
  SearchHits result = template.search(query, Product.class);
// 3.处理查询结果
for (SearchHit productSearchHit : result) {
    Productproduct= productSearchHit.getContent();
    System.out.println(product);
   }
}











分页查询
// 分页查询文档
@Test
publicvoid searchDocumentPage() {


// 1.构建查询条件
Pageablepageable= PageRequest.of(03);
NativeQueryquery=newNativeQueryBuilder()
     .withQuery(Queries.matchAllQueryAsQuery())
     .withPageable(pageable)
     .build();
// 2.查询
  SearchHits result = template.search(query, Product.class);


// 3.处理查询结果
  List content = newArrayList();
for (SearchHit productSearchHit : result) {
    Productproduct= productSearchHit.getContent();
    content.add(product);
   }


/**
     * 封装Page对象,参数1:具体数据,参数2:分页条件对象,参数3:总条数
     */

  Page page = newPageImpl(content, pageable, result.getTotalHits());


  System.out.println(page.getTotalElements());
  System.out.println(page.getTotalPages());
  System.out.println(page.getContent());
}











结果排序
// 结果排序
@Test
publicvoidsearchDocumentSort() {
// 1.构建查询条件
NativeQueryquery=newNativeQueryBuilder()
     .withQuery(Queries.matchAllQueryAsQuery())
     .withSort(Sort.by(Sort.Direction.DESC,  "id"))
     .build();
// 2.查询
  SearchHits result = template.search(query, Product.class);


// 3.处理查询结果
for (SearchHit productSearchHit : result) {
    Productproduct= productSearchHit.getContent();
    System.out.println(product);
   }
}









Elasticsearch集群

在这里插入图片描述
在这里插入图片描述

在单台ES服务器上,随着一个索引内数据的增多,会产生存储、效率、安全等问题。
  1. 1. 假设项目中有一个500G大小的索引,但我们只有几台200G硬盘的服务器,此时是不可能将索引放入其中某一台服务器中的。
在这里插入图片描述
在这里插入图片描述
  1. 2. 此时我们需要将索引拆分成多份,分别放入不同的服务器中,此时这几台服务器维护了同一个索引,我们称这几台服务器为一个集群,其中的每一台服务器为一个节点,每一台服务器中的数据称为一个分片
在这里插入图片描述
在这里插入图片描述
  1. 3. 此时如果某个节点故障,则会造成集群崩溃,所以每个节点的分片往往还会创建副本,存放在其他节点中,此时一个节点的崩溃就不会影响整个集群的正常运行。
    在这里插入图片描述
    在这里插入图片描述

    节点(node):一个节点是集群中的一台服务器,是集群的一部分。它存储数据,参与集群的索引和搜索功能。集群中有一个为主节点,主节点通过ES内部选举产生。

集群(cluster):一组节点组织在一起称为一个集群,它们共同持有整个的数据,并一起提供索引和搜索功能。

分片(shards):ES可以把完整的索引分成多个分片,分别存储在不同的节点上。

副本(replicas):ES可以为每个分片创建副本,提高查询效率,保证在分片数据丢失后的恢复。

粗样式**
粗样式**

注:

分片的数量只能在索引创建时指定,索引创建后不能再更改分片数量,但可以改变副本的数量。

为保证节点发生故障后集群的正常运行,ES不会将某个分片和它的副本存在同一台节点上。

搭建集群

在这里插入图片描述
在这里插入图片描述

安装第一个ES节点

  1. 1. 修改系统进程最大打开文件数
#修改系统文件
vim /etc/security/limits.conf


#添加如下内容
es soft nofile 65535
es hard nofile 131072





  1. 2. 安装
#解压:
tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz


#重命名:
mv elasticsearch-8.10.4 myes1


#移动文件夹:
mv myes1 /usr/local/


#安装ik分词器
unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes1/plugins/analysis-ik


#安装拼音分词器
unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes1/plugins/analysis-pinyin


#es用户取得该文件夹权限:
chown -R es:es /usr/local/myes1









  1. 3. 修改配置文件
#打开节点一配置文件:
vim /usr/local/myes1/config/elasticsearch.yml





配置如下信息:

#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node1
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9200
#集群间通信端口号
transport.port: 9300
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证
xpack.security.enabled: false









  1. 4. 启动
#切换为es用户:
su es


#后台启动第一个节点:
ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes1/bin/elasticsearch -d





安装第二个ES节点

  1. 1. 安装
#解压:
tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz

#重命名:
mv elasticsearch-8.10.4 myes2

#移动文件夹:
mv myes2 /usr/local/

#安装ik分词器
unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes2/plugins/analysis-ik

#安装拼音分词器
unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes2/plugins/analysis-pinyin

#es用户取得该文件夹权限:
chown -R es:es /usr/local/myes2









  1. 2. 修改配置文件
#打开节点二配置文件:
vim /usr/local/myes2/config/elasticsearch.yml
配置如下信息:





#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node2
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9201
#集群间通信端口号
transport.port: 9301
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证
xpack.security.enabled: false









  1. 3. 启动
#切换为es用户:
su es

#后台启动第二个节点:
ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes2/bin/elasticsearch -d





安装第三个ES节点

  1. 1. 安装
#解压:
tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz

#重命名:
mv elasticsearch-8.10.4 myes3

#移动文件夹:
mv myes3 /usr/local/

#安装ik分词器
unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes3/plugins/analysis-ik

#安装拼音分词器
unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes3/plugins/analysis-pinyin

#es用户取得该文件夹权限:
chown -R es:es /usr/local/myes3









  1. 2. 修改配置文件
#打开节点一配置文件:
vim /usr/local/myes3/config/elasticsearch.yml





配置如下信息:

#集群名称,保证唯一
cluster.name: my_elasticsearch
#节点名称,必须不一样
node.name: node3
#可以访问该节点的ip地址
network.host: 0.0.0.0
#该节点服务端口号
http.port: 9202
#集群间通信端口号
transport.port: 9302
#候选主节点的设备地址
discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#候选主节点的节点名
cluster.initial_master_nodes: ["node1","node2","node3"]
#关闭安全认证
xpack.security.enabled: false









  1. 3. 启动
#切换为es用户:
su es

#后台启动第三个节点:
ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes3/bin/elasticsearch -d





测试集群
访问http://虚拟机IP:9200/_cat/nodes查看集群是否搭建成功。

kibana连接es集群

  1. 1. 在kibana中访问集群



    
# 打开kibana配置文件
vim /usr/local/kibana-8.10.4/config/kibana.yml





添加如下配置

# 该集群的所有节点
elasticsearch.hosts: ["http://127.0.0.1:9200","http://127.0.0.1:9201","http://127.0.0.1:9202"]







启动kibana

#切换为es用户:
su es

#启动kibana:
cd /usr/local/kibana-8.10.4/bin
./kibana





  1. 3. 访问kibana:http://虚拟机IP:5601

测试集群状态

  1. 1. 在集群中创建一个索引
PUT /product1
{
"settings": {
"number_of_shards"5, // 分片数
"number_of_replicas" 1 // 每个分片的副本数
  },
"mappings": {
"properties": {
   "id": {
    "type""integer",
    "store": true,
    "index": true
    },
   "productName": {
    "type""text",
    "store": true,
    "index": true
    },
   "productDesc": {
    "type""text",
    "store": true,
    "index": true
    }
   }
  }
}









  1. 2. 查看集群状态
# 查看集群健康状态
GET /_cat/health?v
# 查看索引状态
GET /_cat/indices?v
# 查看分片状态
GET /_cat/shards?v 





故障应对和水平扩容

  1. 1. 关闭一个节点,可以发现ES集群可以自动进行故障应对。
  2. 2. 重新打开该节点,可以发现ES集群可以自动进行水平扩容。
  3. 3. 分片数不能改变,但是可以改变每个分片的副本数、
PUT /索引/_settings
{
  "number_of_replicas": 副本数
}





内存设置

ES默认占用内存是4GB,我们可以修改config/jvm.option设置ES的堆内存大小,Xms表示堆内存的初始大小,Xmx表示可分配的最大内存。

  • • Xmx和Xms的大小设置为相同的,可以减轻伸缩堆大小带来的压力。
  • • Xmx和Xms不要超过物理内存的50%,因为ES内部的Lucene也要占据一部分物理内存。
  • • Xmx和Xms不要超过32GB,由于Java语言的特性,堆内存超过32G会浪费大量系统资源,所以在内存足够的情况下,最终我们都会采用设置为31G:
-Xms 31g
-Xmx 31g





例如:在一台128GB内存的机器中,我们可以创建两个节点,每个节点分配31GB内存。

磁盘选择

ES的优化即通过调整参数使得读写性能更快

磁盘通常是服务器的瓶颈。Elasticsearch重度使用磁盘,磁盘的效率越高,Elasticsearch的执行效率就越高。这里有一些优化磁盘的技巧:

  • • 使用SSD(固态硬盘),它比机械磁盘优秀多了。
  • • 使用RAID0模式(将连续的数据分散到多个硬盘存储,这样可以并行进行IO操作),代价是一块硬盘发生故障就会引发系统故障。
  • • 不要使用远程挂载的存储。

分片策略

分片和副本数并不是越多越好。每个分片的底层都是一个Lucene索引,会消耗一定的系统资源。且搜索请求需要命中索引中的所有分片,分片数过多会降低搜索性能。索引的分片数需要架构师和技术人员对业务的增长有预先的判断,一般来说我们遵循以下原则:

  • • 每个分片占用的硬盘容量不超过ES的最大JVM的堆空间设置(一般设置不超过32G)。比如:如果索引的总容量在500G左右,那分片数量在16个左右即可。
  • • 分片数一般不超过节点数的3倍。比如:如果集群内有10个节点,则分片数不超过30个。
  • • 推迟分片分配:节点中断后集群会重新分配分片。但默认集群会等待一分钟来查看节点是否重新加入。我们可以设置等待的时长,减少重新分配的次数:



    
PUT /索引/_settings
{
  "settings":{
    "index.unassianed.node_left.delayed_timeout":"5m"
   }
}





  • • 减少副本数量:进行写入操作时,需要把写入的数据都同步到副本,副本越多写入的效率就越慢。我们进行大批量进行写入操作时可以先设置副本数为0,写入完成后再修改回正常的状态。

Elasticsearch案例

需求说明

使用ES模仿百度搜索,即自动补全+搜索引擎效果:

ES自动补全

GET /索引/_search
{
"suggest": {
    "prefix_suggestion": {// 自定义推荐名
      "prefix""elastic",// 被补全的关键字
      "completion": {
        "field""productName",// 查询的域
        "skip_duplicates": true, // 忽略重复结果
        "size"10 //最多查询到的结果数
       }
     }
   }
}





注:自动补全对性能要求极高,ES不是通过倒排索引来实现的,所以需要将对应的查询字段类型设置为completion。

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


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


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


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









创建索引

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

使用logstash工具可以将mysql数据同步到es中:

  1. 1. 解压logstash-8.10.4-windows-x86_64

logstash要和elastisearch版本一致

  1. 2. 在logstash解压路径下的/config中创建mysql.conf文件,文件写入以下脚本内容:
input {
   jdbc {
     jdbc_driver_library => "F:\Elasticsearch8\mysql-connector-java-5.1.37-bin.jar"
     jdbc_driver_class => "com.mysql.jdbc.Driver"
     jdbc_connection_string => "jdbc:mysql:///news"
     jdbc_user => "root"
     jdbc_password => "root"
     schedule => "* * * * *"
     jdbc_default_timezone => "Asia/Shanghai"
     statement => "SELECT * FROM news;"
   }
}


filter {
    mutate {
        split => {"tags" => ","}
    }
}


output {
   elasticsearch {
        hosts => ["http://192.168.0.187:9200","http://192.168.0.187:9201","http://192.168.0.187:9202"]
     index => "news"
       document_id => "%{id}"
   }
}









  1. 3. 在解压路径下打开cmd黑窗口,运行命令:
bin\logstash -f config\mysql.conf





  1. 4. 测试自动补齐
GET /news/_search
{
"suggest": {
    "my_suggest": {
      "prefix""li",
      "completion": {
        "field""tags",
        "skip_duplicates": true,
        "size"10
       }
     }
   }
}





项目搭建

创建Springboot项目,加入SpringDataElasticsearch和SpringMVC的起步依赖

<dependency>
  <groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>





写配置文件:




    
# 连接elasticsearch
spring:
elasticsearch:
   uris:192.168.0.187:9200,192.168.0.187:9201,192.168.0.187:9202


# 日志格式
logging:
pattern:
console:'%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'





创建实体类

@Document(indexName = "news")
@Data
publicclassNews {
@Id
@Field
private Integer id;
@Field
private String title;
@Field
private String content;
@Field
private String url;
@CompletionField
@Transient
private Completion tags;
}











创建Repository接口

@Repository
publicinterface NewsRepositoryextendsElasticsearchRepository {
  
}





自动补全功能

@Service
publicclassNewsService {
@Autowired
private ElasticsearchClient client;

// 自动补齐
public List autoSuggest(String keyword)throws IOException {
    // 1.自动补齐查询条件
    Suggestersuggester= Suggester.of(
        s -> s.suggesters("prefix_suggestion", FieldSuggester.of(
            fs -> fs.completion(
                cs -> cs.skipDuplicates(true)
                     .size(10)
                     .field("tags")


             )
         )).text(keyword)
     );
    // 2.自动补齐查询
    SearchResponse response = client.search(s -> s.index("news")
         .suggest(suggester), Map.class);


    // 3.处理查询结果
    MapresultMap= response.suggest();
    List suggestionList = (List) resultMap.get("prefix_suggestion");
    Suggestionsuggestion= suggestionList.get(0);
    List resultList = suggestion.completion().options();


    List result = newArrayList<>();
     for (CompletionSuggestOption completionSuggestOption : resultList) {
      Stringtext= completionSuggestOption.text();
      result.add(text);
     }
    return result;
   }
}











搜索关键字功能

在repository接口中添加高亮搜索关键字方法

// 高亮搜索关键字
@Highlight(fields = {@HighlightField(name = "title"), @HighlightField(name = "content")})
ListfindByTitleMatchesOrContentMatches(String title, String content);





service类中调用该方法

// 查询关键字
public List highLightSearch(String keyword){
  List> result = repository.findByTitleMatchesOrContentMatches(keyword, keyword);
// 处理结果,封装为News类型的集合
  List newsList = newArrayList();
for (SearchHit newsSearchHit : result) {
    Newsnews= newsSearchHit.getContent();
    // 高亮字段
    Map> highlightFields = newsSearchHit.getHighlightFields();
    if (highlightFields.get("title") != null){
      news.setTitle(highlightFields.get("title").get(0));
     }
    if (highlightFields.get( "content") != null){
      news.setContent(highlightFields.get("content").get(0));
     }
    newsList.add(news);
   }
return newsList;
}









创建Controller类

@RestController
publicclassNewsController {
@Autowired
private NewsService newsService;

@GetMapping("/autoSuggest")
public List autoSuggest(String term)// 前端使用jqueryUI,发送的参数默认名为term
    return newsService.autoSuggest(term);
   }

@GetMapping("/highLightSearch")
public List highLightSearch(String term){
    return newsService.highLightSearch(term);
   }
}











前端页面

我们使用jqueryUI中的autocomplete插件完成项目的前端实现。

<script>
  $("#newsTag").autocomplete({
    source "/autoSuggest"// 请求路径
    delay100//请求延迟
    minLength1//最少输入多少字符像服务器发送请求
   })


functionsearch() {
    var term = $("#newsTag").val();
    $.get("/highLightSearch", {term: term}, function (data) {
      var str = "";
      for (var i = 0; i < data.length; i++) {
        vardocument = data[i];
        str += "
  • "
  •  +
              "    

    "

     +
              "      " + document.title + "" +
              "     " +
              "    

    "

     + document.content + "" +
              "  ";
           }
          $("#news").html(str);
         })
       }
    script>









    如果我的内容对你有帮助,请==点赞,评论,收藏==。创作不易,大家的支持就是我坚持下去的动力

    链接:https://blog.csdn.net/m0_74436895/article/details/142526240?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522f799522f5fddae157ff926066df2d9ef%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=f799522f5fddae157ff926066df2d9ef&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-6-142526240-null-null.142^v102^pc_search_result_base1&utm_term=Elasticsearch&spm=1018.2226.3001.4187


    END

    官方站点:www.linuxprobe.com

     Linux命令大全:www.linuxcool.com

    刘遄老师QQ:5604215

    Linux技术交流群:2636170

    (新群,火热加群中……)

    想要学习Linux系统的读者可以点击"阅读原文"按钮来了解书籍《Linux就该这么学》,同时也非常适合专业的运维人员阅读,成为辅助您工作的高价值工具书!


    Python社区是高质量的Python/Django开发社区
    本文地址:http://www.python88.com/topic/180998
     
    183 次点击