黑马淘淘商城第六天Solrj使用测试、把数据库中的数据导入索引库、搜索功能的实现

    技术2025-07-15  12

    1. 课程计划

    第六天:

    1、Solrj使用测试 2、把数据库中的数据导入索引库 3、搜索功能的实现

    2. 使用solrJ管理索引库

    使用SolrJ可以实现索引库的增删改查操作。

    2.1. 添加文档

    第一步:把solrJ的jar包添加到工程中。 第二步:创建一个SolrServer,使用HttpSolrServer创建对象。 第三步:创建一个文档对象SolrInputDocument对象。 第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。 第五步:把文档添加到索引库中。 第六步:提交。

    @Test public void addDocument() throws Exception { // 第一步:把solrJ的jar包添加到工程中。 // 第二步:创建一个SolrServer,使用HttpSolrServer创建对象。 SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr"); // 第三步:创建一个文档对象SolrInputDocument对象。 SolrInputDocument document = new SolrInputDocument(); // 第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。 document.addField("id", "test001"); document.addField("item_title", "测试商品"); document.addField("item_price", "199"); // 第五步:把文档添加到索引库中。 solrServer.add(document); // 第六步:提交。 solrServer.commit(); }

    2.2. 删除文档

    2.2.1. 根据id删除 第一步:创建一个SolrServer对象。 第二步:调用SolrServer对象的根据id删除的方法。 第三步:提交。

    @Test public void deleteDocumentById() throws Exception { // 第一步:创建一个SolrServer对象。 SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr"); // 第二步:调用SolrServer对象的根据id删除的方法。 solrServer.deleteById("1"); // 第三步:提交。 solrServer.commit(); }

    2.2.2. 根据查询删除

    @Test public void deleteDocumentByQuery() throws Exception { SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr"); solrServer.deleteByQuery("title:change.me"); solrServer.commit(); }

    2.3. 查询索引库

    查询步骤: 第一步:创建一个SolrServer对象 第二步:创建一个SolrQuery对象。 第三步:向SolrQuery中添加查询条件、过滤条件。。。 第四步:执行查询。得到一个Response对象。 第五步:取查询结果。 第六步:遍历结果并打印。 2.3.1. 简单查询

    @Test public void queryDocument() throws Exception { // 第一步:创建一个SolrServer对象 SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr"); // 第二步:创建一个SolrQuery对象。 SolrQuery query = new SolrQuery(); // 第三步:向SolrQuery中添加查询条件、过滤条件。。。 query.setQuery("*:*"); // 第四步:执行查询。得到一个Response对象。 QueryResponse response = solrServer.query(query); // 第五步:取查询结果。 SolrDocumentList solrDocumentList = response.getResults(); System.out.println("查询结果的总记录数:" + solrDocumentList.getNumFound()); // 第六步:遍历结果并打印。 for (SolrDocument solrDocument : solrDocumentList) { System.out.println(solrDocument.get("id")); System.out.println(solrDocument.get("item_title")); System.out.println(solrDocument.get("item_price")); } }

    2.3.2. 带高亮显示

    @Test public void queryDocumentWithHighLighting() throws Exception { // 第一步:创建一个SolrServer对象 SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr"); // 第二步:创建一个SolrQuery对象。 SolrQuery query = new SolrQuery(); // 第三步:向SolrQuery中添加查询条件、过滤条件。。。 query.setQuery("测试"); //指定默认搜索域 query.set("df", "item_keywords"); //开启高亮显示 query.setHighlight(true); //高亮显示的域 query.addHighlightField("item_title"); query.setHighlightSimplePre("<em>"); query.setHighlightSimplePost("</em>"); // 第四步:执行查询。得到一个Response对象。 QueryResponse response = solrServer.query(query); // 第五步:取查询结果。 SolrDocumentList solrDocumentList = response.getResults(); System.out.println("查询结果的总记录数:" + solrDocumentList.getNumFound()); // 第六步:遍历结果并打印。 for (SolrDocument solrDocument : solrDocumentList) { System.out.println(solrDocument.get("id")); //取高亮显示 Map<String, Map<String, List<String>>> highlighting = response.getHighlighting(); List<String> list = highlighting.get(solrDocument.get("id")).get("item_title"); String itemTitle = null; if (list != null && list.size() > 0) { itemTitle = list.get(0); } else { itemTitle = (String) solrDocument.get("item_title"); } System.out.println(itemTitle); System.out.println(solrDocument.get("item_price")); } }

    3. 把商品数据导入到索引库中

    3.1. 功能分析

    schema.xml中定义 1、商品Id 2、商品标题 3、商品卖点 4、商品价格 5、商品图片 6、分类名称 7、商品描述

    需要从tb_item, tb_item_cat, tb_item_desc表中查询数据。 Sql1: SELECT a.id, a.title, a.sell_point, a.price, a.image, b. NAME category_name, c.item_desc FROM tb_item a, tb_item_cat b, tb_item_desc c WHERE a.cid = b.id AND a.id = c.item_id AND a.status = 1;

    Sql2: SELECT a.id, a.title, a.sell_point, a.price, a.image, b. NAME category_name, c.item_desc FROM tb_item a JOIN tb_item_cat b ON a.cid = b.id JOIN tb_item_desc c ON a.id = c.item_id WHERE a.status = 1

    参数:无 业务逻辑:taotao-search中实现 1、查询所有商品数据。 2、创建一个SolrServer对象。 3、为每个商品创建一个SolrInputDocument对象。 4、为文档添加域 5、向索引库中添加文档。 6、返回TaotaoResult。

    在taotao-manager-web中调用服务。实现数据导入功能。

    3.2. Dao层

    返回一个pojo对应查询的sql语句。

    public class SearchItem implements Serializable{ private String id; private String title; private String sell_point; private long price; private String image; private String category_name; private String item_desc; }

    放到taotao-common中。

    接口的返回值:List

    接口定义:

    public interface SearchItemMapper { List<SearchItem> getItemList(); }

    Mapper映射文件:

    <?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.taotao.search.mapper.SearchItemMapper" > <select id="getItemList" resultType="com.taotao.common.pojo.SearchItem"> SELECT a.id, a.title, a.sell_point, a.price, a.image, b. NAME category_name, c.item_desc FROM tb_item a JOIN tb_item_cat b ON a.cid = b.id JOIN tb_item_desc c ON a.id = c.item_id WHERE a.status = 1 </select> </mapper>

    3.3. Service层

    参数:无 业务逻辑:taotao-search中实现 1、查询所有商品数据。 2、创建一个SolrServer对象。 3、为每个商品创建一个SolrInputDocument对象。 4、为文档添加域 5、向索引库中添加文档。 6、返回TaotaoResult。 在 applicationContext-solr.xml中配置SolrServer对象。

    @Service public class SearchItemServiceImpl implements SearchItemService { @Autowired private SearchItemMapper searchItemMapper; @Autowired private SolrServer solrServer; @Override public TaotaoResult importAllItems() throws Exception{ // 1、查询所有商品数据。 List<SearchItem> itemList = searchItemMapper.getItemList(); // 2、创建一个SolrServer对象。 // 3、为每个商品创建一个SolrInputDocument对象。 for (SearchItem searchItem : itemList) { SolrInputDocument document = new SolrInputDocument(); // 4、为文档添加域 document.addField("id", searchItem.getId()); document.addField("item_title", searchItem.getTitle()); document.addField("item_sell_point", searchItem.getSell_point()); document.addField("item_price", searchItem.getPrice()); document.addField("item_image", searchItem.getImage()); document.addField("item_category_name", searchItem.getCategory_name()); document.addField("item_desc", searchItem.getItem_desc()); // 5、向索引库中添加文档。 solrServer.add(document); } //提交修改 solrServer.commit(); // 6、返回TaotaoResult。 return TaotaoResult.ok(); } }

    3.3.1. 发布服务

    3.4. 表现层

    3.4.1. 引用服务

    3.4.2. Controller

    请求的url:/index/importall 参数:无 返回值:json数据。TaotaoResult。

    @Controller public class SearchItemController { @Autowired private SearchItemService searchItemService; @RequestMapping("/index/importall") @ResponseBody public TaotaoResult importAllItems() { try { TaotaoResult result = searchItemService.importAllItems(); return result; } catch (Exception e) { e.printStackTrace(); return TaotaoResult.build(500, "导入数据失败"); } } }

    3.4.3. Jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div> <a href="javascript:void(0)" class="easyui-linkbutton" onclick="importAll()">一键导入商品数据到索引库</a> </div> <script type="text/javascript"> function importAll() { $.post("/index/importall",null,function(data){ if (data.status==200) { $.messager.alert('提示','商品数据导入成功!'); } else { $.messager.alert('提示','商品数据导入失败!'); } }); } </script>

    index-mangager taotao-search-service工程的pom文件中添加如下配置:

    <!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 --> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <!-- 如果没有此节点,src/main/resources目录下的配置文件将被忽略 --> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>

    4. 商品搜索功能实现

    4.1. 搜索表现层工程搭建

    搜索结果页面的展示。 可以参考taotao-manager-web创建。 打包方式war。 Taotao-search-web

    4.1.1. Pom文件

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.taotao</groupId> <artifactId>taotao-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.taotao</groupId> <artifactId>taotao-search-web</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.taotao</groupId> <artifactId>taotao-search-interface</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- JSP相关 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> </dependency> <!-- dubbo相关 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <!-- 排除依赖 --> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> <exclusion> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </dependency> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies> <!-- 配置tomcat插件 --> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8085</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>

    4.1.2. 框架整合

    4.2. 搜索功能分析

    (1)在taotao-potal-web工程index.jsp页面中动态包含了head.jsp,而该页面有一处是搜索功能的: 加载head.jsp页面的时候会加载base-v1.js,修改该js 修改端口号,使之在搜索的时候会跳转到指定的另外一个搜索工程中search.html页面上。 (2)在taotao-search-web工程中添加静态页面 在首页的搜索框中输入搜索条件,然后跳转到搜索结果页面。搜索结果页面在taotao-search-web工程中。 请求的url:/search 参数: 1、q 查询条件。 2、page 页码。默认为1 返回值: 逻辑视图,返回值。String。

    业务逻辑: 1、接收查询条件。 2、创建一个SolrServer对象,需要注入。 3、创建一个SolrQuery对象。 4、需要设置查询条件、分页条件、设置默认搜索域、高亮设置。 5、执行查询,返回QueryResponse对象。 6、取返回结果,封装到List中。 7、返回查询结果的总记录数,计算查询结果的总页数。 8、得到查询结果,渲染jsp

    4.3. Dao层 访问索引库的类。定义一些通用的数据访问方法。 业务逻辑就是查询索引库。 参数:SolrQuery对象 业务逻辑: 1、根据Query对象进行查询。 2、返回查询结果。List、查询结果的总记录数。 需要把返回结果封装到pojo中,至少包含两个属性:List、Long recordCount 再包含一个总页数。

    public class SearchResult implements Serializable { private List<SearchItem> itemList; private long recordCount; private long pageCount; }

    返回值:SearchResult

    @Repository public class SearchDao { @Autowired private SolrServer solrServer; public SearchResult search(SolrQuery query) throws Exception { //根据query对象查询索引库 QueryResponse response = solrServer.query(query); //取商品列表 SolrDocumentList solrDocumentList = response.getResults(); //商品列表 List<SearchItem> itemList = new ArrayList<>(); for (SolrDocument solrDocument : solrDocumentList) { SearchItem item = new SearchItem(); item.setId((String) solrDocument.get("id")); item.setCategory_name((String) solrDocument.get("item_category_name")); item.setImage((String) solrDocument.get("item_image")); item.setPrice((long) solrDocument.get("item_price")); item.setSell_point((String) solrDocument.get("item_sell_point")); //取高亮显示 Map<String, Map<String, List<String>>> highlighting = response.getHighlighting(); List<String> list = highlighting.get(solrDocument.get("id")).get("item_title"); String itemTitle = ""; //有高亮显示的内容时。 if (list != null && list.size() > 0) { itemTitle = list.get(0); } else { itemTitle = (String) solrDocument.get("item_title"); } item.setTitle(itemTitle); //添加到商品列表 itemList.add(item); } SearchResult result = new SearchResult(); //商品列表 result.setItemList(itemList); //总记录数 result.setRecordCount(solrDocumentList.getNumFound()); return result; } }

    4.4. Service层

    参数:queryString:查询条件 Page:页码 Rows:每页显示的记录数。 业务逻辑: 1、创建一个SolrQuery对象。 2、设置查询条件 3、设置分页条件 4、需要指定默认搜索域。 5、设置高亮 6、执行查询,调用SearchDao。得到SearchResult 7、需要计算总页数。 8、返回SearchResult 返回值:SearchResult

    @Service public class SearchServiceImpl implements SearchService { @Autowired private SearchDao searchDao; @Override public SearchResult search(String queryString, int page, int rows) throws Exception { // 1、创建一个SolrQuery对象。 SolrQuery query = new SolrQuery(); // 2、设置查询条件 query.setQuery(queryString); // 3、设置分页条件 query.setStart((page - 1) * rows); query.setRows(rows); // 4、需要指定默认搜索域。 query.set("df", "item_title"); // 5、设置高亮 query.setHighlight(true); query.addHighlightField("item_title"); query.setHighlightSimplePre("<em style=\"color:red\">"); query.setHighlightSimplePost("</em>"); // 6、执行查询,调用SearchDao。得到SearchResult SearchResult result = searchDao.search(query); // 7、需要计算总页数。 long recordCount = result.getRecordCount(); long pageCount = recordCount / rows; if (recordCount % rows > 0) { pageCount++; } result.setPageCount(pageCount); // 8、返回SearchResult return result; } }

    4.4.1. 发布服务

    4.5. 表现层

    需要taotao-search-web中实现。引用服务 请求的url:/search 参数: 1、q 查询条件。 2、page 页码。默认为1 返回值: 逻辑视图,返回值。String。

    业务逻辑: 1、接收参数 2、调用服务查询商品列表 3、把查询结果传递给页面。需要参数回显。

    @Controller public class SearchController { @Value("${ITEM_ROWS}") private Integer ITEM_ROWS; @Autowired private SearchService searchService; @RequestMapping("/search") public String search(@RequestParam("q")String queryString, @RequestParam(defaultValue="1")Integer page, Model model) throws Exception { SearchResult result = searchService.search(queryString, page, ITEM_ROWS); //传递给页面 model.addAttribute("query", queryString); model.addAttribute("totalPages", result.getPageCount()); model.addAttribute("itemList", result.getItemList()); model.addAttribute("page", page); //返回逻辑视图 return "search"; } }

    需要对请求参数进行转码处理: 翻页处理: 4.6. 图片显示处理 数据库中保存的图片是以逗号分隔的url列表,只需要展示第一张图片即可。 方法: 1、向索引库中添加文档时,只取第一张写入索引库 2、从文档列表转换为商品列表时可以取一张。 3、在jsp中对列表拆分,只取一张展示。

    可以在SearchItem中添加一个getImages方法:

    5. 全局异常处理

    5.1. 处理思路

    5.2. 创建全局异常处理器

    public class GlobalExceptionReslover implements HandlerExceptionResolver { Logger logger = LoggerFactory.getLogger(GlobalExceptionReslover.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //写日志文件 logger.error("系统发生异常", ex); //发邮件、发短信 //Jmail:可以查找相关的资料 //需要在购买短信。调用第三方接口即可。 //展示错误页面 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", "系统发生异常,请稍后重试"); modelAndView.setViewName("error/exception"); return modelAndView; } }

    5.3. Springmvc中配置异常处理器

    Processed: 0.010, SQL: 9