商城06——solr索引库搭建&solr搜索功能实现&图片显示问题解决

1.   课程计划

1、搜索工程的搭建

2、linux下solr服务的搭建

3、Solrj使用测试

4、把数据库中的数据导入索引库

5、搜索功能的实现

2.   搜索工程搭建

要实现搜索功能,需要搭建solr服务、搜索服务工程、搜索系统(表现层的工程)

2.1. Solr服务搭建

2.1.1.    Solr的环境

  Solr是java开发。

  solr的安装文件需要安装jdk。

  安装环境Linux。

  需要安装Tomcat。

2.1.2.    搭建步骤

  第一步:把solr 的压缩包上传到Linux系统。

      Alt+p打开sftp窗口:输入put "F:/java/ziyuan/solr-4.10.3.tgz.tgz"

  第二步:解压solr :tar -xvf solr-4.10.3.tgz.tgz

      新建一个目录安装solr: cd /usr/local     --->     mkdir solr

  第三步:安装Tomcat,解压缩即可。

  第四步:把solr部署到Tomcat下。

      将tomcat拷贝到solr目录下:cp apache-tomcat-7.0.52 /usr/local/solr -r

  第五步:解压缩war包。可以启动Tomcat解压。

      war包所在位置:solr-4.10.3/example/webapps/solr.war

      将该war包拷贝到:cp  solr.war /usr/local/solr/apache-tomcat-7.0.52/webapps

      启动Tomcat解压:

          cd /usr/local/solr/apache-tomcat-7.0.52/bin/

          ./startup.sh

          ./shutdown.sh

          ../webapps

          rm -rf solr.war  (删除掉这个war文件)         

  第六步:把/root/solr-4.10.3/example/lib/ext目录下的所有的jar包,添加到solr工程中。

    [root@localhost webapps]# cp /root/solr-4.10.3/example/lib/ext/*.jar /usr/local/solr/apache-tomcat-7.0.52/webapps/solr/WEB-INF/lib

  第七步:创建一个solrhome。/example/solr目录就是一个solrhome。复制此目录到/usr/local/solr/solrhome:

    [root@localhost webapps]# cp /root/solr-4.10.3/example/solr  /usr/local/solr/solrhome -r

  第八步:关联solr及solrhome。需要修改solr工程的web.xml文件。

    cd /usr/local/solr/apache-tomcat-7.0.52/webapps/solr/WEB-INF/

    修改web.xml文件: vim web.xml :

    

  第九步:启动Tomcat:

    cd /usr/local/solr/apache-tomcat-7.0.52/bin/

    ./startup.sh

    查看输出:

      

    网页测试:http://192.168.25.131:8080/solr/

  和windows下的配置完全一样。

2.1.3.    Solr的使用

  添加文档必须有id域,其他域 必须在solr的schema.xml中定义。 

2.2. 配置业务域

schema.xml中定义

  1、商品Id      使用schema.xml中的id域

  2、商品标题

  3、商品卖点

  4、商品价格

  5、商品图片

  6、分类名称

  7、商品描述 

创建对应的业务域。需要制定中文分析器。创建步骤:

  第一步:把中文分析器添加到solr工程中。

    1、把IKAnalyzer2012FF_u1.jar添加到solr工程的lib目录下

     [root@localhost IKAnalyzer]# cp IKAnalyzer2012FF_u1.jar /usr/local/solr/apache-tomcat-7.0.52/webapps/solr/WEB-INF/lib

    2、把扩展词典、配置文件放到solr工程的WEB-INF/classes目录下。

        cd /usr/local/solr/apache-tomcat-7.0.52/webapps/solr/WEB-INF

     [root@localhost WEB-INF]# mkdir classes

     [root@localhost WEB-INF]# cp /root/IKAnalyzer/*.dic *.xml ./classes

  第二步:配置一个FieldType,制定使用IKAnalyzer

    修改schema.xml文件,添加FieldType:

      cd /usr/local/solr/solrhome/collection1/conf/

      vim schema.xml

<fieldType name="text_ik" class="solr.TextField">
  <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>

  第三步:配置业务域,type制定使用自定义的FieldType。

    设置业务系统Field(type="text_ik"要分词(string不分词),indexed="true"要索引,stored="true"要存储)

<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
<field name="item_price"  type="long" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category_name" type="string" indexed="true" stored="true" />
<field name="item_desc" type="text_ik" indexed="true" stored="false" />

