https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference
Spring Data Elasticsearch 是 Elasticsearch 搜索引擎开发的解决方案。它提供:
模板对象,用于存储、搜索、排序文档和构建聚合的高级API。
在 Elasticsearch 中存储学生数据,并对学生数据进行搜索测试。
数据结构:
学号 姓名 性别 出生日期
27 张三 男 2020-12-4
案例测试以下数据操作:
学号 | 姓名 | 性别 | 出生日期 |
---|---|---|---|
27 | 张三 | 男 | 2020-12-4 |
创建 students 索引和映射
C - 创建学生数据
R - 访问学生数据
U - 修改学生数据
D - 删除学生数据
使用 Repository 和 Criteria 搜索学生数据
spring:
elasticsearch:
rest:
uris:
- http://192.168.64.181:9200
- http://192.168.64.181:9201
- http://192.168.64.181:9202
logging:
level:
tracer: TRACE
在开始运行测试之前,在 Elasticsearch 中先创建 students 索引:
PUT /students
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2,
"index.max_ngram_diff":30,
"analysis": {
"analyzer": {
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 30,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text",
"analyzer": "ngram_analyzer"
},
"gender": {
"type": "keyword"
},
"birthDate": {
"type": "date",
"format": "yyyy-MM-dd"
}
}
}
}
/**
* @Document 用于指定索引,也可以用来自动创建索引,
* 一般索引是自己手动创建*/
@Document(indexName = "students",shards = 3,replicas = 2)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
@Id //使用学生学号作为索引id()
private Long id;
private String name;
private Character gender;
//设置索引中的字段名,对应实体类中的属性
//若字段名和属性名相同,可以省略此注解
@Field("birthDate")
private String birthDate;
}
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**Spring Data Repository 规范
* 只需要定义抽象接口,抽象方法,不需要自己实现
* Repository 底层代码已经实现了所有的增删改成的数据操作代码
* */
public interface StudentRepository
extends ElasticsearchRepository {//数据类型Student,索引类型Long
}
Spring Data 的 Repository 接口提供了一种声明式的数据操作规范,无序编写任何代码,只需遵循 Spring Data 的方法定义规范即可完成数据的 CRUD 操作。
ElasticsearchRepository 继承自 Repository,其中已经预定义了基本的 CURD 方法,我们可以通过继承 ElasticsearchRepository,添加自定义的数据操作方法。
自定义数据操作方法需要遵循 Repository 规范,示例如下:
Elasticsearch(五)Spring Data Elasticsearch - 增删改查API_wanght笔记-CSDN博客
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Optional;
@SpringBootTest
public class Test1 {
@Autowired
private StudentRepository r;
@Test //import org.junit.jupiter.api.Test;
public void test1() {
Student s1 = new Student(9527L, "唐伯虎", '男', "2021-09-29");
Student s2 = new Student(9528L, "华夫人", '女', "2021-08-29");
Student s3 = new Student(9529L, "祝枝山", '男', "2021-07-29");
Student s4 = new Student(9530L, "华安", '男', "2021-06-29");
Student s5 = new Student(9531L, "旺财", '男', "2021-05-29");
Student s6 = new Student(9532L, "小强", '男', "2021-04-29");
Student s7 = new Student(9533L, "秋香", '女', "2021-03-29");
Student s8 = new Student(9534L, "石榴姐", '男', "2021-02-29");
r.save(s1);
r.save(s2);
r.save(s3);
r.save(s4);
r.save(s5);
r.save(s6);
r.save(s7);
r.save(s8);
}
//修改
@Test
public void test2(){
Student student = new Student(9533L, "零零", '女', "2021-03-09");
r.save(student);
}
//查询
@Test
public void test3(){
Optional o1 = r.findById(9533L);
if (o1.isPresent()) { //是否存在/出现
Student student = o1.get();
System.out.println(student);
}
System.out.println("--------------------");
Iterable all = r.findAll();
for (Student s: all){
System.out.println(s);
}
}
//删除
@Test
public void test4(){
r.deleteById(9533L);
}
}
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
/**Spring Data Repository 规范
* 只需要定义抽象接口,抽象方法,不需要自己实现
* Repository 底层代码已经实现了所有的增删改成的数据操作代码
* */
public interface StudentRepository
extends ElasticsearchRepository {//数据类型Student,索引类型Long
// 在 name 中搜索关键词
List findByName(String nameKey);
// 在 name 和 birthDate 中搜索
List findByNameOrBirthDate(String name,String birthDate);
}
//根据名字搜索
@Test
public void test5(){
List l = r.findByName("华");
for (Student s:l){
System.out.println(s);
}
}
//根据名字或者生日搜索
@Test
public void test6(){
List list = r.findByNameOrBirthDate("华", "2021-08-29");
for(Student s:list){
System.out.println(s);
}
}
Spring Data Elasticsearch 中,可以使用 SearchOperations 工具执行一些更复杂的查询,这些查询操作接收一个 Query 对象封装的查询操作。
Spring Data Elasticsearch 中的 Query 有三种:
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class StudentSearcher {
//执行CriteriaQuery 查询的工具对象
@Autowired
private ElasticsearchOperations o;
// 任意定义的搜索方法
public List findByName(String nameKey){
Criteria c = new Criteria("name");
c.is(nameKey);
return exec(c);
}
public List findByBirthDate(String from,String to){
Criteria c = new Criteria("birthDate");
c.between(from,to);
return exec(c);
}
private List exec(Criteria c) {
// 把条件,封装到一个查询对象
CriteriaQuery q = new CriteriaQuery(c);
// 集合SearchHit[SearchHit, SearchHit, SearchHit...]
SearchHits shs = o.search(q, Student.class);
// List list =new ArrayList<>();
// for (SearchHit sh : shs) {
// Student s = sh.getContent();
// // 从SearchHit集合中取出的SearchHit放入list集合中
// list.add(s);
// }
// 可以使用集合的 Stream api 和 lambda 语法, 简化上面的代码
// 映射出SearchHit集合的数据,放到另一个集合中
List list = shs.stream().map(SearchHit::getContent).collect(Collectors.toList());
return list;
}
}
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class Test2 {
@Autowired
private StudentSearcher s;
@Test
public void test1() {
List list = s.findByName("华");
for (Student s : list) {
System.out.println(s);
}
}
@Test
public void test2() {
List list = s.findByBirthDate("2000-01-01", "2030-01-01");
for (Student s : list) {
System.out.println(s);
}
}
}
根据关键词搜索出来很多结果,会对结果进行分页
List findByName(String nameKey, Pageable pageable)
...
Pageable p =PageRequest.of(0,20);
r.finfByName("华,)
//根据名字搜索并分页
@Test
public void test5(){
Pageable p = PageRequest.of(0, 20);//第一页开始,每页20条数据
Page list = r.findByName("华", p);
for (Student s:list){
System.out.println(s);
}
System.out.println(list.getNumber());
System.out.println(list.hasPrevious());
System.out.println(list.hasNext());
System.out.println(list.getTotalPages());
System.out.println(list.getSize());
}
http://localhost/search/toSearch.html?key=%E6%89%8B%E6%9C%BA
org.springframework.boot
spring-boot-starter-data-elasticsearch
org.projectlombok
lombok
spring
....
elasticsearch:
rest:
uris:
- http://192.168.64.181:9200
- http://192.168.64.181:9201
- http://192.168.64.181:9202
package com.pd.pojo;
import lombok.AllArgsConstructor;
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;
@Document(indexName = "pditems") // 指定创建的索引
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Item {
@Id
private Long id;
private String brand;
private String title;
@Field("sell_point")//因为这个字段有下划线,需要进行指定
private String sellPoint;
private String price;
private String image;
}
package com.pd.es;
import com.pd.pojo.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ItemRepository
extends ElasticsearchRepository- {//对象类型,数据类型
//方法名必须要按格式书写
Page
- findByTitleOrSellPoint(String key1, String key2, Pageable pageable);
}
package com.pd.service;
import com.pd.pojo.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface SearchService {
Page- search(String key, Pageable pageable);
}
package com.pd.service.impl;
import com.pd.es.ItemRepository;
import com.pd.pojo.Item;
import com.pd.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private ItemRepository r;
@Override
public Page- search(String key, Pageable pageable) {
return r.findByTitleOrSellPoint(key,key,pageable); //pageable分页参数
}
}
package com.pd.controller;
import com.pd.pojo.Item;
import com.pd.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
//.../search/toSearch.html?key=手机&page=1&size=20
@GetMapping("/search/toSearch.html")
public String search(Model model, String key, Pageable pageable){
// Model 是用来向界面传递数据工具
Page- page =
searchService.search(key, pageable);//搜索结果
model.addAttribute("page",page);//结果放到model对象中
return "/search.jsp"; //在这里面显示
}
}
webapp-->commons-->headers.jsp
注意:
第一步:ItemRepository
package com.pd.es;
import com.pd.pojo.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ItemRepository extends ElasticsearchRepository- {
@Highlight(
parameters = @HighlightParameters(
preTags = "",
postTags = ""
),
fields = @HighlightField(name = "title")
)
List
> findByTitleOrSellPoint(String k1, String k2, Pageable pageable);
}
第二步:SearchService接口以及实现类
package com.pd.service;
import com.pd.pojo.Item;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHit;
import java.util.List;
public interface SearchService {
List> search(String key, Pageable pageable);
}
package com.pd.service.impl;
import com.pd.es.ItemRepository;
import com.pd.pojo.Item;
import com.pd.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private ItemRepository r;
@Override
public List> search(String key, Pageable pageable) {
return r.findByTitleOrSellPoint(key,key,pageable);
}
}
第三步:SearchController
package com.pd.controller;
import com.pd.pojo.Item;
import com.pd.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.List;
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
@GetMapping("/search/toSearch.html")
public String search(Model model, String key, Pageable pageable){
List> list = searchService.search(key, pageable);
List- items = new ArrayList<>();
for (SearchHit
- sh : list) {
Item it = sh.getContent();
String title=hlTitle(sh.getHighlightField("title"));
it.setTitle(title);
items.add(it);
}
model.addAttribute("items",items);
model.addAttribute("page",pageable);
// pageable.getPageSize();
// pageable.getPageNumber();
return "/search.jsp";
}
private String hlTitle(List
title) {
StringBuilder sb = new StringBuilder();
for (String s : title) {
sb.append(s);
}
return sb.toString();
}
}
第四步:search.jsp