springdata+elasticsearch实现搜索引擎

1、前言

配置方面请自行百度,可参考
Windows下搭建ElasticSearch、Logstash以及基本插件

2、搭建springdata项目

注意:这里只给几个重要点的代码,本文最后有网盘链接

(1)pojo

indexName 表示索引名

@Data
@Document(indexName = "goodscat", type = "docs", shards = 1, replicas = 0)
public class Goods {
    @Id
    private Long cid;
    //@Field(type = FieldType.Keyword, index = true, analyzer = "ik_max_word")
    //@Field(index = true,type = FieldType.Text,analyzer="ik_max_word",searchAnalyzer="ik_max_word")
    private String name;
    private String isParent;
    private String parentId;
    private Long level;
    private String pathid;
}
@Data
@Entity(name="wp_ex_source_goods_tb_cat_copy")
public class XcGoods implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cid")
    private Long cid;
    @Column(name = "name")
    private String name;
    @Column(name = "is_parent")
    private String isParent;
    @Column(name = "parent_id")
    private String parentId;
    @Column(name = "level")
    private Long level;
    @Column(name = "pathid")
    private String pathid;
    @Column(name = "path")
    private String path;
}

(2)service

@Slf4j
@Service
public class SearchServiceImpl implements SearchService {


    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchTemplate template;

    @Override
    public Goods buildGoods(XcGoods xcgoods) {
        //搜索字段
//        String all =  xcgoods.getName();

        //构建goods对象
        Goods goods = new Goods();
        goods.setCid(xcgoods.getCid());
        goods.setName(xcgoods.getName());
        goods.setIsParent(xcgoods.getIsParent());
        goods.setParentId(xcgoods.getParentId());
        goods.setPathid(xcgoods.getPathid());
        goods.setLevel(xcgoods.getLevel());
        // 搜索字段,包含标题,分类,品牌,规格,等等
//        goods.setAll(all);

        return goods;
    }


    @Override
    public PageResult<Goods> search(SearchRequest request) {
        int page = request.getPage() - 1;
        int size = request.getSize();

        //创建查询构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"cid", "name"}, null));

        //分页
        queryBuilder.withPageable(PageRequest.of(page, size));
        //过滤
        queryBuilder.withQuery(QueryBuilders.matchQuery("name", request.getKey()));

        //查询
        Page<Goods> result1 = goodsRepository.search(queryBuilder.build());
        AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);

        //解析结果
        //分页结果解析
        long total = result.getTotalElements();
        Integer totalPages1 = result.getTotalPages();    //失效
        Long totalPages = total % size == 0 ? total / size : total / size + 1;
        List<Goods> goodsList = result.getContent();
        System.out.println(goodsList.size());
        //解析聚合结果

        return new SearchResult(total, totalPages, goodsList);
    }

(3)

@RestController
public class SearchController {
    @Autowired
    private SearchService searchService;

    /**
     * 搜索功能
     * @param request
     * @return
     */
    @GetMapping("search")
    public ResponseEntity<PageResult<Goods>> search(SearchRequest request) {
        return ResponseEntity.ok(searchService.search(request));
    }

(4)
运行testCreateIndex()创建索引,注意如果索引名已存在会报错

@SpringBootTest(classes = {SpringdataelasticsearchApplication.class})
public class SearchServiceImplTest {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchTemplate template;

    @Autowired
    private SearchService searchService;