<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_sell_point" dest="item_keywords"/>
<copyField source="item_category_name" dest="item_keywords"/>
<copyField source="item_desc" dest="item_keywords"/>

  第四步:重启tomcat

    cd /usr/local/solr/apache-tomcat-7.0.52/bin

    [root@localhost bin]# ./startup.sh 

2.3. 搜索服务工程搭建

可以参考taotao-manager创建。

  taotao-search(聚合工程pom

    |--taotao-search-interface(jar

    |--taotao-search-Service(war

(1)taotao-search的pom.xml:

<modules>
      <module>taotao-search-interface</module>
      <module>taotao-search-service</module>
  </modules>
  <build>
        <plugins>
            <!-- tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8084</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
View Code

(2)taotao-search-interface的pom.xml:

  依赖taotao-commom、taotao-manager-pojo

(3)taotao-search-service的pom.xml:

<dependencies>
        <!-- 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>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>
        <!-- solr客户端 -->
        <dependency>
            <groupId>org.apache.solr</groupId>
            <artifactId>solr-solrj</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.12.4</version>
                <configuration>
                    <!-- 跳过测试 -->
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>
View Code

  配置文件:

applicationContext-dao.xml:

  由于搜索的数据涉及到3张表,需要自己定义mapper,而mapper的使用,只在搜索服务工程中,所以mapper接口及映射文件需要放在taotao-search-service工程中。

<!-- 数据源 -->
    <!-- sqlsessionfactory -->
    <!-- mapper扫描器 -->
    
    <context:property-placeholder location="classpath:properties/*.properties" />
    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="maxActive" value="10" />
        <property name="minIdle" value="5" />
    </bean>
    <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库连接池 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 加载mybatis的全局配置文件 -->
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
    </bean>
    <!-- 只用于搜索的mapper -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.taotao.search.mapper" />
    </bean>

3.   测试使用solrJ管理索引库

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

3.1. 添加文档

  第1步:把solrJ的jar包添加到工程中。

  第2步:创建一个SolrServer,使用HttpSolrServer创建对象。

  第3步:创建一个文档对象SolrInputDocument对象。

  第4步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。

  第5步:把文档添加到索引库中。

  第6步:提交。

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

3.2. 删除文档

3.2.1.    根据id删除

  第1步:创建一个SolrServer对象。

  第2步:调用SolrServer对象的根据id删除的方法。

  第3步:提交。

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

3.2.2.    根据查询删除

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

3.3. 查询索引库

查询步骤:

  第1步:创建一个SolrServer对象

  第2步:创建一个SolrQuery对象。

  第3步:向SolrQuery中添加查询条件、过滤条件。。。

  第4步:执行查询。得到一个Response对象。

  第5步:取查询结果。

  第6步:遍历结果并打印。

3.3.1.    简单查询

@Test
    public void queryDocument() throws Exception {
        // 第一步:创建一个SolrServer对象
        SolrServer solrServer = new HttpSolrServer("http://192.168.25.131: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"));
        }
    }

 3.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"));
        }
    }

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

  域包括tb_item(商品表),tb_item_cat(商品类目表),tb_item_desc(商品描述表)这三张表的数据。

  要实现功能:从数据库将数据导出来后,导入到索引库。

4.1. 功能分析

  在schema.xml中定义以下业务域(已经定义好):

    商品Id

    商品标题

    商品卖点

    商品价格

    商品图片

    分类名称

    商品描述

  

  需要从tb_item, tb_item_cat, tb_item_desc表中查询数据。

   Sql语句:(返回值是列表,需要有一个POJO封装一行记录,再存放到List中返回)

    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;

应该在taotao-manager-web工程中调用导入索引库的服务。

   在taotao-manager-web后台系统中 做一个导入索引库的功能界面(例如:有个按钮,点击即可将数据从数据库中导入到索引库)。

  

   业务逻辑:

    1、点击按钮,表现层调用服务层的工程的导入索引库的方法

    2、服务层实现 调用mapper接口的方法查询所有的商品的数据

    3、将数据一条条添加到solrinputdocument文档中

    4、将文档添加到索引库中

    5、提交,并返回导入成功即可

4.2. Dao层

4.2.1.    创建POJO

  创建以下POJO用于存放查询到的用于搜索的商品的数据,并放入taotao-common中

