校园课程助手【4】-使用Elasticsearch实现课程检索

本节将介绍本项目的查询模块,使用Elasticsearch又不是查询接口,具体流程如图所示(如果不了解Elasticsearch可以使用sql语句进行查询):

这里是两种方法的异同点:

  • Mysql:擅长事务类型操作,可以确保数据的安全和一致性
  • Elasticsearch:擅长海量数据的搜索、分析、计算
  • 对安全性要求较高的写操作,使用mysql实现
  • 对查询性能要求较高的搜索需求,使用elasticsearch实现
  • 两者再基于某种方式,实现数据的同步,保证一致性

具体流程:

1.导入依赖

java 复制代码
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

2.在application.yum引入配置

java 复制代码
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

3.在项目中建立elasticsearch.document和elasticsearch.repository包,用于存放elasticsearch文档类和接口操作

java 复制代码
package com.java.elasticsearch.document;

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.math.BigDecimal;

@Document(indexName = "course")
public class CourseInfo {

    @Id
    private String courseId;

    @Field(type = FieldType.Text, analyzer = "standard") // 使用标准分析器
    private String courseName;

    @Field(type = FieldType.Text, analyzer = "standard") // 使用标准分析器
    private String courseTeacher;

    @Field(type = FieldType.Text)
    private String courseDetail;

    @Field(type = FieldType.Integer)
    private Integer courseAttribute;

    @Field(type = FieldType.Double)
    private BigDecimal coursePrice;

    @Field(type = FieldType.Integer)
    private Integer courseStock;

    // 构造函数、getter 和 setter 方法
    
    // Getters and setters for each field
    public String getCourseId() {
        return courseId;
    }

    public void setCourseId(String courseId) {
        this.courseId = courseId;
    }

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public String getCourseTeacher() {
        return courseTeacher;
    }

    public void setCourseTeacher(String courseTeacher) {
        this.courseTeacher = courseTeacher;
    }

    public String getCourseDetail() {
        return courseDetail;
    }

    public void setCourseDetail(String courseDetail) {
        this.courseDetail = courseDetail;
    }

    public Integer getCourseAttribute() {
        return courseAttribute;
    }

    public void setCourseAttribute(Integer courseAttribute) {
        this.courseAttribute = courseAttribute;
    }

    public BigDecimal getCoursePrice() {
        return coursePrice;
    }

    public void setCoursePrice(BigDecimal coursePrice) {
        this.coursePrice = coursePrice;
    }

    public Integer getCourseStock() {
        return courseStock;
    }

    public void setCourseStock(Integer courseStock) {
        this.courseStock = courseStock;
    }
}

4、在repository包下新建操作Elasticsearch的接口继承ElasticsearchRepository

java 复制代码
package com.java.elasticsearch.repository;

import com.java.elasticsearch.document.CourseInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface CourseRepository extends ElasticsearchRepository<CourseInfo, String> {

    // 按课程名查询
    Page<CourseInfo> findByCourseName(String courseName, int courseAttribute, Pageable pageable);

    // 按授课老师名查询
    Page<CourseInfo> findByCourseTeacher(String courseTeacher, int courseAttribute,Pageable pageable);

    // 查询课程名为courseName且授课老师为courseTeacher的记录
    @Query("{\"bool\": {\"must\": [{\"match\": {\"courseName\": \"?0\"}}, {\"match\": {\"courseTeacher\": \"?1\"}}]}}")
    Page<CourseInfo> findByCourseNameAndCourseTeacher(String courseName, String courseTeacher, Pageable pageable);
}

5.在service包下新建Elasticsearch课程搜索Service类EsCourseService

java 复制代码
package com.java.service;

import com.java.elasticsearch.document.EsCourse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface EsCourseService {
    /**
     * 从数据库中导入课程到ES
     * @return
     */
    int importAll();

    /**
     * 根据id删除课程
     * @param id
     */
    void delete(Long id);

    /**
     * 根据id创建商品
     * @param id
     * @return
     */
    EsProduct create(Long id);

    /**
     * 批量删除
     * @param ids
     */
    void deletes(List<Long> ids);

    /**
     * 根据关键字搜索
     * @param keyword
     * @param pageNum
     * @param pageSize
     * @return
     */
    Page<EsProduct> searchPage(String keyword, Integer pageNum,Integer pageSize);
}

实现上述方法

java 复制代码
package com.java.service.impl;

import com.java.dao.EsProductDao;
import com.java.elasticsearch.document.EsProduct;
import com.java.elasticsearch.repository.EsProductRepository;
import com.java.service.EsProductService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Service
@Transactional
public class EsProductServiceImpl implements EsProductService {
    private static final Logger logger = LoggerFactory.getLogger(EsProductServiceImpl.class);
    @Autowired
    private EsProductDao esProductDao;
    @Autowired
    private EsProductRepository esProductRepository;
    @Override
    public int importAll() {
        List<EsProduct> esProductList = esProductDao.getProductEs(null);
        Iterable<EsProduct> iterable = esProductRepository.saveAll(esProductList);
        Iterator<EsProduct> iterator = iterable.iterator();
        logger.info("导入ES数据{}:",iterator);
        int count = 0;
        while (iterator.hasNext()) {
            count++;
            iterator.next();
        }
        return count;
    }

    @Override
    public void delete(Long id) {
        logger.info("删除ES中的商品{}:",id);
        esProductRepository.deleteById(id);
    }

