ElasticSearch完结篇------京东搜索实战

实战

项目搭建

controller层

/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/5 13:03
 * @Description TODO
 * @pojectname wang-es-jd
 */
@Controller
public class IndexController {
    @GetMapping({"/","/index"})
    public String index(){
        return "index";
    }
}
server.port=9090
#关闭thymeleaf缓存
spring.thymeleaf.cache=false

还有我们的自定义版本,要与本地ES版本一致

其他的css,index页面又狂神说UP提供,有兴趣的可以去搜一下

启动项目

在这里插入图片描述

数据获取

数据库获取,消息队列中获取,都可以成为数据源,我们在这里用了一个简单的爬虫,不违法的哦

爬取数据

获取请求返回的页面信息,筛选出我们想要的数据====>Jsoup包

<!--        解析网页-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.10.2</version>
        </dependency>

爬取数据代码

public class HtmlParseUtil {
    public static void main(String[] args) throws IOException {
        new HtmlParseUtil().parseHtml("Java").forEach(System.out::println);
    }
    public List<Content> parseHtml(String keywords) throws IOException {
        //获取请求  https://search.jd.com/Search?keyword=java
        //要联网,而且不能获取到ajax数据
        String url ="https://search.jd.com/Search?keyword="+keywords;
        //解析网页 返回的document就是浏览器的document对象
        Document document = Jsoup.parse(new URL(url), 3000);
        //所有js的方法都可以使用
        //获取商品列表外层的大的div    J_goodsList
        Element element = document.getElementById("J_goodsList");
        //System.out.println(element.html());
        //获取全部的li元素
        Elements elements = element.getElementsByTag("li");
        ArrayList<Content> contents = new ArrayList<>();
        //获取元素中的内容,el就是每一个li标签
        for (Element el : elements) {
            //图片地址
            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
            //价格
            String price = el.getElementsByClass("p-price").eq(0).text();
            //标题
            String title = el.getElementsByClass("p-name").eq(0).text();
            Content content = new Content();
            content.setImg(img);
            content.setPrice(price);
            content.setTitle(title);
            contents.add(content);

        }
        return contents;
    }
}

运行会发现我们的img地址获取不到,因为图片特别多 的网站,他们所有的图片都是延迟加载的方式data-lazy-img

根据我们的工具类,写出我们自己的实体类封装

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Content {
    private String title;
    private String img;
    private String price;
    //可以自己添加属性
}

下面我们开始准备我们的业务实现

首先是我们的配置类,配置我们的ES

/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/4 19:48
 * @Description TODO
 * @pojectname es
 */
@Configuration
public class ElasticSearchClientConfig {

    @Bean
    RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1",9200,"http")));
        return client;
    }
}

接下来是service层,实现我们的业务层

//业务编写
@Service
public class ContentService {

    @Autowired
    RestHighLevelClient restHighLevelClient;
    //解析数据,放入es索引
    public Boolean parseContent(String keywords) throws IOException {
        List<Content> contents = new HtmlParseUtil().parseHtml(keywords);
        //批量插入到es中
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");
        for (int i = 0; i < contents.size(); i++) {
            System.out.println(JSON.toJSONString(contents.get(i)));
            bulkRequest.add(
                    new IndexRequest("jd_goods")
                .source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulk.hasFailures();
    }
    //获取这些数据
    //实现搜索功能
    public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize) throws IOException {
        if (pageNo<1){
            pageNo = 1;
        }
        //条件搜索
        SearchRequest searchRequest = new SearchRequest("jd_goods");
        //构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //实现分页功能
        searchSourceBuilder.from(pageNo);
        searchSourceBuilder.size(pageSize);
        //精确匹配
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title",keyword);
        searchSourceBuilder.query(termQueryBuilder);
        //60s加载时间
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        ArrayList<Map<String,Object>> list = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            list.add(documentFields.getSourceAsMap());
        }
        return list;
    }
}

这里有两个功能,一个是一个是向 ES中增加数据,一个是ES中取出数据对应的他的controller也会有两个get请求

编写我们的ContentController

//请求编写
@RestController
public class ContentController {
    @Autowired
    private ContentService contentService;

    @GetMapping("/parse/{keyword}")
    public Boolean parse(@PathVariable("keyword") String keyword) throws IOException {
        return contentService.parseContent(keyword);
    }
    @GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
    public List<Map<String ,Object>> search(@PathVariable("keyword") String keyword,
                                            @PathVariable("pageNo") int pageNo,
                                            @PathVariable("pageSize") int pageSize) throws IOException {
        return contentService.searchPage(keyword, pageNo, pageSize);
    }
}

我在前面测试的时候已经向ES数据库插入了一些关于java的数据,vue的数据,还有医学的数据

在这里插入图片描述

成功拿到我们的数据

如果我们查询的有中文的话,我们是要去规范编码的,否则是查不出来的

绑定前端

<script th:src="@{/js/axios.min.js}"></script>
<script th:src="@{/js/vue.min.js}"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            keyword:'' ,//搜索的关键字
            results:[] //搜索的结果
        },
        methods:{
            searchKey(){
                var keyword = this.keyword;
                console.log(keyword);
                //对接后端接口
                axios.get('search/'+keyword+"/1/10").then(response=>{
                    console.log(response);
                    //绑定数据
                    this.results = response.data;
                })
            }
        }
    })
    在我们相应的地方  搜索框和按钮绑定数据和事件
</script>

搜索高亮功能

//业务编写
@Service
public class ContentService {

    @Autowired
    RestHighLevelClient restHighLevelClient;
    //解析数据,放入es索引
    public Boolean parseContent(String keywords) throws IOException {
        List<Content> contents = new HtmlParseUtil().parseHtml(keywords);
        //批量插入到es中
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");
        for (int i = 0; i < contents.size(); i++) {
            System.out.println(JSON.toJSONString(contents.get(i)));
            bulkRequest.add(
                    new IndexRequest("jd_goods")
                .source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulk.hasFailures();
    }
    //获取这些数据
    //实现搜索功能
    public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize) throws IOException {
        if (pageNo<1){
            pageNo = 1;
        }
        //条件搜索
        SearchRequest searchRequest = new SearchRequest("jd_goods");
        //构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //实现分页功能
        searchSourceBuilder.from(pageNo);
        searchSourceBuilder.size(pageSize);
        //精确匹配
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title",keyword);
        searchSourceBuilder.query(termQueryBuilder);
        //60s加载时间
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        //这里可以配置多个字段,信息等等
        //关闭多个高亮
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        ArrayList<Map<String,Object>> list = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            //解析我们的高亮字段
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            //原来的结果
            Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
            if (documentFields != null)
            {
                Text[] fragments = title.fragments();
                String n_title = "";
                //将高亮替换原来的字段
                for (Text text : fragments) {
                    n_title += text;
                }
                sourceAsMap.put("title",n_title);
            }
            list.add(sourceAsMap);
        }
        return list;
    }
}

区别就是我们现实解析了需要高亮的地方,然后是有高亮格式包裹,最后一点改变是在我们封装结果回去的时候,再次解析高亮字段,但是我们跑起来发现一个问题

在这里插入图片描述

我们还要解析这个标签,vue就能帮助我们了

<p class="productTitle">
    <a v-html="result.title">  </a>
</p>

在这里插入图片描述

完成收工

不过对于中文的爬取可以成功,但是搜索的时候会被编码所打乱,搜索不到,我们就得对关键字进行编码处理,后续我们研究一下,有了解的人看到了可以交流一下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会写代码的花城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值