/**
 * 搜索的商品数据POJO
 */
public class SearchItem implements Serializable {
    private Long id;     //商品的id 
    private String title;//商品标题
    private String sell_point;//商品卖点
    private Long price;  //价格
    private String image;//商品图片的路径
    private String category_name;//商品分类名称
    private String item_desc;//商品的描述
    set/get......
}

4.2.2.    定义mapper接口

 接口(SearchItemMapper)

/**
 * 定义mappre,关联查询3张表 查询出搜索时的商品数据
 */
public interface SearchItemMapper {
    //查询所有商品数据
    public List<SearchItem> getSearchItemList();
}

4.2.3.    Mapper映射文件(SearchItemMapper.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.taotao.search.mapper.SearchItemMapper" >
    <select id="getSearchItemList" 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,
        tb_item_cat b,
        tb_item_desc c
    WHERE
        a.cid = b.id
    AND a.id = c.item_id
    AND a.status = 1
    
    </select>
</mapper>

4.3. Service层

  参数:无

  业务逻辑:taotao-search中实现

    1、查询所有商品数据。

    2、创建一个SolrServer对象。

    3、为每个商品创建一个SolrInputDocument对象。

    4、为文档添加域

    5、向索引库中添加文档。

    6、返回TaotaoResult。

4.3.1.    创建applicationContext-solr.xml

  在 applicationContext-solr.xml中配置SolrServer对象。

    <bean class="org.apache.solr.client.solrj.impl.HttpSolrServer">
        <constructor-arg name="baseURL" value="http://192.168.25.131:8080/solr"></constructor-arg>
    </bean>

4.3.2.    定义service接口

  接口:

public interface SearchService {
    /**
     * 导入所有的商品数据到索引库中
     */
    public TaotaoResult importAllItems() throws Exception;
}

4.3.3.    service实现类

 taotao-search-service 的 com.taotao.search.service.impl 包下:

@Service
public class SearchServiceImpl implements SearchService {

    // 1.注入mapper
    @Autowired
    private SearchItemMapper searchItemMapper;
    
    @Autowired
    private SolrServer solrServer;
    
    @Override
    public TaotaoResult importAllItems() throws Exception {
        //2.调用mapper的方法,查询所有的商品的数据
        List<SearchItem> searchItemList = searchItemMapper.getSearchItemList();
        //3.通过solorj将数据写入到索引库中
            //3.1 创建httpSolrServer(配置文件applicationContext-solr.xml已经创建了)
            //3.2 创建SolrInputDocument ,将列表searchItemList中的元素一个个放到索引库中
        for(SearchItem searchItem:searchItemList){
            //为每个商品创建一个SolrInputDocument对象。
            SolrInputDocument document = new SolrInputDocument();
            //为文档添加域
            document.addField("id", searchItem.getId().toString());//这里是字符串需要转换
            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());
            //添加到索引库
            solrServer.add(document);
        }
        //提交
        solrServer.commit();
        //4.返回TaotaoResult
        return TaotaoResult.ok();
    }
}

4.3.4.    在taotao-search-service中发布服务

<!-- 使用dubbo发布服务 -->
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="taotao-search" />
    <dubbo:registry protocol="zookeeper" address="192.168.25.131:2181" />
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20882" />
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.taotao.search.service.SearchService" ref="searchServiceImpl" timeout="300000"/>

4.4. 表现层

4.4.1.    创建JSP(前端页面实现)

  index.jsp中添加网站索引管理:

  

  在taotao-manager-web中创建 indexManager.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">
// 通过ajax 发送请求
   function importAll(){
        $.ajax({
            url:"/index/importAll",
            type:"GET",
            success:function(data){
                //taotaoresult
                if(data.status==200){
                    alert("导入成功");
                }else{
                    alert("导入失败");
                }
            }
            
        });       
   }
</script>

4.4.2.  引用服务

  在 taotao-manager-web 中的 springmvc.xml 引用服务。

<dubbo:reference interface="com.taotao.search.service.SearchService" id="searchService" timeout="300000"/>

4.4.3.  添加taotao-search-interface的依赖

  在taotao-manager-web工程中的pom.xml中添加

4.4.4.   Controller

  请求的url:/index/importAll

  参数:无

  返回值:json数据。TaotaoResult。

@Controller
public class SearchItemController {

