全文搜索引擎 ES(Elasticsearch) 简单使用说明

官网: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 + 属性 + 关键词 + 连接符,示例如下

关键词命名规则解释方法命名示例
andfindByField1AndField2根据Field1与Field2查询findByTitleAndBrand
orfindByField1OrField2根据Field1或Field2查询findByTitleOrBrand
isfindByField根据Field获得数据findByTitle
notfindByFieldNot根据Field获得补集findByTitleNot
betweenfindByFieldBetween根据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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值