ES搜索学习

1、搜索分类

 

  • 顺序扫描:eg:使用like模糊搜索,select * from user where name like "%林%"
  • 全文检索:

2、全文检索

  搜索原理概括:

  • 内容爬取,停顿词过滤。eg:一些无用的像“的”,“了”子类的语气词/连接词
  • 内容分词,提取关键字
  • 根据关键词建立倒排索引
  • 用户输入关键词进行搜索       

3、倒排索引

索引就类似于目录,平时使用的索引都是通过主键定位到数据,倒排索引刚好相反,就是通过数据定位到主键

正向索引

idnamecontent
1lin01内容1
2lin02内容2
3lin01内容3
4lin某某内容2

通过id可以查询到name/content

反向索引

关键词id
lin011,3
内容22,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

重点需要注意的:

  1. 需要排序的字段,实体类型不能是 Bigdecimal 类型,否则排序会有问题(eg:字段类型为bigdecimal时,价格倒序时,价格为10.20 的商品会排在 价格为100的前面)
  2. 需要进行分词的字段 在es库中对应字段的 analyzer 要为 ik_max_word (这样在进行分词查询时,会把对应name字段分词,并且把搜索入参进行分词,然后进行匹配)

eg:"name" : {
          "type" : "text",
          "analyzer" : "ik_max_word"
        },

       3.使用matchPhraseQuery查询方法时,字段analyzer不要使用ik_max_word

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lin_XXiang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值