    @Autowired
    private SearchService searchService;
    /**
     * 导入所有的商品数据到索引库中
     * @return
     */
    @RequestMapping("/index/importAll")
    @ResponseBody
    public TaotaoResult importAllItems(){
        
        try {
            return searchService.importAllItems();
        } catch (Exception e) {
            e.printStackTrace();
            return TaotaoResult.build(500, "导入数据失败");
        }
    }
}

4.4.5.    测试

  测试出错,原因是未将工程中的映射文件发布到classpath中。

  在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>

还需要在taotao-search-service中加入mybatis和数据库连接池的依赖:

<dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- Mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
            </dependency>
            <!-- 分页插件pagehelper -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
            </dependency>

            <!-- MySql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- 连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
            </dependency>
View Code

效果:

导入成功后solr索引库数据:

 

5.   商品搜索功能实现

5.1. 搜索表现层工程搭建

  搜索结果页面的展示。

  Taotao-search-web,打包方式war。可以参考taotao-manager-web创建。

5.1.1.    Pom文件

 <dependencies>
        <!-- common -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- search-interface -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-search-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </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>

        <!-- 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>
        <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8085</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
View Code

5.1.2.    配置文件

springmvc.xml:

<!-- 组件扫描 -->
    <!-- 注解驱动 -->
    <!-- 视图解析器 -->
    <!-- 调用服务 -->
    <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:application name="taotao-search-web" />
    <dubbo:registry protocol="zookeeper" address="192.168.25.131:2181" />
    <!-- <dubbo:reference interface="com.taotao.search.service.SearchItemService"    id="searchItemService" timeout="30000"/> -->

web.xml:

<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>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
View Code

5.1.3.    静态页面

5.2. 搜索功能分析

   在首页的搜索框中输入搜索条件,然后跳转到搜索结果页面。搜索结果页面在taotao-search-web工程中。

   

  首页搜索框的点击按钮处理函数在:首页的JS中,应当改成8085,如图:

  

  请求的url/search   

  http://localhost:8085/search.html?q=

  参数

    1、q 查询条件。

    2、page 页码。默认为1   每页显示多少行 在controller中写死即可。比如:60

  返回值: String。(商品的列表信息,总页数 ,总记录数,数据回显)   定义一个返回结果类SearchResult,存放商品的列表信息,总页数 ,总记录数。

  业务逻辑

    1、接收查询条件。

    2、创建一个SolrServer对象,需要注入。

    3、创建一个SolrQuery对象。

    4、需要设置查询条件、分页条件、设置默认搜索域、高亮设置。

    5、执行查询,返回QueryResponse对象。

    6、取返回结果,封装到List<SearchItem>中。

    7、返回查询结果的总记录数,计算查询结果的总页数。

    8、得到查询结果,渲染jsp. 

5.3. Dao层

5.3.1.    功能分析

  访问索引库的类。定义一些通用的数据访问方法。(注:不能用逆向工程了。dao层功能在taotao-search-service工程中的com.taotao.search.dao包中定义)

  业务逻辑就是查询索引库。

  参数:SolrQuery对象

  业务逻辑:

    1、根据Query对象进行查询。

    2、返回查询结果。包括List<SearchItem>、查询结果的总记录数。

  需要把返回结果封装到pojo中,至少包含两个属性:List<SearchItem>、Long recordCount。再包含一个总页数。

  创建如下SearchResult对象,放入taotao-common中

public class SearchResult implements Serializable {
    private List<SearchItem> itemList;//搜索结果列表
    private long recordCount;//总记录数
    private long pageCount;//总页数
    set/get......
}

5.3.2.    创建SearchDao

由于搜索功能只在搜索工程中用到,可以不写接口,只写类(将Search直接定义为类,没必要定义为接口再实现)。返回值:SearchResult 

/**
 * 从索引库中搜索商品的DAO
 * @author smileZTT
 *
 */
@Repository
public class SearchDao {
    
    //1.创建solrServer对象 由sprin管理注入
    @Autowired
    private SolrServer solrServer;

