1、搜索分类
顺序扫描:eg:使用like模糊搜索,select * from user where name like "%林%"
全文检索:
2、全文检索
搜索原理概括:
内容爬取,停顿词过滤。eg:一些无用的像“的”,“了”子类的语气词/连接词
内容分词,提取关键字
根据关键词建立倒排索引
用户输入关键词进行搜索
3、倒排索引
索引就类似于目录,平时使用的索引都是通过主键定位到数据,倒排索引刚好相反,就是通过数据定位到主键
正向索引
id name content
1 lin01 内容1
2 lin02 内容2
3 lin01 内容3
4 lin某某 内容2
通过id可以查询到name/content
反向索引
关键词 id
lin01 1,3
内容2 2,4
把name与content的字段抽取关键词。通过这个关键词搜索的时候,那么这个关键词就有可能出现在name/content字段内容中。
Elasticsearch 与 Mysql 对比:
ES 里的 Index 可以看做一个库,而 Types 相当于表, Documents 则相当于表的行。这里 Types 的概念已经被逐渐弱化, Elasticsearch 6.X 中,一个 index 下已经只能包含一个type, Elasticsearch 7.X 中, Type 的概念已经被删除了。
4、ElasticSearch简介
ElasticSearch是什么:简称ES,是一个分布式、RESTful风格的搜索和数据分析引擎。
优势:
完美封装了Lucene核心库,设计了友好的Restful-API,开箱即用
分片与副本机制,只解决了集群下性能与高可用问题
ES主要几个版本特性:
5.x版本(大转折)
发布时间:2016.10.26
主要特性
Lucene 6.x 的支持,磁盘空间少一半;索引时间少一半;查询性能提升25%;支持IPV6。
Internal engine级别移除了用于避免同一文档并发更新的竞争锁,带来15%-20%的性能提升
提供了第一个Java原生的REST客户端SDK IngestNode
提供了 Painless 脚本,代替Groovy脚本
新增了Profile API
新增了Rollover API
新增Reindex
提供了第一个Java原生的REST客户端SDK,基于HTTP协议的客户端对Elasticsearch的依赖解耦,没有jar包冲突,提供了集群节点自动发现、日志处理、节点请求失败自动进行请求轮询,充分发挥Elasticsearch的高可用能力
引入新的字段类型 Text/Keyword 来替换 String
限制索引请求大小,避免大量并发请求压垮 ES
限制单个请求的 shards 数量,默认 1000 个
仅支持非root用户启动
6.x版本
发布时间:2017.08.31
主要特性
稀疏性 Doc Values 的支持
Index sorting,即索引阶段的排序
Removal of types,在 6.0 里面,开始不支持一个 index 里面存在多个 type
已经关闭的索引将也支持 replica 的自动处理,确保数据可靠
Load aware shard routing, 基于负载的请求路由,目前的搜索请求是全节点轮询,那么性能最慢的节点往往会造成整体的延迟增加,新的实现方式将基于队列的耗费时间自动调节队列长度,负载高的节点的队列长度将减少,让其他节点分摊更多的压力,搜索和索引都将基于这种机制。
顺序号的支持,每个 es 的操作都有一个顺序编号(类似增量设计)无缝滚动升级
7.x版本
发布时间:2019.04.10
主要特性
集群连接变化:TransportClient被废弃以至于es7的java代码,只能使用restclient。对于java编程,建议采用 High-level-rest-client 的方式操作ES集群
ES程序包默认打包jdk
Lucene9.0的支持
正式废除单个索引下多Type的支持,es6时,官方就提到了es7会删除type,并且es6时已经规定每一个index只能有一个type。在es7中使用默认的_doc作为type,官方说在8.x版本会彻底移除type。,api请求方式也发送变化,如获得某索引的某ID的文档:GET index/_doc/id其中index和id为具体的值
7.1开始,Security功能免费使用
ECK-ElasticSearch Operator on Kubernetes,支持k8s
支持Zen2 是 Elasticsearch 的全新集群协调层,提高了可靠性、性能和用户体验,变得更快、更安全,并更易于使用
Weak-AND算法提高查询性能
默认的Primary Shared数从5改为1,避免Over Sharding
间隔查询(Intervals queries) 某些搜索用例(例如,法律和专利搜索)引入了查找单词或短语彼此相距一定距离的记录的需要。 Elasticsearch 7.0中的间隔查询引入了一种构建此类查询的全新方式,与之前的方法(跨度查询span queries)相比,使用和定义更加简单。 与跨度查询相比,间隔查询对边缘情况的适应性更强
支持arm平台的包下载,包括kibana,logstash
5、安装ES(Linux-centos7)
ES版本下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch
找到对应版本下载
注:ES不允许使用root账号启动服务,当前账号是root 就要用 su "账户名" eg:su es,没有另外一个账号就需要创建一个
# 创建用户并赋予相应的权限
adduser es
passwd es
chown -R es:es .
使用7.x以上的ES可以使用自带的jdk环境,环境配置:
注意:yum -y install vim* 安装vim
1、输入命令 echo $ES_JAVA_HOME 如有显示出路径,说明已经进行了配置,否则继续下一步。
2、vim /etc/profile 在最下面添加,修改配置:
export ES_JAVA_HOME=/usr/local/es/elasticsearch-7.17.3/jdk
export ES_JRE_HOME=$ES_JAVA_HOME/jre
export CLASSPATH=$ES_JRE_HOME/lib:$ES_JRE_HOME/lib:$CLASSPATH
export PATH=$ES_JAVA_HOME/bin:$ES_JRE_HOME/bin:$PATH
3、 vim config/elasticsearch.yml 文件修改
# 加入如下配置
network.host: 0.0.0.0 #ES开启远程访问
cluster.name: elasticsearch
node.name: node-1
http.port: 9200
cluster.initial_master_nodes: ["node-1"]
4、修改虚拟机jvm堆内存大小 vim config/jvm.options
-Xms1g
-Xmx1g
5、修改/etc/security/limits.conf
# 在文件末尾中增加下面内容
# 每个进程可以打开的文件数的限制
es soft nofile 65536
es hard nofile 65536
6、修改/etc/security/limits.d/20-nproc.conf
# 在文件末尾中增加下面内容
# 每个进程可以打开的文件数的限制
es soft nofile 65536
es hard nofile 65536
# 操作系统级别对每个用户创建的进程数的限制
* hard nproc 4096
# 注: * 带表 Linux 所有用户名称
7、修改/etc/sysctl.conf
# 在文件中增加下面内容
# 一个进程可以拥有的 VMA(虚拟内存区域)的数量,默认值为 65536
vm.max_map_count=655360
8、修改完sysctl.conf 之后 需要重新加载
sysctl -p
9、默认配置,至少需要配置 discovery.seed_hosts / discovery.seed_providers / cluster.initial_master_nodes
discovery.seed_hosts: ["222.186.173.176"] #自己linux的地址
# discovery.seed_providers: 基于配置文件配置集群主机列表
cluster.initial_master_nodes: ["node-1"] #启动时初始化的参与选主的node,生产环境必填
启动ES
#非root用户 启动ES
bin/elasticsearch
# -d 后台启动
bin/elasticsearch -d
6、分词器(ik)插件
下载:https://github.com/medcl/elasticsearch-analysis-ik/releases 需要找到对应es的版本(版本相同)
下载完之后解压到es文件夹下的plugins目录下的ik目录(新建一个ik目录)
注:在线下载到这个文件夹下时,记得要把压缩包删除
自定义词库:
1:现在对应的es版本的ik分词器的 jdbc-reload.properties 配置中配置对应的数据库地址:
2:添加热更新线程HotDictReloadThread
public class HotDictReloadThread implements Runnable {
private static final Logger LOGGER = ESPluginLoggerFactory.getLogger(HotDictReloadThread.class.getName());
@Override
public void run() {
while(true) {
LOGGER.info("[==========]reload hot dict from mysql......");
Dictionary.getSingleton().reLoadMainDict();
}
}
}
并且需要启动热词字典加载线程,在 Dictionary 原文件中修改(找到 initial 方法中添加)
3:加载自定义数据库拓展词典到主词库表(在 Dictionary类中添加 loadExtendDictFromMysql 加载拓展词方法 与 loadStopDictFromMysql 加载停用词方法)
public void loadExtendDictFromMysql(){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try{
Path file = PathUtils.get(getDictRoot(),"jdbc-reload.properties");
props.load(new FileInputStream(file.toFile()));
logger.info("loading jdbc-reload.properties");
for (Object key : props.keySet()) {
logger.info(key + "=" + props.getProperty(String.valueOf(key)));
}
logger.info(" hot dict " + props.getProperty("jdbc.reload.extend.sql"));
connection = DriverManager.getConnection(
props.getProperty("jdbc.url"),
props.getProperty("jdbc.user"),
props.getProperty("jdbc.password"));
statement = connection.createStatement();
resultSet = statement.executeQuery(props.getProperty("jdbc.reload.extend.sql"));
while (resultSet.next()){
// 加载扩展词典数据到主内存词典中
String theWord = resultSet.getString("word");
logger.info(theWord);
_MainDict.fillSegment(theWord.trim().toLowerCase().toCharArray());
}
// 加载时间
Thread.sleep(Integer.valueOf(String.valueOf(props.get("jdbc.reload.interval"))));
}catch (Exception e){
logger.error("[Extend Dict Loading] "+ e);
}finally {
if(resultSet != null){
try {
statement.close();
} catch (SQLException e) {
logger.error("[Extend Dict Loading] " + e);
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
logger.error("[Extend Dict Loading] " + e);
}
}
}
}
public void loadStopDictFromMysql(){
// 建立主词典实例
_StopWords = new DictSegment((char) 0);
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try{
Path file = PathUtils.get(getDictRoot(),"jdbc-reload.properties");
props.load(new FileInputStream(file.toFile()));
logger.info("loading jdbc-reload.properties");
for (Object key : props.keySet()) {
logger.info(key + "=" + props.getProperty(String.valueOf(key)));
}
logger.info(" stop dict " + props.getProperty("jdbc.reload.stop.sql"));
connection = DriverManager.getConnection(
props.getProperty("jdbc.url"),
props.getProperty("jdbc.user"),
props.getProperty("jdbc.password"));
statement = connection.createStatement();
resultSet = statement.executeQuery(props.getProperty("jdbc.reload.stop.sql"));
while (resultSet.next()){
// 加载扩展词典数据到主内存词典中
String theWord = resultSet.getString("word");
logger.info(theWord);
_StopWords.fillSegment(theWord.trim().toLowerCase().toCharArray());
}
// 加载时间
Thread.sleep(Integer.valueOf(String.valueOf(props.get("jdbc.reload.interval"))));
}catch (Exception e){
logger.error("[Stop Dict Loading] "+ e);
}finally {
if(resultSet != null){
try {
statement.close();
} catch (SQLException e) {
logger.error("[Stop Dict Loading] " + e);
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
logger.error("[Stop Dict Loading] " + e);
}
}
}
}
4:在 loadMainDict() 中添加自定义的加载拓展词的方法
5:在loadStopWordDict() 方法中添加自定义的加载停止词的方法
6:通过maven打包,
注意:我这里使用的是es自带的jdk,所以要把ip和端口开通socket网络链接权限
这个很重要!!!
到 es自带的 jdk/lib/security/default.policy文件中配置:
若不是使用es自带的jdk,那么常见的报错就是java.lang.ExceptionInInitializerError: null …access denied (“java.lang.RuntimePermission” “setContextClassLoader”)---配置内容位置相同
解决:
这是因为jdk权限不够
jdk14在/var/local/jdk-14.0.2/lib/security/default.policy中的grant{}里添加
jdk8是在/var/local/jdk1.8.0_271/jre/lib/security/java.policy中的grant{}里添加
permission java.lang.RuntimePermission "setContextClassLoader";
7、索引操作
创建索引:索引命名必须小写,不能以下划线开头
#创建索引 注:对比关系型数据库,创建索引就等同于创建数据库。
PUT /es_db
#创建索引可以设置分片数和副本数
PUT /es_db
{
"setting" : {
"number_of_shards" : 3,
"number_of_replicas" : 2
}
}
#查询索引
GET /es_db
#查询索引是否存在
HEAD /es_db
#删除索引
DELETE /ed_db
#关闭索引
POST /es_db/_close
#打开索引
POST /es_db/_open
#创建文档,指定id
PUT /es_db/_doc/1
{
"name":"张三",
"sex":1,
"age":25,
"address":"浙江杭州",
"remark":"初学es"
}
#创建文档,指定id
PUT /es_db/_create/2
{
"name":"李四",
"sex":2,
"age":18,
"address":"浙江杭州",
"remark":"初学es进阶"
}
#查询文档前10条信息内容
GET /es_db/_doc/_search
#查询对应id下的某一些信息
GET /es_db/_doc/1/_source
8、java-API操作elasticsearch
首先在pom中导入elasticsearch所需要的三个包
<!-- 使用elasticsearch必须导入的三个包 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.1.0</version>
</dependency>
创建索引
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import java.io.IOException;
public class CreateIndex {
public static void main(String[] args) throws IOException {
// 创建客户端对象
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 创建索引 - 请求对象
CreateIndexRequest request = new CreateIndexRequest("user2");
// 发送请求,获取响应
CreateIndexResponse response = client.indices().create(request,
RequestOptions.DEFAULT);
boolean acknowledged = response.isAcknowledged();
// 响应状态
System.out.println("操作状态 = " + acknowledged);
// 关闭客户端连接
client.close();
}
}
查询索引
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import java.io.IOException;
public class SearchIndex {
public static void main(String[] args) throws IOException {
// 创建客户端对象
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 查询索引 - 请求对象
GetIndexRequest request = new GetIndexRequest("user2");
// 发送请求,获取响应
GetIndexResponse response = client.indices().get(request,
RequestOptions.DEFAULT);
System.out.println("aliases:"+response.getAliases());
System.out.println("mappings:"+response.getMappings());
System.out.println("settings:"+response.getSettings());
client.close();
}
}
删除索引
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import java.io.IOException;
public class DeleteIndex {
public static void main(String[] args) throws IOException {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 删除索引 - 请求对象
DeleteIndexRequest request = new DeleteIndexRequest("user2");
// 发送请求,获取响应
AcknowledgedResponse response = client.indices().delete(request,RequestOptions.DEFAULT);
// 操作结果
System.out.println("操作结果 : " + response.isAcknowledged());
client.close();
}
}
写一个通用方法去连接并关闭elasticsearch
import org.elasticsearch.client.RestHighLevelClient;
public interface ElasticsearchTask {
void doSomething(RestHighLevelClient client) throws Exception;
}
public class ConnectElasticsearch{
public static void connect(ElasticsearchTask task){
// 创建客户端对象
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
try {
task.doSomething(client);
// 关闭客户端连接
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用这个通用方法使用lambda表达式新增一条文档数据,并且附上直接连接elasticsearch并添加数据
package com.es.mian;
import com.es.entity.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
public class InsertDoc {
public static void main(String[] args) {
ConnectElasticsearch.connect(client -> {
// 新增文档 - 请求对象
IndexRequest request = new IndexRequest();
// 设置索引及唯一性标识
request.index("user2").id("1001");
// 创建数据对象
User user = new User();
user.setName("李四");
user.setAge(18);
user.setSex("女");
ObjectMapper objectMapper = new ObjectMapper();
String productJson = objectMapper.writeValueAsString(user);
// 添加文档数据,数据格式为 JSON 格式
request.source(productJson, XContentType.JSON);
// 客户端发送请求,获取响应对象
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
//3.打印结果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_id:" + response.getId());
System.out.println("_result:" + response.getResult());
});
}
/*public static void main(String[] args) throws IOException {
// 传入IP地址和端口号
HttpHost httpHost = new HttpHost("localhost", 9200, "http");
// 创建客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(httpHost));
// 1.要在指定索引下创建文档,所以要先创建索引,再创建文档
IndexRequest request=new IndexRequest();
// index()方法设置索引名;id()方法设置唯一id标识
request.index("user2").id("10001");
// 2.创建实体类对象,填充数据
User user=new User();
user.setName("张三");
user.setAge(30);
user.setSex("男");
// 3.利用jackson将实体类对象转换成JSON格式字符串
ObjectMapper mapper=new ObjectMapper();
String userJson = mapper.writeValueAsString(user);
// 4.添加文档数据,数据格式为JSON格式
request.source(userJson, XContentType.JSON);
// 5.发送请求,获取响应结果
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
System.out.println("_index: "+response.getIndex());
System.out.println("_id: "+response.getId());
System.out.println("_result: "+response.getResult());
// 一番操作后,关闭客户端连接
client.close();
}*/
}
更新操作
package com.es.mian;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;
public class UpdateDoc {
public static void main(String[] args) {
ConnectElasticsearch.connect(client -> {
// 修改文档 - 请求对象
UpdateRequest request = new UpdateRequest();
// 配置修改参数
request.index("user2").id("1001");
// 设置请求体,对数据进行修改
request.doc(XContentType.JSON, "name", "李四-修改之后的数据");
// 客户端发送请求,获取响应对象
UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
System.out.println("_index:" + response.getIndex());
System.out.println("_id:" + response.getId());
System.out.println("_result:" + response.getResult());
});
}
}
查询user2索引下面的id为1001的文档数据内容
package com.es.mian;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.RequestOptions;
public class GetDoc {
public static void main(String[] args) {
ConnectElasticsearch.connect(client -> {
//1.创建请求对象
GetRequest request = new GetRequest().index("user2").id("1001");
//2.客户端发送请求,获取响应对象
GetResponse response = client.get(request, RequestOptions.DEFAULT);
//3.打印结果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_type:" + response.getType());
System.out.println("_id:" + response.getId());
System.out.println("source:" + response.getSourceAsString());
});
}
}
删除user2索引下面id为10001的文档数据
package com.es.mian;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.client.RequestOptions;
public class DeleteDoc {
public static void main(String[] args) {
ConnectElasticsearch.connect(client -> {
//创建请求对象
DeleteRequest request = new DeleteRequest().index("user2").id("10001");
//客户端发送请求,获取响应对象
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
//打印信息
System.out.println(response.toString());
});
}
}
9、Kibana安装
下载对应elasticsearch的版本的kibana eg:es版本为7.17.3 那么kibana下载7.17.3的版本
1、下载地址:https://www.elastic.co/cn/downloads/past-releases#kibana
2、对应压缩包放到对应目录 解压:tar -zxvf
10、ElasticSearch 基本概念
关系型数据库 与 ElasticSearch
在7.0之前,一个index可以设置多个Types
目前Type已经被Deprecated,7.0开始,一个索引只能创建一个Type - "_doc"
两者的区别
文档(Document)
Elasticsearch 是面向文档的,文档是所有可能搜索数据的最小单位
文档会被序列化成JSON格式,保存在Elasticsearch中
每一个文档都有一个Unique ID
一篇文档包含了一系列字段,类似于数据库表中的一条记录
JSON文档,格式灵活,不需要预先定义格式
批量写入
批量对文档进行写操作失通过 _bulk 的API来实现的
请求方式:post
请求地址:_bulk
请求参数:通过_bulk操作文档,一般至少有两行参数(或偶行参数)
-第一行参数为指定操作的类型及操作的对象(index,type和id)
-第二行参数才是操作的数据
参数类似于:
{“actionName”:{"_index":"indexName","_type":"typeName","_id":"id"}}
{"field1":"value1","field2":"value2"}
# actionName:表示操作类型,主要操作类型有 create,index,delete和update
批量查询
_mget批量读取
#批量读取对应index下的文档id,没有指定索引的_mget
GET _mget
{
"docs": [
{
"_index":"es_db",
"_id": "1"
},
{
"_index":"user2",
"_id":"1"
},
{
"_index":"user2",
"_id":"2"
}
]
}
#批量读取index为user2下的文档id,指定索引
GET /user2/_mget
{
"docs": [
{
"_id": "1"
},
{
"_id":"2"
},
{
"_id":"3"
}
]
}
#进一步简化
GET /user2/_mget
{
"ids":["1","2","3"]
}
_msearch: 在_msearch中,请求格式和bulk类似。查询一条数据需要两个对象,第一个设置index和type,第二个设置查询语句。查询语句和search相同。如果只是查询一个index,我们可以在url中带上indedx,这样,如果查询该index可以直接用空对象表示。
11、Easy-Es 的maven
easy-es 接口官网:快速开始 | Easy-Es
easy-es 主要是es结合了mybatis-plus,使用方式与mybatis-plus一样。
<!-- 引入easy-es最新版本的依赖-->
<dependency>
<groupId>cn.easy-es</groupId>
<artifactId>easy-es-boot-starter</artifactId>
<!--这里Latest Version是指最新版本的依赖,比如2.0.0,可以通过下面的图片获取-->
<version>Latest Version</version>
</dependency>
<!-- 排除springboot中内置的es依赖,以防和easy-es中的依赖冲突-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.0</version>
</dependency>
Elasticsearch 基础语法:
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//wildcardQuery 通配符查询,支持* 任意字符串;?任意一个字符
boolQuery.should(QueryBuilders.wildcardQuery("categoryId", "*"+item+"*"));
//termsQuery 分词精确查询
boolQuery.should(QueryBuilders.termsQuery("spuId", spuId));
//matchPhraseQuery 顺序相同不是指字段值的内容完全包含关键词,而是指分词顺序相同。
boolQuery.should(QueryBuilders.matchPhraseQuery("name", "黄山风景"));
//注意:此时分词搜索,name 为 "安徽黄山风景区"的内容,搜索不出来,因为"安徽黄山风景区"和"黄山风景"分词,发现分词"山风"和"风景"之间还间隔了"风景区"这个分词,所以不符合条件。
//解决可以设置slop解决分词间隔数量(分析:对第一条文档中的字段分词,"安徽黄山风景区"分词后发现安徽距离风景区相差两个字段,所以设置slop=2可以解决默认相隔字段为0的问题。)
boolQuery.should(QueryBuilders.matchPhraseQuery("name", "黄山风景").slop(2))
//matchQuery 即匹配查询。返回与提供的文本、数字、日期或布尔值匹配的文档。在匹配之前分析提供的文本。匹配查询是执行全文搜索的标准查询,包括模糊匹配选项。
boolQuery.should(QueryBuilders.matchQuery("dosTitle","我爱中华"))
//rangeQuery 范围查询
boolQuery.should(QueryBuilders.boolQuery().must(
QueryBuilders.boolQuery()
//定时上架时间 大于等于当前时间
.must(QueryBuilders.rangeQuery("offShelvesTime" ).gte(date.getTime()))
//定时下架时间 小于等于当前时间
.must(QueryBuilders.rangeQuery("putawayStartTime" ).lte(date.getTime())))
);
//条件写完之后,进行查询
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
//查询条件
.withQuery(boolQuery)
//分页
.withPageable(PageRequest.of(pageNum, pageSize))
//排序
.withSort(sortBuilders.scoreSort().order(SortOrder.DESC));
val search = restTemplate.search(nativeSearchQueryBuilder.build(), EsGoodsSpuEs.class);
for (SearchHit<EsGoodsSpuEs> searchHit : search.getSearchHits()) {
list.add(searchHit.getContent());
}
map.put("total",search.getTotalHits()); //es搜索出来的总条数
map.put("list",list); //当前页数据list
重点需要注意的:
需要排序的字段,实体类型不能是 Bigdecimal 类型,否则排序会有问题(eg:字段类型为bigdecimal时,价格倒序时,价格为10.20 的商品会排在 价格为100的前面)
需要进行分词的字段 在es库中对应字段的 analyzer 要为 ik_max_word (这样在进行分词查询时,会把对应name字段分词,并且把搜索入参进行分词,然后进行匹配)
eg:"name" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
3.使用matchPhraseQuery查询方法时,字段analyzer不要使用ik_max_word
————————————————
版权声明:本文为CSDN博主「Lin_XXiang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Lin_XXiang/article/details/127605205