    @Autowired
    private XcGoodsRepository xcGoodsRepository;
//创建索引
    @Test
    public void testCreateIndex() {

        template.createIndex(Goods.class);
        template.putMapping(Goods.class);
    }
//加载数据
    @Test
    public void loadData() {
        int page = 1;
        int rows = 100;
        int size = 0;

        //查询spu信息
        do {
            Page<XcGoods> result = xcGoodsRepository.findAll(PageRequest.of(page - 1, rows));
            List<XcGoods> spuList = result.getContent();
            if (CollectionUtils.isEmpty(spuList)){
                break;
            }
            //构建成goods
            List<Goods> goodsList = spuList.stream()
                    .map(searchService::buildGoods).collect(Collectors.toList());
            //存入索引库
            goodsRepository.saveAll(goodsList);
            //翻页
            page++;
            size = spuList.size();
        } while (size == 100);

    }
}
3、前端使用vue
<template>
  <div>
    <div class="bookstable">
      <el-autocomplete
        v-model="state"
        :fetch-suggestions="querySearchAsync"
        placeholder="请输入内容"
        @select="handleSelect"
        select-when-unmatched="true"
        :debounce="0"
      ></el-autocomplete>
      <el-button slot="append" icon="el-icon-search" @click="onSubmit"></el-button>
      <div class="div2" v-show="con">
        <p>
          搜索
          <span style="color:	#F08080">{{state}}</span>的结果(总共搜索到
          <span style="color:	#F08080">{{total}}</span>条记录)
        </p>
        <p v-for="entity in All" class="p2">
          <a href="http://www.baidu.com">{{entity.name}}</a>
        </p>
        <!--分页组件 -->
        <div class="block">
          <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                         :page-count="totalPage" :page-sizes="[5, 10, 20, 30]" :page-size="20"
                         layout="total, sizes, prev, pager, next, jumper"
                         @prev-click="prePage" @next-click="nextPage" :total="total" :current-page="page">
          </el-pagination>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  const axios = require('axios')
  export default {
    name: 'HelloWorld',
    data () {
      return {
        state: '',
        con: false,
        restaurants: [],
        All: [],
        timeout: null,
        page: 1, //分页默认
        rows: 20, //每页数量
        total: 0,//总页数
        totalPage: 5, //总页数
        pageSizes: {
          default () {
            return [5, 10, 20, 30]
          }
        },
      }
    },
    watch: {//监控一个值的变换
      state: { // 监视字段,页数
        handler () {
          if (this.state.length > 0) {
            this.loadAll()
          } else {
            this.con = false
            this.restaurants = []
            this.All = []
            this.page = 1
          }
        }
      },
      page: { // 监视字段,页数
        handler () {
          this.loadAll()
        }
      }
    }, methods: {//提供存放方法的地方
      nextPage () {
        if (this.page < this.totalPage) this.page++
      },
      prePage () {
        if (this.page > 1) this.page--
      },
      selectPage (i) {
        this.page = i //修改当前页
      },
      handleSizeChange (val) {
        this.rows = val
        console.log(`每页 ${val} 条`)
      },
      handleCurrentChange (val) {
        this.page = val
        console.log(`当前页: ${val}`)
      },
      loadAll (h) {
        var app = this
        var a = h == undefined ? app : h
        axios.get('search', {
          params: {
            'page': this.page,
            'key': this.state,
          }
        })
          .then(response => {
            this.total = response.data.total
            this.totalPage = response.data.totalPages
            var rs = response.data.items
            app.All = rs
            //在这里为这个数组中每一个对象加一个value字段, 因为autocomplete只识别value字段并在下拉列中显示
            for (let i of app.All) {
              i.value = i.name  //将想要展示的数据作为value
            }
            app.restaurants = response.data.items
            console.log('response', app.restaurants)
          })
          .catch(function (error) {
            // handle error
            console.log(error)
          })
      },
      querySearchAsync (queryString, cb) {
        var restaurants = this.restaurants
        var results = queryString ? restaurants.filter(this.createStateFilter(queryString)) : restaurants

        clearTimeout(this.timeout)
        this.timeout = setTimeout(() => {
          cb(results)
        }, 3000 * Math.random())
      },
      createStateFilter (queryString) {
        return (state) => {
          return (state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
        }
      },
      handleSelect (item) {
        this.con = true
        console.log(item)
      },
      onSubmit () {
        this.con = true
      }
    }
  }
</script>
<style>
  .el-autocomplete {
    width: 400px;
  }

  .p2 {
    margin-left: 100px;
    text-align: left;
    font-size: 20px;
    color: blue;
  }

  .div2 {
    /* background: blue; */
    margin-top: 25px;
    padding-top: 25px;
    margin-left: 270px;
    width: 750px;
    height: 650px;
  }
</style>

运行效果如下
在这里插入图片描述
后台代码
链接:https://pan.baidu.com/s/1TT0KX6hB3svvamdMaJwBwA
提取码:e0xq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值