    /*根据查询条件查询商品的结果集*/
    public SearchResult search(SolrQuery query) throws Exception {
        
        SearchResult searchResult = new SearchResult();
        
        //2.直接执行查询
        QueryResponse response = solrServer.query(query);
        //3.获取结果集
        SolrDocumentList results = response.getResults();
        
        //4.遍历结果集
        List<SearchItem> itemList = new ArrayList<SearchItem>();
        for(SolrDocument solrDocument : results){
            SearchItem item = new SearchItem();
            item.setId(Long.parseLong(solrDocument.get("id").toString()));
            item.setCategory_name(solrDocument.get("item_category_name").toString());
            item.setImage(solrDocument.get("item_image").toString());
            item.setPrice((long) solrDocument.get("item_price"));
            item.setSell_point(solrDocument.get("item_sell_point").toString());
            //取高亮显示
            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 = solrDocument.get("item_title").toString();
            }
            item.setTitle(itemTitle);
            //添加到商品列表
            itemList.add(item);
        }
        //5.设置searchResult中的属性,然后返回
        searchResult.setItemList(itemList);//设置搜索结果列表
        searchResult.setRecordCount(results.getNumFound());//设置总记录数
        return searchResult;
    }
}

5.4. Service层

5.4.1.    功能分析

  参数:queryString:查询条件

            Page:页码

            Rows:每页显示的记录数。

  业务逻辑

    1、创建一个SolrQuery对象。

    2、设置查询条件

    3、设置分页条件

    4、需要指定默认搜索域。

    5、设置高亮

    6、执行查询,调用SearchDao。得到SearchResult

    7、需要计算总页数。

    8、返回SearchResult

5.4.2.   service接口

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

public interface SearchService {
    
    /**
     * 导入所有的商品数据到索引库中
     */
    public TaotaoResult importAllItems() throws Exception;
    
    /**
     * 根据搜索条件搜索
     * @param queryString  查询的主条件
     * @param page  查询的当前页码
     * @param rows  每页显示的行数,这个在Controller中写死
     * @return
     * @throws Exception
     */
    public SearchResult search(String queryString, Integer page, Integer rows) throws Exception;
}

5.4.3.  service接口实现类  SearchServiceImpl :

@Service
public class SearchServiceImpl implements SearchService {
    
    @Autowired
    private SearchDao searchDao;
    
    @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("*:*");
        }
        //2.1 设置过滤条件 设置分页
        if(page == null) page = 1;
        if(rows == null) rows = 60;
        query.setStart((page - 1) * rows);
        query.setRows(rows);
        //2.2 设置默认的搜索域
        query.set("df", "item_keywords");
        //2.3 设置高亮
        query.setHighlight(true);//开启高亮
        query.addHighlightField("item_title");//设置高亮显示的域
        query.setHighlightSimplePre("<em style="color:red">");
        query.setHighlightSimplePost("</em>");
        
        //3.利用dao方法返回的SearchResult只包含了总记录数和商品列表
        SearchResult result = searchDao.search(query);
        
        //4.设置SearchResult的总页数
        long recordCount = result.getRecordCount();
        long pageCount = recordCount / rows;
        if(recordCount % rows > 0)
            pageCount++;
        result.setPageCount(pageCount);
        
        //5.返回
        return result;
    }
}

5.4.4.   在taotao-search-service中发布服务

  <!-- 使用dubbo发布服务 -->
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="taotao-search" />
    <dubbo:registry protocol="zookeeper" address="192.168.25.131:2181" />
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20882" />
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.taotao.search.service.SearchService" ref="searchServiceImpl" timeout="300000"/>

5.4. 表现层

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

5.4.1.   引用服务

 taotao-search-web的sprinmvc.xml 中引用:

  <!-- 引用dubbo服务 -->
    <dubbo:application name="taotao-search-web" />
    <dubbo:registry protocol="zookeeper" address="192.168.25.131:2181" />
    <dubbo:reference interface="com.taotao.search.service.SearchService"    id="searchService" timeout="30000"/>

5.4.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 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";    
    }
}

属性文件中配置行数:(文件存放在taotao-search-web中的resource目录下)

还需要配置属性文件加载:在springmvc.xml中

5.4.3.    测试

测试发现有乱码,是GET请求乱码,需要对请求参数进行转码处理:

翻页处理:在taotao-search-web工程中:修改如下:

5.5. 图片显示处理

  数据库中保存的图片是以逗号分隔的url列表,只需要展示第一张图片即可。

  方法:

    1、向索引库中添加文档时,只取第一张写入索引库

    2、从文档列表转换为商品列表时可以取一张。

    3、在jsp中对列表拆分,只取一张展示。

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

  

   在search.jsp中取属性:

      

原文地址:https://www.cnblogs.com/toria/p/taotao06.html