为了账号安全,请及时绑定邮箱和手机立即绑定

day73_淘淘商城项目_06_solr索引库搭建 + solr搜索功能实现 + 图片显示等问题解决二

标签:
深度学习

3.4.3、修改index.jsp

由于把从数据库中查询到的新的商品数据导入到索引库中属于后台功能,所以我们在taotao-manager-web的后台系统中做一个导入索引库的功能界面。(例如:有个按钮,点击即可将从数据库中查询到的数据导入到索引库)。
业务逻辑:

    1、点击按钮,表现层调用服务层的工程的导入索引库的方法。
    2、服务层实现调用Mapper接口的方法查询所有的商品的数据。
    3、将数据一条条添加到SolrInputDocument文档中。
    4、将文档添加到索引库中。
    5、提交,并返回导入成功即可。

添加如下代码到index.jsp中:

    <li>
         <span>网站前台搜索管理</span>
         <ul>
             <li data-options="attributes:{'url':'import-index'}">导入索引库</li>
         </ul>
     </li>

3.4.4、创建一个jsp

在taotao-manager-web中创建一个import-index.jsp:
中间做了一点效果,用户点击按钮后,按钮不可再点击,直至导入索引库成功之后才可以再点击,即导入索引库成功后还原按钮状态:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<div>
    <a href="javascript:void(0)" class="easyui-linkbutton" ="importAll()">一键导入商品数据到索引库</a>
</div>
<script type="text/javascript">
function importAll() {
    // 移除原有按钮
    $('.l-btn-text').remove("span");
    // 追加不可点击按钮
    $('.l-btn-left').after('<input type="button" disabled="true" id="disabledButton" value="正在导入商品搜索索引库,请稍后......">');
    $.post("/index/importAll",null,function(data) {
        // 移除不可点击按钮
        $('#disabledButton').remove("input");
        // 追加原有按钮
        $('.l-btn-left').after('<span class="l-btn-text">一键导入商品数据到索引库</span>');
        if (data.status==200) {
            $.messager.alert('提示','商品数据导入索引库成功!');
        } else {
            $.messager.alert('提示','商品数据导入索引库失败!');
        }
    }); 
}
</script>

3.4.5、Controller

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

/**
 * 索引库维护Controller
 * @author    chenmingjun
 * @date    2018年11月21日下午4:02:43
 * @version 1.0
 */
@Controller
public class SearchItemController {

    @Autowired
    private SearchItemService searchItemService;

    @RequestMapping("/index/importAll")
    @ResponseBody
    public TaotaoResult importSearchItemsToIndex() {
        try {
            TaotaoResult result = searchItemService.importSearchItemsToIndex();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return TaotaoResult.build(500, "商品数据一键导入索引库失败!");
        }
    }
}

3.4.6、测试

测试之前我们先将索引库中的测试数据清空,否则会对我们测试有影响,方法如下:

https://img1.sycdn.imooc.com//5bf67b180001bef412630721.jpg

测试出错,原因是:未将工程中新建的映射文件SearchItemMapper.xml发布到classpath中。错误截图如下:
https://img1.sycdn.imooc.com//5bf67b2200010ed515270264.jpg
由于maven发布代码和配置文件时,在src/main/java下,maven只会将*.java文件编译成*.class文件发布到classpath下,对于*.xml文件等,maven是不会理会的,所以我们需要配置maven扫描src/main/java下的*.xml文件。但是又不能影响maven扫描src/main/resources下面的*.xml、*.properties文件,所以我们需要在taotao-search-service中这样配置:

    <build>
        <!-- 如果不添加此节点,mybatis的mapper.xml文件都会被漏掉 -->
        <!-- 注意:配置了此方式,原来的默认的资源拷贝行为将无效了 -->
        <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>

浏览器页面展示结果:

https://img1.sycdn.imooc.com//5bf67b2b000189fb13700967.jpg

回到顶部

4、商品搜索功能实现(前台功能)

4.1、搜索表现层工程的搭建

可以参考taotao-portal-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>
    <artifactId>taotao-search-web</artifactId>
    <packaging>war</packaging>
    <dependencies>
        <!-- 配置对common的依赖 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- 配置对taotao-search-interface的依赖:表现层调用服务要通过该接口 -->
        <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的依赖 -->
        <!-- dubbo相关 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <!-- 排除对低版本jar包的依赖 -->
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>netty</artifactId>
                    <groupId>org.jboss.netty</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 配置Tomcat插件  -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8086</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.1.2、配置文件

