目录
Elasticsearch介绍
Elasticsearch 是一个开源的分布式搜索和分析引擎,专为速度、扩展和 AI 应用而打造。作为一个检索平台,它可以实时存储结构化、非结构化和向量数据,提供快速的混合和向量搜索,支持可观测性与安全分析,并以高性能、高准确性和高相关性实现 AI 驱动的应用。
Elasticsearch 主要适用实时快速的全文搜索场景,它是分布式的,基于Restful分隔的API操作索引文档等,它和数据库(如Mysql)类比如下:
Elasticsearch核心的概念:分词、倒排索引、TD/IDF打分、分布式集群 。
分词:Elasticsearch默认分词器,根据空格和标点符号对英文进行分词,会进行单词的大小写转换,对中文搜索效果很差,IK分词器可以实现中文搜索效果。
倒排索引,也可以理解为反向索引,传统数据库的正向索引都是按照表的id正向的找到行的记录;Elasticsearch倒排索引是指通过文档关键词(分词后)指向对应的数据库的id,进而找到真正的数据库行记录,示意图如下(左图是正向索引、右图是倒排索引):
TD/IDF打分:用于对查询结果的排序。TD-IDF是一种统计方法,用于评估一个词在文档集中的重要程度。它的值越大,表示这个词在整个语料库中就越重要。TD-IDF算法由两部分组成:词频 (TF) 和逆文档频率 (IDF)。
分布式集群:Elasticsearch基于Raft协议选举leader。下图示例中,假定集群中有5个节点,节点3是leader,其他节点是从节点。索引数据分3片存储,每片数据有1份备份。
从上面的示意图可以看出,当客户端写入索引数据时,请求到集群任意一个节点,节点会自动转发给要写入的节点(假定索引数据要写入到分片1) ,写入完成后,并将分片1的数据同步到节点4。
从上面的示意图可以看出,当客户端查询数据时,请求到集群任意一个节点,假设请求的节点只包含分片1的数据,此时当前节点会分别转发包含分片2和分片3数据的节点,最终会汇总分片1-2-3的数据后返回给客户端。
Elasticsearch安装及Kibana的使用
Elasticsearch服务安装
安装步骤如下:
1、下载地址:Download Elasticsearch | Elastic (本文演示Windows平台),版本:8.17.3,下载elasticsearch-8.17.3-windows-x86_64.zip
2、解压elasticsearch-8.17.3-windows-x86_64.zip,运行elasticsearch.bat 启动
3、浏览器输入https://localhost:9200/ ,输入用户名密码elastic、NKzxfY05pHVHBwEh1na0 登录验证(用户名密码可以通过bin/elasticsearch-reset-password.bat 重置密码)
Kibana安装
Kibana是一款开源的数据分析和可视化平台,用于和Elasticsearch协作。可以使用Kibana对Elasticsearch索引中的数据进行搜索、查看、交互操作。安装步骤如下:
1、下载和Elasticsearch相同版本的kibana,下载地址:Download Kibana Free | Get Started Now | Elastic ,下载版本:kibana-8.17.3-windows-x86_64.zip
2、解压kibana-8.17.3-windows-x86_64.zip,修改config/kibana.yml 添加中文支持
3、运行bin/kibana.bat 启动,浏览器输入:http://localhost:5601/ ,输入和Elasticsearch 服务相同的用户名密码登录
分词器
ES文档的数据拆分成一个个有完整含义的关键词,并将关键词与文档对应,这样就可以通过关键词查询文档。要想正确的分词,需要选择合适的分词器。本文主要介绍默认分词器和IK分词器。
默认分词器
standard analyzer,默认分词器,根据空格和标点符号对英文进行分词,会进行单词的大小写转换。通过Kibana控制台查看此分词器效果:
IK分词器
Elasticsearch默认没有安装IK分词器,需要手工以插件的方式安装到Elasticsearch中,先到Index of: analysis-ik/stable/ 下载和Elasticsearch相同版本的IK分词器elasticsearch-analysis-ik-8.17.3.zip,解压导入到Elasticsearch的elasticsearch-8.17.3\plugins 目录,最后重启Elasticsearch即可。
IK分词器提供了2种分词算法:1、ik_smart:最少切分 2、ik_max_word:最细粒度划分,在kibana控制台演示如下:
{
"tokens": [
{
"token": "中华人民共和国",
"start_offset": 0,
"end_offset": 7,
"type": "CN_WORD",
"position": 0
},
{
"token": "中华人民",
"start_offset": 0,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "中华",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 2
},
{
"token": "华人",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 3
},
{
"token": "人民共和国",
"start_offset": 2,
"end_offset": 7,
"type": "CN_WORD",
"position": 4
},
{
"token": "人民",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 5
},
{
"token": "共和国",
"start_offset": 4,
"end_offset": 7,
"type": "CN_WORD",
"position": 6
},
{
"token": "共和",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 7
},
{
"token": "国",
"start_offset": 6,
"end_offset": 7,
"type": "CN_CHAR",
"position": 8
}
]
}
在实际项目中,一般写索引数据时用ik_max_word,查询搜索时用ik_smart。
Kibana操作
索引操作
Elasticsearch是使用RESTful风格的http请求访问操作的,请求参数和返回值都是Json格式的,我们可以使用kibana发送http请求操作ES。
创建索引
打开kibana控制台,输入如下信息,创建索引product
PUT /product
{
"mappings": {
"properties": {
"id":{"type": "long"},
"name":{"type": "text","analyzer": "ik_max_word","search_analyzer":"ik_smart"}
}
}
}
删除索引
删除索引:DELETE product
文档操作
创建文档:语法如下:POST /索引/_doc/[id值] { "field名":field值 } ,kibana示例如下:
查询文档:GET /索引/_doc/id值
删除文档:DELETE /索引/_doc/id值
文档数据搜索
基于【文档操作】向索引product中添加文档:1苹果手机,2小米手机,3小米汽车,4奔驰汽车,5小米电脑。
查询“汽车”后,命中小米汽车和奔驰汽车;查询“小米”后,命中小米汽车,小米手机,小米电脑,符合预期。
Springboot集成Elasticsearch
集成版本说明
本项目用到框架版本如下:
1、spring boot :3.1.12
2、elasticsearch : 8.17.3
3、JDK :17
Elasticsearch 8.x 推出后,官方推荐使用 Java REST Client 和 Elasticsearch Client,Spring Data Elasticsearch 也支持这些新的客户端。本文演示基于Spring Data Elasticsearch的方式操作Elasticsearch。操作步骤如下:
添加elasticsearch 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gingko</groupId>
<artifactId>elasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elasticsearch</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.1.12</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.gingko.elasticsearch.ElasticsearchApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
项目配置elasticsearch
注意:elasticsearch-8.17.3为确保数据传输的安全性,默认通过https来进行通信, 在开发环境中,暂时禁用HTTPS认证,修改:elasticsearch-8.17.3\config\elasticsearch.yml
编写测试代码测试
示例中以产品信息(Product)为例演示,包含id和名称name,其中name以ik_max_word分词器分词,索引建立在product中。
package com.gingko.elasticsearch.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName = "product")
public class Product {
@Id
private String id;
@Field(type= FieldType.Text,analyzer="ik_max_word",searchAnalyzer = "ik_smart")
private String name;
}
基于产品信息实体编写了2个方法,1个是建立索引,1个是根据name关键字查询数据,用到核心的类是ElasticsearchTemplate,建立索引:elasticsearchTemplate.save 方法;查询数据elasticsearchTemplate.search方法。
package com.gingko.elasticsearch.service;
import com.gingko.elasticsearch.entity.Product;
import jakarta.annotation.Resource;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ProductService {
@Resource
private ElasticsearchTemplate elasticsearchTemplate;
/**
* 根据产品信息建立索引
* @param product
* @return
*/
public String saveProduct(Product product) {
elasticsearchTemplate.save(product);
return "success";
}
/**
* 根据关键字搜索产品名称
* @param productName 产品名称关键字
* @return
*/
public List<Product> searchProducts(String productName) {
List<Product> result = new ArrayList<>();//查询结果
//构造搜索条件,查询name匹配关键字
Criteria criteria = new Criteria("name").matches(productName);
Query query = new CriteriaQuery(criteria);
SearchHits<Product> searchHits = elasticsearchTemplate.search(query, Product.class);
if(searchHits.hasSearchHits()) {
searchHits.getSearchHits().forEach(productSearchHit -> {
result.add(productSearchHit.getContent());
});
}
return result;
}
}
对外发布的controller接口如下:
package com.gingko.elasticsearch.controller;
import com.gingko.elasticsearch.entity.Product;
import com.gingko.elasticsearch.service.ProductService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("product")
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("/search")
public String search(@RequestParam String name) {
String str = "";
List<Product> result = productService.searchProducts(name);
if(result != null && result.size() > 0) {
for (Product product : result) {
str += product.getId() + ":" + product.getName() + ",";
}
}
return str;
}
@PostMapping("/createProduct")
public String createProduct(@RequestBody Product product) {
return this.productService.saveProduct(product);
}
}
通过postman 创建了7个document并索引,分别是:1:苹果手机,2:华为手机,3:小米手机,4:奔驰汽车,5:宝马汽车,6:奥迪汽车,7:小米汽车
通过关键字【小米】查询效果如下图,符合预期。