MySQL, Oracle, Linux, 软件架构及大数据技术知识分享平台

网站首页 > 精选文章 / 正文

使用Elasticsearch检索 elasticsearch搜索语法

2024-12-29 03:29 huorong 精选文章 4 ℃ 0 评论

Elasticsearch加载数据

我们要想完成高效的搜索任务,需要ES的支持

因为数据库的模糊查询效率太低了

我们就是说,我们再前端页面中完成的搜索是从ES中搜索数据

这样就要求,我们再查询之前,需要先将商品信息(spu)保存到ES中

准备实体类

我们搜索功能编写在mall-search模块中

找到cn.tedu.mall.pojo.search.entity下的SpuforElastic

这个类有四个需要分词的属性

//SPU名称
@Field(name = "name",type = FieldType.Text,
analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
@ApiModelProperty(value="SPU名称")
private String name;
//.....

//标题
@Field(name="title",type = FieldType.Text,
analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
@ApiModelProperty(value="标题")
private String title;
// 简介
@Field(name="description",type = FieldType.Text,
analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
@ApiModelProperty(value="简介")
private String description;
//.....

//类别名称(冗余)

@Field(name="category_name",type = FieldType.Text,
analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
@ApiModelProperty(value="类别名称(冗余)")
private String categoryName;
//.....

创建ES的持久层

连接ES的持久层的包名repository(SpringData的规范名称)

创建SpuForElasticRepository代码如下

public interface SpuForElasticRepository extends
ElasticsearchRepository<SpuForElastic,Long> {
}

这个接口提供了批量新增到ES数据的方法

但是要想获得数据库中的所有pms_spu表的数据,必须连接数据库查询这些数据

但是search模块是负责管理ES的,所以需要Dubbo调用Product模块获取所有数据

Product模块查询所有数据

product查询所有spu的持久层代码SpuMapper

// 全查所有spu
@Select("select * from pms_spu")
List<Spu> findAllList();

业务逻辑层用分页进行查询

ForFrontSpuServiceImpl添加方法

@Override
public JsonPage<Spu> getSpuByPage(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum,pageSize);
List<Spu> list=spuMapper.findAllList();
return JsonPage.restPage(new PageInfo<>(list));
}

因为一般加载到ES中的数据量非常大(几十万上百万条),

我们不可能一次性将所有数据查询出来,增到ES中,必须分批分次

分页查询就是典型的分批查询,每次查询一部分数据,通过循环遍历,将每页数据都新增到ES中

Search模块执行加载

mall-search模块 创建service.impl包

包中创建ServiceLocalServiceImpl类,用于本地ES新增和使用

业务逻辑层代码如下

@Service
@Slf4j
public class ServiceLocalServiceImpl implements ISearchService {
@DubboReference // Dubbo消费模块
private IForFrontSpuService dubboSpuService;
@Autowired
private SpuForElasticRepository spuRepository;
// 利用Dubbo从product模块中分页查询所有spu数据,并新增到ES的操作
@Override
public void loadSpuByPage() {
// 先要查询一次,才能知道分页信息,制定循环条件,是典型的先运行后判断
// 所以推荐大家使用do-while循环
int i=1; //循环次数,也是页码
int pages=0; // 总页数初始值为0即可
do{
// Dubbo查询当前页spu信息
JsonPage<Spu> spus=dubboSpuService.getSpuByPage(i,2);
// 实例化一个SpuForElastic泛型的集合,以备spus集合中的数据转换后赋值添加
List<SpuForElastic> esSpus=new ArrayList<>();
// 遍历从数据库中查询出的spus
for(Spu spu:spus.getList()){
// 实例化一个ES的实体类
SpuForElastic esSpu=new SpuForElastic();
// 将spu对象的同名属性赋值给esSpu
BeanUtils.copyProperties(spu,esSpu);
// 将赋好值的esSpu对象,新增到esSpus集合中
esSpus.add(esSpu);
}

// 利用SpringData框架提供的批量新增方法执行新增操作
spuRepository.saveAll(esSpus);
pages=spus.getTotalPage();
log.info("成功新增第{}页数据",i);
i++;
}while (i<=pages);// 循环条件是当前页码不超过总页数
}
@Override
public JsonPage<SpuEntity> search(String keyword, Integer page, Integer pageSize) {
return null;
}
}

下面进行测试

@SpringBootTest
public class TestSearch {
@Autowired
private ISearchService searchService;
@Test
void loadData(){
searchService.loadSpuByPage();
System.out.println("ok");
}
}

运行测试前保证

Nacos\Seata\\ES

Leaf\product\

运行测试,没有报错即可

验证数据加载

我们可以继续在测试类中编写全查当前ES的方法

遍历输出

验证当前数据加载成功

@Autowired
private SpuForElasticRepository elasticRepository;
@Test
void getAll(){
Iterable<SpuForElastic> es=elasticRepository.findAll();
es.forEach(e-> System.out.println(e));
}

测试自定义条件查询

上次课我们已经将数据库中的spu保存到了ES中,并能够全查出来

我们现在可以在Repository接口中新增自定义方法查询包含"手机"的商品信息

SpuForElasticRepository

@Repository
public interface SpuForElasticRepository extends
ElasticsearchRepository<SpuForElastic,Long> {
// 自定义查询方法
Iterable<SpuForElastic> querySpuForElasticsByTitleMatches(String title);
}

测试类添加方法测试

@Test
void getSpu(){
Iterable<SpuForElastic> it=elasticRepository
.querySpuForElasticsByTitleMatches("手机");
it.forEach(e-> System.out.println(e));
}

保证Nacos\Redis\ES启动

不需要其他项目,直接启动测试即可

下面完成多条件查询

SpringData也支持我们再代码中编写查询语句

@Query("{\n" +
" \"bool\": {\n" +
" \"should\": [\n" +
" { \"match\": { \"name\": \"?0\"}},\n" +
" { \"match\": { \"title\": \"?0\"}},\n" +
" { \"match\": { \"description\": \"?0\"}},\n" +
" { \"match\": { \"category_name\": \"?0\"}}\n" +
" ]\n" +
" }\n" +
"}")
Iterable<SpuForElastic> querySearch(String keyword);

测试代码

@Test
void getKeyword(){
Iterable<SpuForElastic> it=elasticRepository.querySearch("手机");
it.forEach(e-> System.out.println(e));
}

再实际开发中

我们ES数据还有更新问题

所有对Spu数据库表进行增删改操作时,都需要将操作同步到ES

也就是在业务逻辑层中,更新完spu表后,还要更新ES,这会有很多问题,不但业务复杂,而且还有事务问题

我们会在后面学习Linux系统中有更好的解决办法

最后添加支持分页查询的持久层方法

@Query("{\n" +
" \"bool\": {\n" +
" \"should\": [\n" +
" { \"match\": { \"name\": \"?0\"}},\n" +
" { \"match\": { \"title\": \"?0\"}},\n" +
" { \"match\": { \"description\": \"?0\"}},\n" +
" { \"match\": { \"category_name\": \"?0\"}}\n" +
" ]\n" +
" }\n" +
"}")
Page<SpuForElastic> querySearch(String keyword, Pageable pageable);

开发搜索功能的业务逻辑层

首先修改一下原有的业务逻辑层接口返回值的泛型

ISearchService

public interface ISearchService {
// ES分页查询spu的方法
// ↓↓↓↓↓↓↓↓↓↓↓↓↓
JsonPage<SpuForElastic> search(String keyword, Integer page, Integer pageSize)
//......
}

ServiceLocalServiceImpl实现类添加方法

@Override
public JsonPage<SpuForElastic> search(String keyword, Integer page, Integer pageSize) {
// SpringData分页,参数0表示第一页,需要将page-1才能查询正确页码
Page<SpuForElastic> spus=spuRepository.querySearch(
keyword, PageRequest.of(page-1,pageSize));
// 业务逻辑层要求返回JsonPage类型,我们目前最简单的办法就是实例化JsonPage对象给它赋值
JsonPage<SpuForElastic> jsonPage=new JsonPage<>();
// 当前页码
jsonPage.setPage(page);
jsonPage.setPageSize(pageSize);
// 赋值总页数
jsonPage.setTotalPage(spus.getTotalPages());
// 赋值总条数
jsonPage.setTotal(spus.getTotalElements());
// 赋值分页数据
jsonPage.setList(spus.getContent());
// 别忘了返回JsonPage!!!
return jsonPage;
}

开发控制层代码

创建controller包

包中创建搜索方法,代码如下

@RestController
@RequestMapping("/search")
@Api(tags = "搜索模块")
public class SearchController {
@Autowired
private ISearchService searchService;
// 搜索模块最主要的功能就是实现搜索
// 面对这种功能对应的控制器方法,可以不写任何路径
// 按下面@GetMapping注解表示当前控制路径为localhost:10008/search
@GetMapping
@ApiOperation("根据关键字查询ES中的信息")
@ApiImplicitParams({
@ApiImplicitParam(value = "搜索关键字",name = "keyword",dataType = "string",
required = true),
@ApiImplicitParam(value = "页码",name = "page",dataType = "int",
required = true),
@ApiImplicitParam(value = "每页条数",name = "pageSize",dataType = "int",
required = true)
})

public JsonResult<JsonPage<SpuForElastic>> searchByKeyword(
String keyword,
@RequestParam(value = "page",defaultValue = "1")Integer page,
@RequestParam(value = "pageSize",defaultValue = "5") Integer pageSize
){
JsonPage<SpuForElastic> list=searchService.search(keyword,page,pageSize);
return JsonResult.ok(list);
}
}

继续保证Nacos\Redis\ES正在运行

启动seata

依次启动相关服务(因为代码没有对搜索功能放行,所以必须登录才能访问,如果需要生成JWT,还需启动passport)

Leaf\Product\search\passport

按登录流程先访问10002获得jwt

再测试http://localhost:10008/doc.html

全局参数设置完成后测试搜索

学习记录,如有侵权请联系删除

Tags:apiimplicitparam注解用法

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言