目录结构如下:

https://img1.sycdn.imooc.com//5bf67b360001840c04230423.jpg

4.1.3、springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
    http://code.alibabatech.com/schema/dubbo 
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <!-- 配置加载属性文件 -->
    <context:property-placeholder location="classpath:resource/resource.properties"/>

    <!-- 配置包扫描器,扫描所有需要带@Controller注解的类 -->
    <context:component-scan base-package="com.taotao.search.controller" />

    <!-- 配置注解驱动 -->
    <mvc:annotation-driven />
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 引用dubbo服务 :需要先引入dubbo的约束-->
    <dubbo:application name="taotao-search-web"/>
    <dubbo:registry protocol="zookeeper" address="192.168.25.128:2181"/>    
    <!-- <dubbo:reference interface="com.taotao.content.service.ContentService" id="contentService" /> -->
</beans>

4.1.4、web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>taotao-search-web</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <!-- 配置解决post乱码的过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 配置springmvc的前端控制器 -->
    <servlet>
        <servlet-name>taotao-search-web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, 
             springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>taotao-search-web</servlet-name>
        <!-- 拦截(*.html)结尾的请求,实现了网页的伪静态化,SEO:搜索引擎优化-->
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

4.1.5、搜索结果的前台静态页面

https://img1.sycdn.imooc.com//5bf67b400001b3b205810172.jpg
将上面的搜索结果静态页面放入到taotao-search-web工程中
https://img1.sycdn.imooc.com//5bf67b470001969002510385.jpg

4.2、搜索功能分析

首页的搜索框中输入搜索条件,然后跳转到搜索结果页面。搜索结果页面在taotao-search-web工程中(端口为8086)。
首页搜索框的点击按钮处理函数在:在taotao-portal-web工程首页的JS中,应当改成8086,如图:

https://img1.sycdn.imooc.com//5bf67b5000011f2410290217.jpg
请求的url:/search    
  以搜索手机为例:http://localhost:8086/search.html?q=手机
https://img1.sycdn.imooc.com//5bf67b570001a42306060211.jpg
参数:
  1、q,表示查询条件。
  2、page,页码。默认为1。每页显示多少行,我们在controller中写死即可。比如:60。
返回值: String。(包括:新的商品列表信息、总页数、总记录数、数据回显)
业务逻辑:
  1、接收查询条件。
  2、创建一个SolrServer对象,需要注入。
  3、创建一个SolrQuery对象。
  4、需要设置查询条件、分页条件、设置默认搜索域、高亮设置。
  5、执行查询,返回QueryResponse对象。
  6、取返回结果,封装到List中。
  7、返回查询结果的总记录数,计算查询结果的总页数。
  8、得到查询结果,渲染jsp

4.3、Dao层

4.3.1、功能分析

访问索引库的类。定义一些通用的数据访问方法。
业务逻辑就是查询索引库。
参数:SolrQuery对象
业务逻辑:
  1、根据Query对象进行查询。
  2、返回查询结果。包括List<SearchItem>、查询结果的总记录数。
需要把返回结果封装到pojo中,至少包含两个属性:List<SearchItem>(搜索的结果列表)Long recordCount(总记录数)
再包含一个总页数(Long pageCount)。
创建如下SearchResult对象,放入taotao-common中。
SearchResult.java

package com.taotao.common.pojo;

import java.io.Serializable;
import java.util.List;

/**
 * 商品搜索的分页信息结果对象
 * @author chenmingjun
 * @date 2018年11月21日下午11:33:14
 * @version 1.0
 */
public class SearchResult implements Serializable {

    private static final long serialVersionUID = 1L;

    private List<SearchItem> itemList; // 搜索的结果列表(列表中放的是新的商品数据!不是之前的Item)
    private Long recordCount; // 总记录数
    private Long pageCount; // 总页数

    public List<SearchItem> getItemList() {
        return itemList;
    }

    public void setItemList(List<SearchItem> itemList) {
        this.itemList = itemList;
    }

    public Long getRecordCount() {
        return recordCount;
    }