    @Override
    public EsProduct create(Long id) {
        List<EsProduct> esProducts = esProductDao.getProductEs(id);
        if (CollectionUtils.isEmpty(esProducts)) {
            return null;
        }
        EsProduct esProduct = esProducts.get(0);
        logger.info("导入ES单条商品{}:",esProduct);
        return esProductRepository.save(esProduct);
    }

    @Override
    public void deletes(List<Long> ids) {
        if (!CollectionUtils.isEmpty(ids)) {
            List<EsProduct> esProductList = new ArrayList<>();
            ids.forEach(id->{
                EsProduct esProduct = new EsProduct();
                esProduct.setId(id);
                esProductList.add(esProduct);
            });
            logger.info("批量删除ES中的商品{}:",esProductList);
            esProductRepository.deleteAll(esProductList);
        }
    }

    @Override
     // 搜索课程
    public Page<CourseInfo> searchCourses(String query, Integer courseAttribute, Pageable pageable) {
        if (query != null && !query.isEmpty()) {
            return courseRepository.findByCourseNameOrCourseTeacher(query, query, pageable);
        } else {
            // 如果没有搜索词,则返回所有符合条件的课程
            return courseRepository.findByCourseAttributeAndCourseStockGreaterThan(courseAttribute, pageable);
        }
    }
}

6.在dao包下新建操作数据库接口EsProductDao和映射xml文件EsProductDao.xml

java 复制代码
package com.java.dao;
import java.util.List;

public interface EsCourseDao {
    List<EsProduct> selectAllCourse();
}
java 复制代码
<!-- src/main/resources/mapper/EsCourseDao.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java.dao.EsProductDao">

    <resultMap id="CourseResultMap" type="EsCourse">
        <result property="course_id" column="course_id"/>
        <result property="course_name" column="course_name"/>
        <result property="course_teacher" column="course_teacher"/>
        <result property="course_attribute" column="course_attribute"/>
		<result property="course_stock" column="course_stock"/>
    </resultMap>

    <!-- 查询所有课程 -->
    <select id="selectAllCourse" resultMap="CourseResultMap">
        SELECT course_id,course_name,course_teacher,course_attribute ,course_stock
        FROM course
    </select>

</mapper>

7.在controller包下新建控制器EsProductController

java 复制代码
/**
 * ES搜索课程Controller
 **/
@Controller
@Api(tags = "EsProductController",description = "ES课程搜索")
public class EsProductController {
    @Autowired
    private EsProductService esProductService;

    @ApiOperation("从数据库导入ES课程数据")
    @RequestMapping(value = "/esProduct/importAll",method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<Integer> importAll(){
        int count = esProductService.importAll();
        return CommonResult.success(count);
    }

    @ApiOperation("根据id删除课程")
    @RequestMapping(value = "/esProduct/delete/{id}",method = RequestMethod.POST)
    @ResponseBody
    public CommonResult deleteById(@PathVariable Long id){
        esProductService.delete(id);
        return RespBean.success("删除成功");
    }

    @ApiOperation("批量删除课程")
    @RequestMapping(value = "/esProduct/deletes",method = RequestMethod.POST)
    @ResponseBody
    public CommonResult deleteById(List<Long> ids){
        esProductService.deletes(ids);
        return RespBean.success("删除成功");
    }

    @ApiOperation("根据id创建课程")
    @RequestMapping(value = "/esProduct/create",method = RequestMethod.POST)
    @ResponseBody
    public CommonResult create(Long id){
        EsProduct esProduct = esProductService.create(id);
        if (StringUtils.isEmpty(esProduct)) {
            return CommonResult.failed("创建失败");
        }
        return RespBean.success("创建成功");
    }

    @ApiOperation("搜索课程")
    @RequestMapping(value = "/esProduct/search",method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<CommonPage<EsProduct>> search(@RequestParam(required = false) String keyword,
                                                      @RequestParam(required = false, defaultValue = "0") Integer pageNum,
                                                      @RequestParam(required = false, defaultValue = "5") Integer pageSize){
        Page<EsProduct> esProductPage = esProductService.searchPage(keyword,pageNum,pageSize);
        return RespBean.success(CommonPage.restPage(esProductPage));
    }
}
相关推荐
柔弱女子爱java5 分钟前
XML文件(超详细):XML文件概念、作用、写法、如何用程序解析XML、写入XML、dom4j框架、DTD文档、schema文档
xml·java·开发语言·后端
wsd_ontheroad7 分钟前
HTML 转 PDF
java·pdf·html
xmh-sxh-131424 分钟前
redis使用介绍
java
chusheng184026 分钟前
Java 基于SpringBoot+Vue的家政服务管理平台
java·vue.js·spring boot·家政服务·家政服务平台
调皮的木木40 分钟前
Mysql的加锁情况详解
java·数据库·mysql
吹老师个人app编程教学1 小时前
集合Queue、Deque、LinkedList、ArrayDeque、PriorityQueue详解
java
幼儿园里的山大王1 小时前
springboot系列--拦截器执行原理
java·spring boot·后端
扬子鳄0081 小时前
Spring集成测试
java·spring·集成测试
小白不太白9502 小时前
设计模式之 解释器模式
java·设计模式·解释器模式
江-小北2 小时前
Java基础面试题05:简述快速失败(fail-fast)和安全失败(fail-safe)的区别 ?
java·开发语言·python