官网:https://www.elastic.co/cn/elasticsearch/
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/index.html
ES说明
elastic:富有弹性的
search:搜索
一般简称为ES,和redis/mysql一样,不仅服务于java,其他语言同样可以使用
功能也是类似数据库的软件,能高效的从大量数据中,搜索匹配指定关键词的内容
ES本质就是一个java开发的应用程序
预设了增删改查的接口,访问ES部署服务端的接口地址(URL),即可进行操作
ES的底层
ES底层使用了java一套名为Lucene的API,此API提供了全文搜索引擎核心操作的接口
ES是在Lucene的基础上进行的完善,实现了开箱即用的搜索引擎软件
市面上其他的全文搜索引擎:
- Solr(也是java实现,基本淘汰)
- MongoDB(目前还很火,偏大数据方向)
为什么需要ES
在传统的关系型数据库中,只要进行模糊查询,查询效率都极为低下,尤其使用前模糊查询
这个时候可以使用ES,来执行查询操作
ES进行优化后,从同样的数据量里进行相同条件的模糊查询,效率能够提高100倍以上!
运行
直接运行es安装包下,bin目录的elasticsearch.bat文件即可
默认端口号为9200,出现如下信息,表示启动成功
.....省略一堆.....
[2022-08-25T15:56:15,379][INFO ][o.e.n.Node ] [DESKTOP-K8VRATH] initialized
[2022-08-25T15:56:15,380][INFO ][o.e.n.Node ] [DESKTOP-K8VRATH] starting ...
[2022-08-25T15:56:15,702][INFO ][o.e.t.TransportService ] [DESKTOP-K8VRATH] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[::1]:9300}
[2022-08-25T15:56:15,912][WARN ][o.e.b.BootstrapChecks ] [DESKTOP-K8VRATH] the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
[2022-08-25T15:56:15,913][INFO ][o.e.c.c.Coordinator ] [DESKTOP-K8VRATH] cluster UUID [mhLGCp_LQXeLG0dGQZIlfw]
[2022-08-25T15:56:15,920][INFO ][o.e.c.c.ClusterBootstrapService] [DESKTOP-K8VRATH] no discovery configuration found, will perform best-effort cluster bootstrapping after [3s] unless existing master is discovered
[2022-08-25T15:56:16,026][INFO ][o.e.c.s.MasterService ] [DESKTOP-K8VRATH] elected-as-master ([1] nodes joined)[{DESKTOP-K8VRATH}{thQ9zjPJQ22JWYhAjrTE-g}{WG2oeMRHTj-gy8tbJ_taZg}{127.0.0.1}{127.0.0.1:9300}{dilm}{ml.machine_memory=34203144192, xpack.installed=true, ml.max_open_jobs=20} elect leader, _BECOME_MASTER_TASK_, _FINISH_ELECTION_], term: 2, version: 20, delta: master node changed {previous [], current [{DESKTOP-K8VRATH}{thQ9zjPJQ22JWYhAjrTE-g}{WG2oeMRHTj-gy8tbJ_taZg}{127.0.0.1}{127.0.0.1:9300}{dilm}{ml.machine_memory=34203144192, xpack.installed=true, ml.max_open_jobs=20}]}
[2022-08-25T15:56:16,062][INFO ][o.e.c.s.ClusterApplierService] [DESKTOP-K8VRATH] master node changed {previous [], current [{DESKTOP-K8VRATH}{thQ9zjPJQ22JWYhAjrTE-g}{WG2oeMRHTj-gy8tbJ_taZg}{127.0.0.1}{127.0.0.1:9300}{dilm}{ml.machine_memory=34203144192, xpack.installed=true, ml.max_open_jobs=20}]}, term: 2, version: 20, reason: Publication{term=2, version=20}
[2022-08-25T15:56:16,205][INFO ][o.e.l.LicenseService ] [DESKTOP-K8VRATH] license [db3b7d4b-9389-48d2-be41-5040d00898d1] mode [basic] - valid
[2022-08-25T15:56:16,206][INFO ][o.e.x.s.s.SecurityStatusChangeListener] [DESKTOP-K8VRATH] Active license is now [BASIC]; Security is disabled
[2022-08-25T15:56:16,212][INFO ][o.e.g.GatewayService ] [DESKTOP-K8VRATH] recovered [0] indices into cluster_state
[2022-08-25T15:56:16,213][INFO ][o.e.h.AbstractHttpServerTransport] [DESKTOP-K8VRATH] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]:9200}
[2022-08-25T15:56:16,213][INFO ][o.e.n.Node ] [DESKTOP-K8VRATH] started
此时,使用浏览器访问 http://localhost:9200/
会显示如下信息
{
"name": "DESKTOP-K8VRATH",
"cluster_name": "elasticsearch",
"cluster_uuid": "mhLGCp_LQXeLG0dGQZIlfw",
"version": {
"number": "7.6.2",
"build_flavor": "default",
"build_type": "zip",
"build_hash": "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date": "2020-03-26T06:34:37.794943Z",
"build_snapshot": false,
"lucene_version": "8.4.0",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "You Know, for Search"
}
ES运行原理
要想使用ES提高模糊查询效率,需要将数据库内需要查询的数据复制到ES中,
在新增数据到ES的过程中,ES可以对指定的列进行分词索引,保存到索引库中,形成倒排索引结构
如上图:左侧为数据库数据,右侧为ES内保存的数据,数字为id,这样进行模糊查询的时候通过ES直接搜索右侧数据,得到id即可快速查询到数据库内对应的数据
ES分词设置(analyzer参数)
- Standard Analyzer:默认分词器,按词切分,小写处理
- Simple Analyzer:按照非字母切分(符号被过滤),小写处理
- Stop Analyzer:停用词过滤(the,a,is),小写处理
- Whitespace Analyzer:按照空格切分,不转小写
- Keyword Analyzer:直接将输入当做输出,不分词
- Patter Analyzer:正则表达式,默认 \W+(非字符分割)
- Language:提供了30多种常见语言的分词器
ES对中文支持不好,所以需要安装别的分词器插件来实现中文分词,此处用的是IK分词器
给ES添加IK分词器插件
下载IK后,解压出来的文件+文件夹共8个
在ES安装目录下,plugins文件夹下,新建一个ik文件夹
然后将上图的文件全部粘贴进去
然后重启ES,应该可以看到此信息
[2022-08-25T16:50:37,209][INFO ][o.e.p.PluginsService ] [DESKTOP-K8VRATH] loaded plugin [analysis-ik]
IK分词器测试
IK内置了2个分词器
- ik_smart
- 优点:粗略分词,快速,占用空间小,速度快
- 缺点:分词粒度大,可能会跳过一些重要分词,导致查询结果不全面
- ik_max_word
- 优点:查询结果全面
- 缺点:分词粒度小,可能会有一些不常用的词语,导致占用空间大,查询速度慢
此时访问ES分词检测,analyzer使用ik_xxx即可使用中文词库进行分词
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "北京冬季奥林匹克运动会完美闭幕",
"analyzer": "ik_smart"
}
{
"tokens": [
{
"token": "北京",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "冬季",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "奥林匹克运动会",
"start_offset": 4,
"end_offset": 11,
"type": "CN_WORD",
"position": 2
},
{
"token": "完美",
"start_offset": 11,
"end_offset": 13,
"type": "CN_WORD",
"position": 3
},
{
"token": "闭幕",
"start_offset": 13,
"end_offset": 15,
"type": "CN_WORD",
"position": 4
}
]
}
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "北京冬季奥林匹克运动会完美闭幕",
"analyzer": "ik_max_word"
}
{
"tokens": [
{
"token": "北京",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "冬季",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "奥林匹克运动会",
"start_offset": 4,
"end_offset": 11,
"type": "CN_WORD",
"position": 2
},
{
"token": "奥林匹克",
"start_offset": 4,
"end_offset": 8,
"type": "CN_WORD",
"position": 3
},
{
"token": "奥林匹",
"start_offset": 4,
"end_offset": 7,
"type": "CN_WORD",
"position": 4
},
{
"token": "克",
"start_offset": 7,
"end_offset": 8,
"type": "CN_CHAR",
"position": 5
},
{
"token": "运动会",
"start_offset": 8,
"end_offset": 11,
"type": "CN_WORD",
"position": 6
},
{
"token": "运动",
"start_offset": 8,
"end_offset": 10,
"type": "CN_WORD",
"position": 7
},
{
"token": "会",
"start_offset": 10,
"end_offset": 11,
"type": "CN_CHAR",
"position": 8
},
{
"token": "完美",
"start_offset": 11,
"end_offset": 13,
"type": "CN_WORD",
"position": 9
},
{
"token": "闭幕",
"start_offset": 13,
"end_offset": 15,
"type": "CN_WORD",
"position": 10
}
]
}
ES的数据库结构
ES中的index(索引)可以简单理解为常规数据库中的表
如下图,有2个index,分别为users,questions
使用HTTP操作ES
ES中的数据搜索与操作
### 测试ES运行是否正常
GET http://localhost:9200
### 测试ES的分词功能
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "北京冬季奥林匹克运动会完美闭幕",
"analyzer": "ik_max_word"
}
### 创建 index
PUT http://localhost:9200/questions
### 删除一个Index
DELETE http://localhost:9200/questions
### 设置index中的文档属性采用ik分词
POST http://localhost:9200/questions/_mapping
Content-Type: application/json
{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
### questions 中添加文档1
POST http://localhost:9200/questions/_create/1
Content-Type: application/json
{
"id":1,
"title":"Java基本数据类型有哪些",
"content":"面时候为啥要问基本类型这么简单问题呀,我们要如何回答呢?"
}
### questions 中添加文档2
POST http://localhost:9200/questions/_create/2
Content-Type: application/json
{
"id":2,
"title":"int类型的范围",
"content":"为啥要了解int类型的范围呢?"
}
### questions 中添加文档3
POST http://localhost:9200/questions/_create/3
Content-Type: application/json
{
"id":3,
"title":"常用集合类有哪些",
"content":"为啥企业经常问集合呀?该如何回复呢"
}
### questions 中添加文档4
POST http://localhost:9200/questions/_create/4
Content-Type: application/json
{
"id":4,
"title":"线程的run方法和start方法有啥区别",
"content":"run方法可以执行线程的计算过程, start也可以执行线程的计算过程,用途一样么?"
}
### 更新questions索引中的文档
POST http://localhost:9200/questions/_doc/4/_update
Content-Type: application/json
{
"doc": {
"title": "Java线程的run方法和start方法有啥区别"
}
}
### 删除questions中的一个文档
DELETE http://localhost:9200/questions/_doc/2
### 查询数据
GET http://localhost:9200/questions/_doc/4
### 搜索 ES
POST http://localhost:9200/questions/_search
Content-Type: application/json
{
"query": { "match": {"title": "类型" } }
}
### 多字段搜索 should 为或的意思,包含任意一个即可,修改为 must 即为必须同时包含
POST http://localhost:9200/questions/_search
Content-Type: application/json
{
"query": {
"bool": {
"should": [
{ "match": { "title": "java类型" }},
{ "match": { "content": "java类型"}}
]
}
}
}
使用Java框架连接ES,并操作
在ES的原生状态下,在Java中,访问ES需要使用socket/http client去处理,但是过于繁琐
所以我们直接使用Spring Data框架来简化操作
Spring Data是Spring提供的一套连接各种第三方数据源的框架集
支持了各种三方数据源,如JDBC,JPA,Redis,MongoDB等.
官网:https://spring.io/projects/spring-data
添加依赖
<!--Elasticsearch依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
调整配置文件
spring:
elasticsearch:
rest:
# 设置ES服务端的地址与端口
uris: http://localhost:9200
logging:
level:
# SpringDataElasticsearch内部有个专门输出状态的类,设置一下debug,方便查看
org.elasticsearch.client.RestClient: debug
添加实体类
package cn.tedu.search.pojo.po;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
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;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
//此注解是SpringDataElasticsearch标记实体类的注解,indexName指定了ES中的索引名称
//如索引不存在,会自动创建,因为该注解的createIndex属性,默认为true;
//详细信息,请查看注解源代码
@Document(indexName = "items")
public class Item implements Serializable {
//表示当前字段为ES的主键
@Id
private Long id;
/**
* 标题
*/
//标记title属性的类型与支持分词以及相关分词器
@Field(type = FieldType.Text,
analyzer = "ik_max_word",
searchAnalyzer = "ik_max_word")
private String title;
/**
* 类别
*/
//类别不需要分词,所以不设置analyzer与searchAnalyzer属性
//并且设置type = FieldType.Keyword
//在ES中,Keyword是不需要分词的字符串
@Field(type = FieldType.Keyword)
private String category;
/**
* 品牌
*/
@Field(type = FieldType.Keyword)
private String brand;
/**
* 单价
*/
@Field(type = FieldType.Double)
private Double price;
/**
* 图片路径
*/
//因为图片路径是不会用来搜索的,所以设置index=false
//数据也是会存储在ES中,只是不会用来当成搜索的条件
@Field(type = FieldType.Keyword,index = false)
private String imgPath;
}
添加接口
接口命名规范
在Spring家族框架中,规范表明,接口包应名为repository
接口的名为XXXRepository,类似Mybatis中的mapper,此处为ItemRepository
方法命名规范
请注意,SpringData可以根据方法名称自动生成对应的实现,但是方法名称必须按照规范来
复杂的查询语句,推荐自己写原生的代码,具体参考最下面的测试类
简单的查询语句,基本语法为 findBy + 属性 + 关键词 + 连接符,示例如下
关键词 | 命名规则 | 解释 | 方法命名示例 |
---|---|---|---|
and | findByField1AndField2 | 根据Field1与Field2查询 | findByTitleAndBrand |
or | findByField1OrField2 | 根据Field1或Field2查询 | findByTitleOrBrand |
is | findByField | 根据Field获得数据 | findByTitle |
not | findByFieldNot | 根据Field获得补集 | findByTitleNot |
between | findByFieldBetween | 根据Field的范围查询 | findByPriceBetween |
完整的命名参考如下:
接口类
package cn.tedu.search.pojo.po.repository;
import cn.tedu.search.pojo.po.Item;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @version 0.0.1
* @Author: LianZuoYang
* @Create: 2022-08-26 11:32
*/
@Repository
public interface ItemRepository extends
//继承此接口,并设置泛型为Item(实体类),Long(实体类@id注解的主键类型)
//继承后,当前接口就会被识别为连接ES的持久层
//SpringData会为他自动生成基本的增删改查方法
ElasticsearchRepository<Item,Long> {
//⽅法命名规则查询的基本语法 findBy + 属性 + 关键词 + 连接符(属性 + 关键词 + 连接符)
//根据 标题查询
List<Item> findByTitle(String title);
//根据 标题以及品牌查询
List<Item> findByTitleAndBrand(String title,String brand);
//根据品牌查询
List<Item> findByBrand(String brand);
//根据品牌查询第一个
Optional<Item> findTopByBrand(String brand);
//根据 标题或品牌查询
List<Item> findByTitleOrBrand(String title,String brand);
//另外一种方法声明的写法
//查询多个结果
List<Item> queryItemsByTitleMatches(String title);
//只查询一个
Optional<Item> queryTopByTitleMatches(String title);
//多条件查询
List<Item> queryItemsByTitleMatchesAndBrandMatches(String title, String brand);
//多条件查询
List<Item> queryItemsByTitleMatchesOrBrandMatches(String title, String brand);
//查询多个结果并按照价格排序(默认按照分数/匹配度排)
List<Item> queryItemsByTitleMatchesOrderByPriceDesc(String title);
}
测试类
package cn.tedu.search;
import cn.tedu.search.pojo.po.Item;
import cn.tedu.search.pojo.po.repository.ItemRepository;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
/**
* @version 0.0.1
* @Author: LianZuoYang
* @Create: 2022-08-26 10:35
*/
@SpringBootTest
public class ESTests {
//装配ItemRepository(SpringData自动生成的)
@Autowired
private ItemRepository itemRepository;
//如果希望使用原生查询,那么让Spring帮你装配ElasticsearchRestTemplate这个对象就可以了
//老版本的ElasticsearchTemplate已经被弃用了
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Test
void save() {
//测试插入数据
Item item = Item.builder()
.id(1L)
.title("雷蛇 Razer 炼狱蝰蛇标准版 黑色新版 人体工程学 侧键 6400DPI 电竞游戏 有线鼠标")
.category("鼠标")
.brand("雷蛇")
.price(89.00)
.imgPath("/1.jpg")
.build();
itemRepository.save(item);
System.out.println("插入完毕");
}
@Test
void saveAll() {
//测试批量插入数据
Item item1 = Item.builder()
.id(2L)
.title("戴尔(DELL)MS116 鼠标有线 商务办公经典对称 有线鼠标 USB接口 即插即用 鼠标 (黑色)")
.category("鼠标")
.brand("戴尔")
.price(22.9)
.imgPath("/2.jpg")
.build();
Item item2 = Item.builder()
.id(3L)
.title("罗技(G)G502 HERO主宰者有线鼠标 游戏鼠标 HERO引擎 RGB鼠标 电竞鼠标 25600DPI")
.category("鼠标")
.brand("罗技")
.price(249.0)
.imgPath("/3.jpg")
.build();
Item item3 = Item.builder()
.id(4L)
.title("罗技(Logitech)M185鼠标 无线鼠标 办公鼠标 对称鼠标 黑色灰边 带无线2.4G接收器")
.category("鼠标")
.brand("罗技")
.price(49.0)
.imgPath("/4.jpg")
.build();
List<Item> list = new ArrayList<>();
list.add(item1);
list.add(item2);
list.add(item3);
itemRepository.saveAll(list);
System.out.println("批量插入完毕");
}
@Test
void count() {
long count = itemRepository.count();
System.out.println("总记录数量:" + count);
}
@Test
void findById() {
//根据ID获取一个数据
Optional<Item> optional = itemRepository.findById(1L);
if (optional.isPresent()) {
Item item = optional.get();
System.out.println(item);
} else {
System.out.println("此ID不存在");
}
}
@Test
void findByTitle() {
//根据条件查询
List<Item> items = itemRepository.findByTitle("USB");
System.out.println("共计搜索到数据条数:" + items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void findByBrand() {
//根据条件查询
List<Item> items = itemRepository.findByBrand("雷蛇");
System.out.println("共计搜索到数据条数:" + items.size());
for (Item item : items) {
System.out.println(item);
}
items = itemRepository.findByBrand("罗技");
System.out.println("共计搜索到数据条数:" + items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void findTopByBrand(){
Optional<Item> optional = itemRepository.findTopByBrand("罗技");
if(optional.isPresent()){
System.out.println(optional.get());
}else {
System.out.println("未查询到数据");
}
}
@Test
void findByTitleAndBrand() {
//根据条件查询
List<Item> items = itemRepository.findByTitleAndBrand("鼠标", "雷蛇");
System.out.println("共计搜索到数据条数:" + items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void findByTitleOrBrand() {
//根据条件查询
List<Item> items = itemRepository.findByTitleOrBrand("DPI", "罗技");
System.out.println("查询结果数量:" + items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void testNativeSearchQuery() {
//使用原生查询,而不使用接口去声明
//复杂的查询条件嵌套的,使用QueryBuilders.boolQuery()去处理
//下面的代码的查询结果与上面的findByTitleOrBrand()的结果是一致的,
//title包含[DPI]或brand为[罗技]
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.boolQuery()
.should(QueryBuilders.queryStringQuery("DPI").defaultField("title"))
.should(QueryBuilders.queryStringQuery("罗技").defaultField("brand"))
)
.withPageable(PageRequest.of(0, 15))//第一页,15个结果
.build();
//获取查询结果
SearchHits<Item> hits = restTemplate.search(nativeSearchQuery, Item.class);
//枚举结果
System.out.println("查询结果数量:" + hits.getTotalHits());
for (SearchHit<Item> hit : hits) {
Item item = hit.getContent();
System.out.println(item);
}
}
@Test
void testNativeSearchQuery2() {
//使用原生查询,演示复杂的嵌套
//(title包含[DPI]且包含[标准版]) 或 brand为[罗技]
//转换为sql的话,类似如下语句
//select * from items where (title like '%DPI%' AND title like '%标准版%') or brand = '罗技'
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.boolQuery()
.should(QueryBuilders.boolQuery()
.must(QueryBuilders.queryStringQuery("DPI").defaultField("title"))
.must(QueryBuilders.queryStringQuery("标准版").defaultField("title"))
)
.should(QueryBuilders.queryStringQuery("罗技").defaultField("brand"))
)
.withPageable(PageRequest.of(0, 15))//第一页,15个结果
.build();
//获取查询结果
SearchHits<Item> hits = restTemplate.search(nativeSearchQuery, Item.class);
//枚举结果
System.out.println("查询结果数量:" + hits.getTotalHits());
for (SearchHit<Item> hit : hits) {
Item item = hit.getContent();
System.out.println(item);
}
}
//另外一种写法的测试
@Test
void queryItem(){
Optional<Item> optional = itemRepository.queryTopByTitleMatches("鼠标");
if(optional.isPresent()){
System.out.println(optional.get());
}else {
System.out.println("未查询到数据");
}
}
@Test
void queryItems(){
List<Item> items = itemRepository.queryItemsByTitleMatches("鼠标");
System.out.println("查询到的数量:"+items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void queryItems2(){
List<Item> items = itemRepository.queryItemsByTitleMatchesAndBrandMatches("DPI","罗技");
System.out.println("查询到的数量:"+items.size());
for (Item item : items) {
System.out.println(item);
}
}
@Test
void queryItems3(){
List<Item> items = itemRepository.queryItemsByTitleMatchesOrBrandMatches("DPI","罗技");
System.out.println("查询到的数量:"+items.size());
for (Item item : items) {
System.out.println(item);
}
}
}
其他方法
参考CrudRepository接口,里面声明了一些基础的方法,就不在这里演示了,请自行查看!
下载
此处提供一个ES 7.6.2的压缩包,已集成ik,下载后可直接运行使用
https://download.csdn.net/download/lianzuo123/86500446
达内:lzy