    public void setRecordCount(Long recordCount) {
        this.recordCount = recordCount;
    }

    public Long getPageCount() {
        return pageCount;
    }

    public void setPageCount(Long pageCount) {
        this.pageCount = pageCount;
    }
}

4.3.2、创建SearchDaoImpl

在Dao层,查询索引库的方式和处理查询结果的方式都是一样的,查询索引库的参数是SolrQuery对象。
在服务层,我们拼装不同的查询条件SolrQuery即可。
因为搜索功能只在搜索工程中用到,所以可以不写接口,只写实现类。返回值:SearchResult。
我们这里是为了方便,但是在实际工作中,我们不能偷懒,一定要是一个接口对应至少一个实现类。
在taotao-search-service中创建com.taotao.search.dao包,在包中SearchDao创建用于访问索引库。
代码如下:

/**
 * 因为搜索功能只在搜索工程中用到,所以可以不写接口,只写实现类。但是实际工作中不推荐。
 * @author    chenmingjun
 * @date    2018年11月22日上午1:02:03
 * @version 1.0
 */

@Repository // @Service也可以,只是我们把Dao和Service层写在一起了
public class SearchDaoImpl {

    // 注入SolrServer
    @Autowired
    private SolrServer solrServer;

    public SearchResult search(SolrQuery query) throws Exception {
        // 1、根据SolrServer对象查询索引库
        QueryResponse response = solrServer.query(query);
        // 2、取出查询结果
        SolrDocumentList solrDocumentList = response.getResults();

        // 3、处理查询结果(即把取出的查询结果进行封装,即设置值)
        SearchResult result = new SearchResult();
        // 1) 取出总记录数放到SearchResult中去
        Long recordCount = solrDocumentList.getNumFound();
        result.setRecordCount(recordCount);

        // 2) 取出新的商品列表放到SearchResult中去(注意:新的商品=>SearchItem,要与之前的商品Item区分开)
        List<SearchItem> searchItemList = new ArrayList<>();
        // 获取高亮列表
        Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
        for (SolrDocument solrDocument : solrDocumentList) {
            SearchItem searchItem = new SearchItem();
            searchItem.setId((String) solrDocument.get("id"));
            searchItem.setSell_point((String) solrDocument.get("item_sell_point"));
            searchItem.setPrice((Long) solrDocument.get("item_price"));
            searchItem.setImage((String) solrDocument.get("item_image"));
            searchItem.setCategory_name((String) solrDocument.get("item_category_name"));
            // 取出高亮显示的内容
            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");
            }
            searchItem.setTitle(itemTitle);
            // 将新的商品添加到商品列表
            searchItemList.add(searchItem);
        }
        // 将新的商品列表放到SearchResult中去
        result.setItemList(searchItemList);

        // 4、返回结果
        return result;
    }
}

4.4、Service层

4.4.1、功能分析

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

4.4.2、定义service接口

接口已存在,只需要添加方法即可。

    /**
     * 根据查询条件搜索
     * @param queryString 页面传过来的查询条件
     * @param page 页码
     * @param rows 每页显示的记录数
     * @return
     * @throws Exception
     */
    SearchResult search(String queryString, Integer page, Integer rows) throws Exception;

4.4.3、定义service接口实现类

代码如下:

    // 注入SearchDaoImpl
    @Autowired
    private SearchDaoImpl searchDaoImpl;

    @Override
    public SearchResult search(String queryString, Integer page, Integer rows) throws Exception {
        // 1、创建一个SolrQuery对象。
        SolrQuery query = new SolrQuery();
        // 2、设置主查询条件。
        if (StringUtils.isNotBlank(queryString)) {
            query.setQuery(queryString); // 按传递过来的条件进行查询
        } else {
            query.setQuery("*:*"); // 查询所有
        }
        // 3、设置过滤条件:分页、默认搜索域、高亮等等
        // 1) 设置分页
        if (page == null) {
            page = 1;
        }
        if (rows == null) {
            rows = 60;
        }
        query.setStart((page - 1) * page);
        query.setRows(rows);
        // 2) 设置默认的搜索域
        query.set("df", "item_keywords");
        // 3) 设置高亮显示
        query.setHighlight(true);
        query.addHighlightField("item_title");
        query.setHighlightSimplePre("<span color='red'>");
        query.setHighlightSimplePost("</span>");

        // 4、调用Dao的方法,执行搜索,返回的是SearchResult,该返回值里面只包含了"总记录数"和"新的商品列表"
        SearchResult result = searchDaoImpl.search(query);
        // 5、设置总页数,需要先计算出总页数
        Long recordCount = result.getRecordCount();
        Long pageCount = recordCount / rows;
        if (recordCount % rows > 0) {
            pageCount++;
        }
        result.setPageCount(pageCount);

        // 6、返回SearchResult
        return result;
    }

