课程计划
第六天:
1, 搜索工程的搭建
2,Linux 下 Solr 服务的搭建
3, 测试使用 solrJ 管理索引库
4, 把商品数据导入到索引库中(后台功能)
5, 商品搜索功能实现(前台功能)
1, 搜索工程的搭建
要实现搜索功能, 需要搭建 Solr 服务, 搜索服务工程,
搜索系统(表现层的工程)
1.1,Solr 服务搭建
1.1.1,Solr 的环境
Solr 是 java 开发的.
Solr 的安装文件.
推荐在 Linux 环境下使用 Solr, 需要安装环境 Linux.
需要安装 jdk. 参考链接: https://www.cnblogs.com/chenmingjun/p/9931593.html
需要安装 tomcat.
1.1.2,Solr 的搭建步骤
第一步: 使用 SecureCRT 的 SFTP 功能, 把 Solr-4.10.3.tgz.tgz 的压缩包上传到 Linux 系统.
第二步: 解压缩 Solr 后, 删除该安装包.
- [root@itheima ~]# tar -zxvf Solr-4.10.3.tgz.tgz
- ......
- ......
- [root@itheima ~]# ll
总用量 146496
drwxr-xr-x. 8 root root 218 11 月 20 17:07 Solr-4.10.3
-rw-r--r--. 1 root root 150010621 9 月 26 23:16 Solr-4.10.3.tgz.tgz
- [root@itheima ~]# rm -rf Solr-4.10.3.tgz.tgz
- [root@itheima ~]# ll
总用量 0
drwxr-xr-x. 8 root root 218 11 月 20 17:07 Solr-4.10.3
[root@itheima ~]#
第三步: 使用 SecureCRT 的 SFTP 功能, 把 apache-tomcat-7.0.47.tar.gz 的压缩包上传到 Linux 系统, 解压后删除安装包.
- [root@itheima ~]# tar -zxvf apache-tomcat-7.0.47.tar.gz
- [root@itheima ~]# # rm -rf apache-tomcat-7.0.47.tar.gz
第四步: 创建 Solr 存放的目录, 复制 apache-tomcat-7.0.47 目录到 / usr/local/Solr/tomcat 目录下
- [root@itheima ~]# mkdir /usr/local/Solr
- [root@itheima ~]# cp -r apache-tomcat-7.0.47/ /usr/local/Solr/tomcat
第五步: 把 Solr-4.10.3/dist/Solr-4.10.3.war 文件部署 (复制) 到 tomcat 目录下, 并重命名为 Solr.war.
[root@itheima ~]# cp Solr-4.10.3/dist/Solr-4.10.3.war /usr/local/Solr/tomcat/webapps/Solr.war
注意: 复制目录 (文件夹) 的时候需要加 - r, 复制文件的时候不需要加 - r.
第六步: 解压缩 Solr.war 包. 启动 tomcat 即可自动解压 war 包, 并查看 tomcat 启动日志.
- [root@itheima ~]# cd /usr/local/Solr/tomcat/bin/
- [root@itheima bin]# ./startup.sh
- Using CATALINA_BASE: /usr/local/Solr/tomcat
- Using CATALINA_HOME: /usr/local/Solr/tomcat
- Using CATALINA_TMPDIR: /usr/local/Solr/tomcat/temp
- Using JRE_HOME: /usr/local/java/jdk1.7.0_80/jre
- Using CLASSPATH: /usr/local/Solr/tomcat/bin/Bootstrap.jar:/usr/local/Solr/tomcat/bin/tomcat-juli.jar
- [root@itheima bin]# cd ..
- [root@itheima tomcat]# tail -f logs/catalina.out
十一月 20, 2018 5:23:24 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying Web application directory /usr/local/Solr/tomcat/webapps/host-manager
十一月 20, 2018 5:23:25 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying Web application directory /usr/local/Solr/tomcat/webapps/manager
十一月 20, 2018 5:23:25 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]
十一月 20, 2018 5:23:25 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-bio-8009"]
十一月 20, 2018 5:23:25 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in 11144 ms
第六步: 关闭 tomcat 后, 删除掉没用的 Solr.war 包. 原则: 没用的东西及时删掉.
注意: 要是想删掉没用的 Solr.war 包, 必须在关闭 tomcat 的情况下, 否则解压缩后的 Solr 包也会一并删除掉.
- [root@itheima tomcat]# bin/shutdown.sh
- Using CATALINA_BASE: /usr/local/Solr/tomcat
- Using CATALINA_HOME: /usr/local/Solr/tomcat
- Using CATALINA_TMPDIR: /usr/local/Solr/tomcat/temp
- Using JRE_HOME: /usr/local/java/jdk1.7.0_80/jre
- Using CLASSPATH: /usr/local/Solr/tomcat/bin/Bootstrap.jar:/usr/local/Solr/tomcat/bin/tomcat-juli.jar
- [root@itheima tomcat]# rm -rf webapps/Solr.war
第七步: 想要启动 Solr 工程, 还需要添加 Solr 的扩展服务包.
把 / root/Solr-4.10.3/example/lib/ext 目录下的所有的 jar 包, 添加到 Solr 工程中.
- [root@itheima ext]# pwd
- /root/Solr-4.10.3/example/lib/ext
- [root@itheima ext]# cp * /usr/local/Solr/tomcat/webapps/Solr/Web-INF/lib/
第八步: 创建一个 solrhome 目录.
我们知道:/root/Solr-4.10.3/example/Solr 目录就是一个 solrhome 目录.
复制此目录中所有内容到 / usr/local/Solr/solrhome 目录下
- [root@itheima example]# pwd
- /root/Solr-4.10.3/example
- [root@itheima example]# cp -r Solr /usr/local/Solr/solrhome
第九步: 关联 Solr 工程及 solrhome. 需要修改 Solr 工程的 Web.xml 文件.
- [root@itheima ~]# cd /usr/local/Solr/tomcat/webapps/Solr/Web-INF/
- [root@itheima Web-INF]# VIM Web.xml
修改如下图所示:
第九步: 再次启动 tomcat
- [root@itheima ~]# cd /usr/local/Solr/tomcat/
- [root@itheima tomcat]# bin/startup.sh
第十步: 修改防火墙配置
CentOS 7.X 默认的防火墙不是 iptables, 而是 firewalld. 我们可以试一下 systemctl stop firewalld 关闭防火墙, 但是不推荐该方式.
CentOS 6.X 是 iptables, 可以使用 VIM /etc/sysconfig/iptables 修改配置即可.
本博主的是 CentOS7, 防火墙使用的是 firewalld, 我们使用命令的方式来添加端口(修改后需要重启 firewalld 服务):
- [root@itheima ~]# cd /etc/firewalld/zones/
- [root@itheima zones]# firewall-cmd --permanent --add-port=8080/tcp
- success
- [root@itheima zones]# service firewalld restart
- Redirecting to /bin/systemctl restart firewalld.service
- [root@itheima zones]#
第十一步: 测试连接
访问地址: http://192.168.25.154:8080/solr/
其实和在 Windows 下的配置完全一样.
浏览器界面如下:
点击按钮 "collection1"
1.1.3,Solr 的使用
添加文档时必须有 id 域
, 其他域必须在 Solr 的 schema.xml 中进行定义.
1.2, 配置业务域
1.2.1, 在 schema.xml 中需要定义以下字段
1, 商品 id(根据 id 查询商品描述页)
2, 商品标题 title
3, 商品卖点 sell_point
4, 商品价格 price
5, 商品图片 image
6, 分类名称 category_name(不是分类 id, 我们一般不会根据商品分类 id 去查询商品, 而是根据商品分类名称去查)
7, 商品描述 item_desc(实际开发中不需要搜索商品描述)
一共涉及到三张表: tb_item,item_cat,item_desc.
创建对应的业务域. 同时需要指定中文分析器.
1.2.2, 创建业务域步骤
第一步: 把中文分析器添加到 Solr 工程中.
0, 把文件夹 IK Analyzer 2012FF_hf1 上传至 Linux 中.
1, 把 IKAnalyzer2012FF_u1.jar 拷贝到 Solr 工程的 lib 目录下.
2, 把扩展词词典, 停用词字典, 配置文件拷贝到 Solr 工程的 Web-INF/classes 目录下.(没有 classes 目录就先创建该目录)
- [root@itheima IK Analyzer 2012FF_hf1]# pwd
- /root/IK Analyzer 2012FF_hf1
- [root@itheima IK Analyzer 2012FF_hf1]# cp IKAnalyzer2012FF_u1.jar /usr/local/Solr/tomcat/webapps/Solr/Web-INF/lib/
- [root@itheima IK Analyzer 2012FF_hf1]# mkdir /usr/local/Solr/tomcat/webapps/Solr/Web-INF/classes
- [root@itheima IK Analyzer 2012FF_hf1]# cp mydict.dic ext_stopword.dic IKAnalyzer.cfg.xml /usr/local/Solr/tomcat/webapps/Solr/Web-INF/classes
- [root@itheima IK Analyzer 2012FF_hf1]# ll /usr/local/Solr/tomcat/webapps/Solr/Web-INF/classes
总用量 12
-rw-r--r--. 1 root root 168 11 月 20 19:29 ext_stopword.dic
-rw-r--r--. 1 root root 419 11 月 20 19:29 IKAnalyzer.cfg.xml
-rw-r--r--. 1 root root 34 11 月 20 19:29 mydict.dic
[root@itheima IK Analyzer 2012FF_hf1]#
第二步: 配置一个自定义的 fieldType, 使用指定的中文分词器 IKAnalyzer.
修改 Solr 工程下的 schema.xml 文件, 在文件末尾添加一个自定义的 fieldType, 注意: 要在标签 < schema></schema > 里面添加.
- [root@itheima conf]# pwd
- /usr/local/Solr/solrhome/collection1/conf
- [root@itheima conf]# VIM schema.xml
添加内容如下:
- <fieldType name="text_ik" class="solr.TextField">
- <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
- </fieldType>
第三步: 配置业务域, type 指定使用自定义的 fieldType.
设置业务系统的 field
- <schema>
- ......
- ......
- <fieldType name="text_ik" class="solr.TextField">
- <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
- </fieldType>
- <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"/>
- </schema>
注意: 分类名称是不分词只建立索引. 商品描述是分词但是不存储.
第四步: 重启 tomcat, 测试我们自定义的业务域是否好使.
测试结果如下:
1.3, 搜索服务层工程的搭建
1.3.1, 搜索服务工程的创建可以参考 taotao-content 的创建
- taotao-search(聚合工程 pom)
- |--taotao-search-interface(jar)
- |--taotao-search-Service(war)
这里不再赘图了.
目录结构如下:
1.3.2,pom.xml 的配置可以参考 taotao-content 的配置
- /taotao-search/pom.xml
- <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</artifactId>
- <packaging>pom</packaging>
- <modules>
- <module>taotao-search-interface</module>
- <module>taotao-search-service</module>
- </modules>
- <dependencies>
- <!-- 配置对 common 的依赖 -->
- <dependency>
- <groupId>com.taotao</groupId>
- <artifactId>taotao-common</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </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>
- </project>
- /taotao-search-interface/pom.xml
- <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-search</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </parent>
- <artifactId>taotao-search-interface</artifactId>
- <dependencies>
- <!-- 配置对 pojo 的依赖 -->
- <dependency>
- <groupId>com.taotao</groupId>
- <artifactId>taotao-manager-pojo</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </dependency>
- </dependencies>
- </project>
- /taotao-search-service/pom.xml
- <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-search</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </parent>
- <artifactId>taotao-search-service</artifactId>
- <packaging>war</packaging>
- <dependencies>
- <!-- 配置对 dao 的依赖 -->
- <dependency>
- <groupId>com.taotao</groupId>
- <artifactId>taotao-manager-dao</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </dependency>
- <!-- 配置对 interface 的依赖: 服务层发布服务要通过该接口 -->
- <dependency>
- <groupId>com.taotao</groupId>
- <artifactId>taotao-search-interface</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- </dependency>
- <!-- 配置对 spring 的依赖 -->
- <!-- 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>
- <!-- 配置对 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>
- </project>
1.3.3, 框架整合的配置文件可以参考 taotao-content-service 的配置
由于搜索的数据涉及到 3 张表, 所以需要自己定义 mapper.
而 mapper 的使用, 只在搜索服务工程中, 所以 mapper 接口及映射文件需要放在 taotao-search-service 工程中.
- applicationContext-dao.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.2.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-4.2.xsd">
- <!-- 配置数据库连接池 -->
- <!-- 加载配置文件 -->
- <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.mapper" />
- </bean>
- </beans>
其他配置文件, 参考 taotao-content-service 的配置.
2, 测试使用 solrJ 管理索引库
使用 solrJ 可以实现索引库的增删改查操作.
2.1, 通过 SolrJ 向索引库中添加 / 更新索引
第一步: 把 solrJ 的 jar 包添加到工程中. 在 Maven 工程中则是添加依赖.
在 / taotao-search-service/pom.xml 添加对 solrj 客户端的依赖, 如下:
- <!-- 配置对 solrj 客户端的依赖 -->
- <dependency>
- <groupId>org.apache.Solr</groupId>
- <artifactId>Solr-solrj</artifactId>
- </dependency>
第二步: 创建一个 SolrServer 对象(抽象类), 使用 HttpSolrServer 创建对象(连接单机版 Solr), 使用 CloudSolrServer 创建对象(连接集群版 Solr).
第三步: 创建一个文档对象 SolrInputDocument 对象.
第四步: 向文档中添加域. 必须有 id 域, 且域的名称必须在 schema.xml 中定义.
第五步: 把文档对象添加到索引库中.
第六步: 提交.
测试代码如下:
- /**
- * 向索引库中添加索引
- * @throws Exception
- */
- @Test
- public void addDocumentTest() throws Exception {
- // 第一步: 把 solrJ 的 jar 包添加到工程中. 在 Maven 工程中则是添加依赖.
- // 第二步: 创建一个 SolrServer 对象(抽象类), 使用 HttpSolrServer 创建连接对象(连接单机版 Solr), 使用 CloudSolrServer 创建连接对象(连接集群版 Solr).
- SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr/collection1"); // 默认是 collection1, 可写可不写
- // 第三步: 创建一个文档对象 SolrInputDocument 对象.
- SolrInputDocument document = new SolrInputDocument();
- // 第四步: 向文档中添加域. 必须有 id 域, 且域的名称必须在 schema.xml 中定义.
- document.addField("id", "test001"); // 注意: id 是字符串类型, 如果是数值类型, 会自动转为字符串
- document.addField("item_title", "测试商品");
- document.addField("item_price", 1999);
- // 第五步: 把文档对象添加到索引库中.
- solrServer.add(document);
- // 第六步: 提交.
- solrServer.commit();
- }
2.2, 通过 SolrJ 从索引库中删除索引
(1)根据指定 ID 来删除索引
- /**
- * 根据指定 ID 来删除索引
- * @throws Exception
- */
- @Test
- public void deleteIndexByIdTest() throws Exception {
- // 1, 创建 HttpSolrServer 对象, 通过它和 Solr 服务器建立连接.
- HttpSolrServer server = new HttpSolrServer("http://192.168.25.154:8080/solr/collection1"); // 参数: 是 Solr 服务器的访问地址
- // 2, 根据指定 ID 来删除索引
- server.deleteById("test001");
- // 3, 提交.
- server.commit();
- }
(2)根据指定条件删除索引
- /**
- * 根据指定条件来删除索引
- * @throws Exception
- */
- @Test
- public void deleteIndexByConditionTest() throws Exception {
- // 1, 创建 HttpSolrServer 对象, 通过它和 Solr 服务器建立连接.
- HttpSolrServer server = new HttpSolrServer("http://192.168.25.154:8080/solr/collection1"); // 参数: 是 Solr 服务器的访问地址
- // 2, 根据指定条件来删除索引
- server.deleteByQuery("id:test002");
- // 删除全部(慎用)
- // server.deleteByQuery("*:*");
- // 3, 提交.
- server.commit();
- }
2.3, 通过 SolrJ 从索引库中查询索引
2.3.1, 简单查询
- /**
- * 简单查询
- * @throws Exception
- */
- @Test
- public void queryIndexTest01() throws Exception {
- // 1, 创建 SolrServer 对象, 通过它和 Solr 服务器建立连接.
- SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr/collection1"); // 参数: 是 Solr 服务器的访问地址
- // 2, 创建 SolrQuery 对象
- SolrQuery query = new SolrQuery();
- // 设置查询条件, 名称 "q" 是固定的且必须的!
- // query.set("q", "item_category_name: 手机"); // 等价于 query.setQuery("item_category_name: 手机");
- query.setQuery("item_category_name: 手机");
- // 3, 调用 solrServer 的查询方法, 查询索引库
- QueryResponse response = solrServer.query(query);
- // 4, 获取查询结果
- SolrDocumentList results = response.getResults();
- // 5, 处理查询结果
- System.out.println("查询结果总数为:" + results.getNumFound());
- // 6, 遍历结果并打印, 示例只打印 2 个字段
- for (SolrDocument solrDocument : results) {
- System.out.println(solrDocument.get("id"));
- System.out.println(solrDocument.get("item_title"));
- System.out.println(solrDocument.get("item_price"));
- System.out.println("--------------------");
- }
- }
2.3.2, 复杂查询
- /**
- * 复杂查询
- * @throws Exception
- */
- @Test
- public void queryIndexTest02() throws Exception {
- // 1, 创建 SolrServer 对象, 通过它和 Solr 服务器建立连接.
- SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr/collection1"); // 参数: 是 Solr 服务器的访问地址
- // 2, 创建 SolrQuery 对象
- SolrQuery query = new SolrQuery();
- // 设置查询条件
- // query.set("q", "item_category_name: 手机");
- query.setQuery("item_category_name: 手机");
- // 设置过滤条件, 如果设置多个过滤条件的话, 需要使用 query.addFilterQuery(fq);
- // query.setFilterQueries("item_price:[1 TO 20]");
- // 设置排序
- query.setSort("item_price", ORDER.desc);
- // 设置分页信息(使用默认的)
- query.setStart(0);
- query.setRows(10);
- // 设置显示的 field 的域集合
- query.setFields("id,item_title,item_sell_point,item_price,item_image,item_category_name");
- // 设置默认搜素域
- query.set("df", "item_keywords");
- // 设置高亮信息
- query.setHighlight(true);
- query.addHighlightField("item_title");
- query.setHighlightSimplePre("<span color='red'>");
- query.setHighlightSimplePost("</span>");
- // 3, 调用 solrServer 的查询方法, 查询索引库
- QueryResponse response = solrServer.query(query);
- // 4, 获取查询结果
- SolrDocumentList results = response.getResults();
- // 5, 处理查询结果
- System.out.println("查询结果总数为:" + results.getNumFound());
- // 获取高亮列表
- Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
- // 遍历结果并打印, 示例打印所有的字段
- for (SolrDocument solrDocument : results) {
- System.out.println(solrDocument.get("id"));
- 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_sell_point"));
- System.out.println(solrDocument.get("item_price"));
- System.out.println(solrDocument.get("item_image"));
- System.out.println(solrDocument.get("item_category_name"));
- System.out.println("--------------------");
- }
- }
3, 把商品数据导入到索引库中(后台功能)
3.1, 功能分析
在 schema.xml 中定义以下业务域(已经定义好):
1, 商品 id(根据 id 查询商品描述页)
2, 商品标题 title
3, 商品卖点 sell_point
4, 商品价格 price
5, 商品图片 image
6, 分类名称 category_name(不是分类 id, 我们一般不会根据商品分类 id 去查询商品, 而是根据商品分类名称去查)
7, 商品描述 item_desc(实际开发中不需要搜索商品描述)
需要从 tb_item,tb_item_cat,tb_item_desc 表中查询数据.
我们先创建对应的业务域(已经创建好了). 同时需要指定中文分析器.
Solr 服务我们已经搭建好了, 自定义的业务域我们也配置好了, 现在我们要实现商品搜素功能, 那么就需要有数据, 需要把数据从数据库中导入进来, 之前我们可以使用 dataimportHandler 插件, 该插件可以将数据库中指定的 sql 语句的结果导入到 Solr 索引库中. 现在我们需要通过我们网站后台来管理索引库, 而不是通过 dataimportHandler 插件了, 而且我们要做 Solr 集群的话, 插件 dataimportHandler 会有干扰. 所以我们现在不推荐使用 dataimportHandler 插件.
所以我们在测试环境下可以使用 dataimportHandler 插件, 但是生产环境下需要我们手工导入数据.
插件 dataimportHandler, 使用参考链接: https://www.cnblogs.com/chenmingjun/p/9887696.html#_label2_3
我们使用手工导入数据, 需要我们先从数据库中把我们分析出来的业务域取出来, 取出来之后, 循环插入索引库中去, 由于涉及到 3 张表的查询, 所以不能在使用逆向工程生成的 Mapper 代码了. 需要我们手写 Mapper 代码. 我们先把 SQL 语句写出来, 如下:
- SQL1:
- SELECT
- a.id,
- a.title,
- a.sell_point,
- a.price,
- a.image,
- b. NAME AS category_name,
- c.item_desc
- FROM
- tb_item a
- LEFT JOIN tb_item_cat b ON a.cid = b.id
- LEFT JOIN tb_item_desc c ON a.id = c.item_id
- WHERE
- a.`status` = 1
- SQL2:
- SELECT
- a.id,
- a.title,
- a.sell_point,
- a.price,
- a.image,
- b. NAME AS 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;
3.2,Dao 层
3.2.1, 创建 POJO
创建以下 POJO 用于存放从数据库中查询到的商品数据和用于存放从索引库中搜索到的商品数据, 并放入 taotao-common 中.
- /**
- * 搜索商品数据使用的 POJO, 用于存放 "从数据库中查询到的商品数据" 和用于存放 "从索引库中搜索到的商品数据"
- * @author chenmingjun
- * @date 2018 年 11 月 21 日上午 1:05:12
- * @version 1.0
- */
- public class SearchItem implements Serializable {
- private static final long serialVersionUID = 1L;
- private String id; // 商品的 id, 我们使用文档的 id 域作为商品的 id, 文档的 id 域默认定义的是 String 类型
- private String title; // 商品的标题
- private String sell_point; // 商品的卖点
- private Long price; // 商品的价格
- private String image; // 商品的图片路径
- private String category_name; // 商品的分类名称
- private String item_desc; // 商品的描述
- // getter 和 setter 方法
- }
注意: 在我们 schema.xml 文件中, 我们使用文档的 id 域作为商品的 id, 而文档的 id 域默认定义的是 String 类型, 索引库会自动转换将数值类型转换为字符串进行存储, 我们从索引库中取出数据, 我们也使用字符串进行接收.
3.2.2, 定义 Mapper 接口
SearchItemMapper.java
- /**
- * 搜索商品的 Mapper
- * @author chenmingjun
- * @date 2018 年 11 月 21 日上午 11:23:28
- * @version 1.0
- */
- public interface SearchItemMapper {
- /**
- * 查询所有商品数据.(注意: 是从 3 张表中查, 此商品非彼商品)
- * @return
- */
- List<SearchItem> getSearchItemList();
- }
3.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 AS category_name,
- c.item_desc
- FROM
- tb_item a
- LEFT JOIN tb_item_cat b ON a.cid = b.id
- LEFT JOIN tb_item_desc c ON a.id = c.item_id
- WHERE
- a.`status` = 1
- </select>
- </mapper>
3.3,Service 层
参数: 无
业务逻辑:
1, 查询所有商品数据.
2, 创建一个 SolrServer 对象(抽象类), 使用 HttpSolrServer 创建连接对象(连接单机版 Solr), 使用 CloudSolrServer 创建连接对象(连接集群版 Solr).
3, 为每个商品创建一个文档对象 SolrInputDocument 对象.
4, 为文档添加域. 必须有 id 域, 且域的名称必须在 schema.xml 中定义.
5, 把文档对象添加到索引库中.
6, 提交修改.
7, 返回 TaotaoResult.
3.3.1, 配置单机版 Solr 的连接: HttpSolrServer
SolrServer 我们使用 spring 容器生成后注入进来, 需要在配置文件进行配置:
- applicationContext-Solr.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.2.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-4.2.xsd">
- <!-- 配置单机版 solr 的连接: HttpSolrServer-->
- <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
- <constructor-arg name="baseURL" value="http://192.168.25.154:8080/solr/collection1"></constructor-arg>
- </bean>
- </beans>
3.3.2, 定义 service 接口
接口放在 taotao-search-interface 中
- /**
- * 从数据库中查询到数据导入索引库
- * @author chenmingjun
- * @date 2018 年 11 月 21 日下午 2:38:41
- * @version 1.0
- */
- public interface SearchItemService {
- /**
- * 导入搜索的商品数据到索引库中
- * @return
- * @throws Exception
- */
- TaotaoResult importSearchItemsToIndex() throws Exception;
- }
3.3.3, 定义 service 实现类
要想注入 SearchItemMapper 成功, 需要在 taotao-search-service 的 applicationContext-dao.xml 文件中进行配置 Mapper 映射文件的包扫描器:
有两种配置方式:
方式一:
- <!-- 配置 Mapper 映射文件的包扫描器 -->
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.taotao.mapper" />
- </bean>
- <!-- 配置只用于搜索功能的 Mapper -->
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.taotao.search.mapper" />
- </bean>
方式二:
- <!-- 配置 Mapper 映射文件的包扫描器, 扫描多个包, 使用逗号进行分割 -->
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.taotao.mapper,com.taotao.search.mapper" />
- </bean>
本案例中我们使用方式二.
实现类代码如下:
- /**
- * 从数据库中查询到数据导入索引库
- * @author chenmingjun
- * @date 2018 年 11 月 21 日下午 2:42:11
- * @version 1.0
- */
- @Service
- public class SearchItemServiceImpl implements SearchItemService {
- // 注入 SearchItemMapper
- @Autowired
- private SearchItemMapper searchItemMapper;
- // 注入 SolrServer
- @Autowired
- private SolrServer solrServer;
- @Override
- public TaotaoResult importSearchItemsToIndex() throws Exception {
- // 1, 查询所有商品数据.
- List<SearchItem> searchItemList = searchItemMapper.getSearchItemList();
- // 2, 创建一个 SolrServer 对象(抽象类), 使用 HttpSolrServer 创建连接对象(连接单机版 Solr), 使用 CloudSolrServer 创建连接对象(连接集群版 Solr).
- // SolrServer 我们使用 spring 容器生成后注入进来
- for (SearchItem searchItem : searchItemList) {
- // 3, 为每个商品创建一个文档对象 SolrInputDocument 对象.
- SolrInputDocument document = new SolrInputDocument();
- // 4, 为文档添加域. 必须有 id 域, 且域的名称必须在 schema.xml 中定义.
- 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);
- }
- // 6, 提交修改.
- solrServer.commit();
- // 7, 返回 TaotaoResult.
- return TaotaoResult.ok();
- }
- }
3.3.4, 在 taotao-search-service 中发布服务
特别注意: 由于我们的 dubbo 和 zooKeeper 是安装在虚拟机 CentOS 7.5 上的, CentOS 7.X 默认的防火墙不是 iptables, 而是 firewalld. 我们可以试一下 systemctl stop firewalld 关闭防火墙, 但是不推荐该方式. CentOS 6.X 是 iptables, 可以使用
VIM /etc/sysconfig/iptables
修改配置即可.
本博主的是 CentOS7, 防火墙使用的是 firewalld, 我们使用命令的方式来添加端口 20882(修改后需要重启 firewalld 服务):
- [root@itheima ~]# cd /etc/firewalld/zones/
- [root@itheima zones]# firewall-cmd --permanent --add-port=20882/tcp
- success
- [root@itheima zones]# service firewalld restart
- Redirecting to /bin/systemctl restart firewalld.service
- [root@itheima zones]#
3.4, 表现层
3.4.1, 引用服务
由于把从数据库中查询到的新的商品数据导入到索引库中属于后台功能, 所以我们在 taotao-manager-Web 中引用服务.
在 / taotao-manager-Web/src/main/resources/spring/springmvc.xml 中引用服务:
3.4.2, 添加对 taotao-search-interface 的依赖
在 taotao-manager-Web 工程中的 pom.xml 中添加如下:
3.4.3, 修改 index.jsp
由于把从数据库中查询到的新的商品数据导入到索引库中属于后台功能, 所以我们在 taotao-manager-Web 的后台系统中做一个导入索引库的功能界面.(例如: 有个按钮, 点击即可将从数据库中查询到的数据导入到索引库).
业务逻辑:
, 点击按钮, 表现层调用服务层的工程的导入索引库的方法.
, 服务层实现调用 Mapper 接口的方法查询所有的商品的数据.
, 将数据一条条添加到 SolrInputDocument 文档中.
, 将文档添加到索引库中.
, 提交, 并返回导入成功即可.
添加如下代码到 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" onclick="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, 测试
测试之前我们先将索引库中的测试数据清空, 否则会对我们测试有影响, 方法如下:
测试出错, 原因是: 未将工程中新建的映射文件 SearchItemMapper.xml 发布到 classpath 中. 错误截图如下:
由于 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>
浏览器页面展示结果:
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, 配置文件
目录结构如下:
- 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, 搜索结果的前台静态页面
将上面的搜索结果静态页面放入到 taotao-search-Web 工程中
4.2, 搜索功能分析
在首页的搜索框中输入搜索条件, 然后跳转到搜索结果页面. 搜索结果页面在 taotao-search-Web 工程中(端口为 8086).
首页搜索框的点击按钮处理函数在: 在 taotao-portal-Web 工程首页的 JS 中, 应当改成 8086, 如图:
请求的 url:/search
以搜索手机为例: http://localhost:8086/search.HTML?q = 手机
参数:
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 文件中发布服务:
要想让扫描组件扫描到 SearchDaoImpl, 需要修改组件扫描的配置:
4.5, 表现层
该功能在 taotao-search-Web 中实现.
4.5.1, 引用服务
在 taotao-search-Web 工程 springmvc.xml 文件中引用服务:
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, 属性文件中配置行数
搜索的结果页面中显示的每页的行数, 我们查询到数据进行回显, 每页显示多少条数据由我们决定.
文件存放在 taotao-search-Web 中的 resource 目录下
还需要配置属性文件加载: 在 taotao-search-Web 的 springmvc.xml 中
4.5.4, 测试
1, 测试发现有乱码, 是 GET 请求乱码, 需要对请求参数进行转码处理:
方式一: 在 Controller 中改
方式二: 修改 Linux 系统上 tomcat 的配置文件
- [root@itheima conf]# pwd
- /usr/local/Solr/tomcat/conf
- [root@itheima conf]# VIM server.xml
增加一句代码如下:
2, 翻页处理: 在 taotao-search-Web 工程中:
/taotao-search-Web/src/main/webapp/JS/search_main.JS, 修改如下:
4.6, 图片显示处理
数据库中保存的图片是以逗号分隔的 url 列表, 只需要展示第一张图片即可.
方法 1:
1, 向索引库中添加 (导入) 文档时, 只取第一张图片的地址写入索引库.
2, 从文档列表转换为商品列表时可以取一张.
3, 在 jsp 中对列表拆分, 只取一张展示.
方法 2:
可以在 SearchItem 中添加一个 getImages()方法, 用于页面获取经过处理的 pojo 的字段数据:
在 jsp 中取属性:
来源: https://www.cnblogs.com/chenmingjun/p/10002204.html