1. 程式人生 > 其它 >ElasticSearch全文搜尋引擎(二)-Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文件)

ElasticSearch全文搜尋引擎(二)-Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文件)

1 Spring Data概述

  Spring Data是spring提供的一套連線各種第三方資料來源的框架集,它支援連線很多第三方資料來源,例如:

  • 資料庫

  • redis

  • ElasticSearch

  • MongoDB等

  包括資料庫在內,很多第三方資料都可以使用SpringData操作,非常方便。

2 Spring Data Elasticsearch

  上面章節介紹了Spring Data可以連線很多第三方資料來源,其中ES就是Spring Data可以連線的物件。原生情況下,我們需要使用socket來連線ES獲得響應,再解析響應,程式碼量非常大,我們現在可以使用Spring Data提供的封裝,連線ES,方便快捷。

轉到knows-search模組:

  下面我們新增Spring Data ES的依賴:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

  application.properties

# 搜尋微服務埠
server.port=8066

# 搜尋微服務名稱
spring.application.name=search-service

# 定位ES的位置
spring.elasticsearch.rest.uris=http://localhost:9200

# 設定日誌門檻,顯示ES的操作資訊
logging.level.cn.tedu.knows.search=debug
# 還需要進一步設定才能使輸出日誌更清晰
logging.level.org.elasticsearch.client.RestClient=debug

  SpringBoot啟動類無需配置!

3 實現基本操作

  操作ES需要類:首先定義一個對應ES資料的型別,建立一個vo包,包中定義Item(商品)類程式碼如下:

package cn.tedu.knows.search.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
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 // lombok
@Accessors(chain = true) // 鏈式賦值(連續set方法)
@AllArgsConstructor // 全參構造
@NoArgsConstructor // 無參構造
//指定當前類物件對應哪個ES中的索引
//如果索引不存在,會自動建立
@Document(indexName = "items")
public class Item {
// 表示當前ES索引的id列
@Id
private Long id;
// 需要分詞的屬性使用Text型別,並指定分詞器
@Field(type = FieldType.Text,analyzer = "ik_smart",
searchAnalyzer = "ik_smart")
private String title; //商品名稱
// 不需要分詞的屬性使用Keyword型別,不用寫分詞器
@Field(type = FieldType.Keyword)
private String category;//分類

@Field(type = FieldType.Keyword)
private String brand; //品牌

@Field(type = FieldType.Double)
private Double price; //價格

//不會使用圖片地址查詢,設定index = false表示當前屬性不會建立索引,節省空間,因為這個屬性不會被查詢
@Field(type = FieldType.Keyword,index = false)
private String images; //圖片地址
// /upload/2021/08/19/abc.jpg

}

這個類中所有屬性均配置了對應ES的屬性和型別,下面我們就可以使用這個類操作ES了。

建立一個包repository,建立一個介面ItemRepository:

@Repository //將實現類的物件存到Spring容器中
//ElasticsearchRepository實現基本的增刪改查
public interface ItemRepository extends
ElasticsearchRepository<Item,Long> {
}

這個介面和Mybatis Plus中Mapper介面繼承的BaseMapper類似,會自動提供基本的增刪改查方法。下面進行測試,測試類程式碼如下:

package cn.tedu.knows.search;

import cn.tedu.knows.search.repository.ItemRepository;
import cn.tedu.knows.search.vo.Item;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@SpringBootTest
class KnowsSearchApplicationTests {
@Resource
ItemRepository itemRepository;
@Test
void contextLoads() {
//例項化物件,新增一個商品
Item item = new Item()
.setId(1L)
.setTitle("羅技鐳射無線遊戲滑鼠")
.setCategory("滑鼠")
.setBrand("羅技")
.setImages("/1.jpg")
.setPrice(285.0);
//呼叫Spring Data提供的方法進行新增,save表示新增
itemRepository.save(item);
System.out.println("ok");
}

//按id查詢
@Test
public void getId(){
//Optional表示裝著Item的盒子
Optional<Item> optional = itemRepository.findById(1L);
//通過get進行查詢
System.out.println(optional.get());
}

//批量新增
@Test
public void addAll(){
List<Item> list = new ArrayList<>();
list.add(new Item(2L,"羅技機械無線遊戲鍵盤","鍵盤","羅技",360.0,"/2.jpg"));
list.add(new Item(3L,"雷蛇鐳射有線遊戲滑鼠","滑鼠","雷蛇",488.0,"/3.jpg"));
list.add(new Item(4L,"羅技降噪藍芽競技耳機","耳機","羅技",378.0,"/4.jpg"));
list.add(new Item(5L,"華為靜音辦公有線滑鼠","滑鼠","華為",220.0,"/5.jpg"));
list.add(new Item(6L,"雷蛇競技機械無線鍵盤","鍵盤","雷蛇",425.0,"/6.jpg"));
itemRepository.saveAll(list);
System.out.println("ok");
}

//全查
@Test
public void getAll(){
//Iterable是List的父介面
Iterable<Item> list = itemRepository.findAll();
for(Item item : list){
System.out.println(item);
}
}

}
//上面都是基本操作,不需要我們自己寫介面中的方法

上面進行了單增、單查、批量增和全查的操作,下面進行自定義的查詢。

Spring Data支援編寫方法名錶達操作,會自動按方法名的表達生成實現程式碼,這是它的一大優勢!

在ItemRepository介面編寫方法:

// Spring Data框架連線資料來源,可以通過方法名來表達操作含義
// 根據商品的title屬性執行模糊查詢
Iterable<Item> queryItemsByTitleMatches(String title);

測試程式碼:

// 下面要完成一些條件查詢,需要呼叫ItemRepository介面中編寫的方法
// 商品標題模糊匹配
@Test
public void queryByTitle(){
Iterable<Item> items=itemRepository.queryItemsByTitleMatches("無線");
for(Item item: items){
System.out.println(item);
}
}

相當於運行了下面的指令:

### 單條件搜尋
POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {"match": { "title": "無線" }}
}

多屬性條件查詢:在ItemRepository介面編寫方法:

// 根據商品的title和brand執行模糊查詢
Iterable<Item> queryItemsByTitleMatchesAndBrandMatches(String title,String brand);

測試類:

// 測試多條件查詢
@Test
public void queryByTitleBrand(){
Iterable<Item> items=itemRepository
.queryItemsByTitleMatchesAndBrandMatches(
"遊戲","羅技"
);
for(Item item: items){
System.out.println(item);
}
}

實際執行的請求:

### 多欄位搜尋
POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {
"bool": {
"must": [
{ "match": { "title": "遊戲" }},
{ "match": { "brand": "羅技"}}
]
}
}
}

排序查詢:在ItemRepository介面編寫方法:

// 排序查詢:按照價格降序查詢標題或者品牌匹配的商品
Iterable<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title,String brand);

測試程式碼:

// 測試排序
@Test
public void order(){
Iterable<Item> items = itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc("遊戲","羅技");
for(Item item:items){
System.out.println(item);
}
}

實際執行的請求:

### 多欄位搜尋

POST http://localhost:9200/items/_search
Content-Type: application/json

{
"query": {
"bool": {
"should": [
{ "match": { "title": "遊戲" }},
{ "match": { "brand": "羅技"}}
]
}
},"sort":[{"price":"desc"}]
}

新增分頁查詢功能::在ItemRepository介面編寫方法

// 分頁查詢 Page相當於PageHelper,Pageable規定第幾頁包含多少行,相當於PageInfo
Page<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title, String brand, Pageable pageable);

測試:

//分頁查詢
@Test
public void page(){
int pageNum=1;
int pageSize=2;
//PageRequest.of返回Pageable,Pageable頁數從第0頁開始
Page<Item> page=itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
"遊戲","羅技",PageRequest.of(pageNum-1,pageSize));
//page實現了Iterable介面
for(Item item:page){
System.out.println(item);
}
}

4.ElasticSearch操作文件

### 建立 index
PUT http://localhost:9200/questions
### 刪除一個Index
DELETE http://localhost:9200/questions
### 設定index中的文件屬性採用ik分詞
### type=text的才能分詞,analyzer表示分詞器,根據分詞器對text內容進行分詞,建立索引
### search_analyzer表示搜尋內容的分詞器,一般與上面的分詞器相同,建立索引
### _mapping配合properties用來設定屬性
### 注意下面的換行,這裡是回車並換行,有嚴格格式要求,必須這樣書寫
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中新增文件
### POST一般為新增或修改的意思,_create表示建立文件,/1中的1表示文件id,為真正的id
### 每執行一次請求必須通過###來分割,既是分隔符,也是註釋符
POST http://localhost:9200/questions/_create/1
Content-Type: application/json

{
"id":1,
"title":"Java基本資料型別有哪些",
"content":"面時候為啥要問基本型別這麼簡單問題呀,我們要如何回答呢?"
}

### questions 中新增文件
POST http://localhost:9200/questions/_create/2
Content-Type: application/json

{
"id":2,
"title":"int型別的範圍",
"content":"為啥要了解int型別的範圍呢?"
}

### questions 中新增文件
POST http://localhost:9200/questions/_create/3
Content-Type: application/json

{
"id":3,
"title":"常用集合類有哪些",
"content":"為啥企業經常問集合呀?該如何回覆呢"
}

### questions 中新增文件
POST http://localhost:9200/questions/_create/4
Content-Type: application/json

{
"id":4,
"title":"執行緒的run方法和start方法有啥區別",
"content":"run方法可以執行執行緒的計算過程, start也可以執行執行緒的計算過程,用途一樣麼?"
}
### 更新questions索引中的文件
### 此處POST是更新的意思,表示對文件4進行更新
POST http://localhost:9200/questions/_doc/4/_update
Content-Type: application/json

{
"doc": {
"title": "Java執行緒的run方法和start方法有啥區別"
}
}
### 刪除questions中的一個文件,DELETE表示刪除
DELETE http://localhost:9200/questions/_doc/2
### 查詢資料,GET表示查詢
GET http://localhost:9200/questions/_doc/4
### 分詞搜尋 單屬性模糊查詢 查詢分詞索引,按照輸出得分(_score:查詢內容佔整個內容的比例)由高到低排序
POST http://localhost:9200/questions/_search
Content-Type: application/json

{
"query": { "match": {"title": "型別" } }
}
### 多欄位搜尋 多屬性模糊查詢 格式固定
### bool表示真假,should表示或,must表示與
### 查詢的內容也會分詞
POST http://localhost:9200/questions/_search
Content-Type: application/json

{
"query": {
"bool": {
"should": [
{ "match": { "title": "java型別" }},
{ "match": { "content": "java型別"}}
]
}
}
}