4.4.4、发布服务

在taotao-search-service工程applicationContext-service.xml文件中发布服务:

https://img1.sycdn.imooc.com//5bf67b670001b3bb11660259.jpg
要想让扫描组件扫描到SearchDaoImpl,需要修改组件扫描的配置:
https://img1.sycdn.imooc.com//5bf67b6f0001b0c711650261.jpg

4.5、表现层

该功能在taotao-search-web中实现。

4.5.1、引用服务

在taotao-search-web工程springmvc.xml文件中引用服务:

https://img1.sycdn.imooc.com//5bf67b7b00010b0d09850136.jpg

4.5.2、Controller

请求的url:/search
参数:
  1、q 查询条件。
  2、page 页码。默认为1
返回值:
  逻辑视图,返回值。String。
业务逻辑:
  1、接收参数
  2、调用服务查询商品列表
  3、把查询结果传递给页面。需要参数回显。
代码如下:

@Controller
public class SearchController {

    @Value("${ITEM_ROWS}")
    private Integer ITEM_ROWS;

    @Autowired
    private SearchItemService searchItemService;

    @RequestMapping("/search")
    public String search(@RequestParam("q")String queryString, 
            @RequestParam(defaultValue="1")Integer page, Model model) throws Exception {

        // 1、引用服务
        // 2、注入searchItemService
        // 3、调用方法
        SearchResult result = searchItemService.search(queryString, page, ITEM_ROWS);
        // 4、设置数据传递到jsp中进行回显
        model.addAttribute("query", queryString);
        model.addAttribute("page", page);

        model.addAttribute("totalPages", result.getPageCount());
        model.addAttribute("itemList", result.getItemList());

        // 5、返回逻辑视图:search.jsp
        return "search";
    }
}

4.5.3、属性文件中配置行数

搜索的结果页面中显示的每页的行数,我们查询到数据进行回显,每页显示多少条数据由我们决定。

https://img1.sycdn.imooc.com//5bf67b85000176ba06600373.jpg
文件存放在taotao-search-web中的resource目录下
还需要配置属性文件加载:在taotao-search-web的springmvc.xml中
https://img1.sycdn.imooc.com//5bf67b8e0001943d11660365.jpg

4.5.4、测试

https://img1.sycdn.imooc.com//5bf67b950001d47410570591.jpg
1、测试发现有乱码,是GET请求乱码,需要对请求参数进行转码处理:
方式一:在Controller中改
https://img1.sycdn.imooc.com//5bf67b9d000140e908650670.jpg
方式二:修改linux系统上tomcat的配置文件

[root@itheima conf]# pwd
/usr/local/solr/tomcat/conf
[root@itheima conf]# vim server.xml 

增加一句代码如下:

https://img1.sycdn.imooc.com//5bf67ba40001aca907570273.jpg
2、翻页处理:在taotao-search-web工程中:
/taotao-search-web/src/main/webapp/js/search_main.js,修改如下:
https://img1.sycdn.imooc.com//5bf67bac0001790c08480312.jpg

4.6、图片显示处理

数据库中保存的图片是以逗号分隔的url列表,只需要展示第一张图片即可。
方法1:
  1、向索引库中添加(导入)文档时,只取第一张图片的地址写入索引库。
  2、从文档列表转换为商品列表时可以取一张。
  3、在jsp中对列表拆分,只取一张展示。
方法2:
可以在SearchItem中添加一个getImages()方法,用于页面获取经过处理的pojo的字段数据:

https://img1.sycdn.imooc.com//5bf67bb40001c03705660389.jpg
在jsp中取属性:
https://img1.sycdn.imooc.com//5bf67bbd0001982309050178.jpg

原文出处:https://www.cnblogs.com/chenmingjun/p/10002204.html  


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消