2 Solr介绍
2.1 什么是solr
Solr是apache的顶级开源项目,它是使用java开发 ,基于lucene的全文检索服务器。
Solr比lucene提供了更多的查询语句,而且它可扩展、可配置,同时它对lucene的性能进行了优化。
Solr是如何实现全文检索的呢?
索引流程:solr客户端(浏览器、java程序)可以向solr服务端发送POST请求,请求内容是包含Field等信息的一个xml文档,通过该文档,solr实现对索引的维护(增删改)
搜索流程:solr客户端(浏览器、java程序)可以向solr服务端发送GET请求,solr服务器返回一个xml文档。
Solr同样没有视图渲染的功能。
2.2 Solr和lucene的区别
Lucene是一个全文检索引擎工具包,它只是一个jar包,不能独立运行,对外提供服务。
Solr是一个全文检索服务器,它可以单独运行在servlet容器,可以单独对外提供搜索和索引功能。Solr比lucene在开发全文检索功能时,更快捷、更方便。
3 Solr安装配置
3.1 下载solr
Solr和lucene的版本是同步更新的,最新的版本是5.2.1
本课程使用的版本:4.10.3
下载地址:http://archive.apache.org/dist/lucene/solr/
下载版本:4.10.3
Linux下需要下载lucene-4.10.3.tgz,windows下需要下载lucene-4.10.3.zip。
3.2 运行环境
l Jdk:1.7及以上
l Solr:4.10.3
l Mysql:5X
l Web服务器:tomcat 7
3.2.1 初始化数据库脚本
3.3 Solr安装配置
3.3.1 Solr的安装部署
第一步:安装tomcat
第二步:将以下的war包,拷贝到tomcat的webapps目录下
第三步:解压缩war包
解压缩之后,将war包删掉
第四步:添加solr的扩展服务包
将以上jar包,添加到以下目录
第五步:添加log4j.properties
将以下目录的文件进行拷贝
复制到以下目录
第六步:在web.xml中指定solrhome的目录
3.3.2 Solrcore的安装
3.3.2.1 Solrcore和solrhome
Solrhome是solr服务运行的主目录,一个solrhome目录里面包含多个solrcore目录,一个solrcore目录里面了一个solr实例运行时所需要的配置文件和数据文件。
每一个solrcore都可以单独对外提供搜索和索引服务。
多个solrcore之间没有关系。
3.3.2.2 Solrcore和solrhome的目录结构
Solrhome的目录结构
Solrcore目录
3.3.2.3 Solrcore的安装
安装solrcore需要先安装solrhome
将以下目录的文件进行拷贝
复制到以下目录
这样solrhome和solrcore就安装成功了。
3.3.2.4 Solrcore配置
在solrcore的conf目录下,有一个solrconfig.xml的配置文件,该配置文件,配置来solrcor的运行信息
在该文件中,主要配置三个标签:lib标签、datadir标签、requestHandler标签
如果对该文件不进行配置也可以,即使用默认的配置项。
3.3.2.4.1 Lib 标签
Solrcore需要添加一个扩展依赖包,通过lib标签来指定依赖包的地址
solr.install.dir:表示solrcore的安装目录
将以下目录的文件进行拷贝
复制到以下目录
修改lib标签
3.3.2.4.2 datadir标签
每个SolrCore都有自己的索引文件目录 ,默认在SolrCore目录下的data中。
data数据目录下包括了index索引目录 和tlog日志文件目录。
如果不想使用默认的目录也可以通过solrConfig.xml更改索引目录 ,如下:
3.3.2.4.3 requestHandler标签
requestHandler请求处理器,定义了索引和搜索的访问方式。
通过/update维护索引,可以完成索引的添加、修改、删除操作。
提交xml、json数据完成索引维护,索引维护小节详细介绍。
通过/select搜索索引。
设置搜索参数完成搜索,搜索参数也可以设置一些默认值,如下:
<requestHandlername="/select" class="solr.SearchHandler">
<!-- 设置默认的参数值,可以在请求地址中修改这些参数-->
<lst name="defaults">
<strname="echoParams">explicit</str>
<intname="rows">10</int><!--显示数量-->
<strname="wt">json</str><!--显示格式-->
<strname="df">text</str><!--默认搜索字段-->
</lst>
</requestHandler>
3.4 solr界面介绍
启动solr服务
3.4.1 Dashboard
仪表盘,显示了该Solr实例开始启动运行的时间、版本、系统资源、jvm等信息。
3.4.2 Logging
Solr运行日志信息
3.4.3 Cloud
Cloud即SolrCloud,即Solr云(集群),当使用SolrCloud模式运行时会显示此菜单,该部分功能在第二个项目,即电商项目会讲解。
3.4.4 Core Admin
Solr Core的管理界面。在这里可以添加SolrCore实例。
3.4.5 java properties
Solr在JVM 运行环境中的属性信息,包括类路径、文件编码、jvm内存设置等信息。
3.4.6 Tread Dump
显示Solr Server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。
3.4.7 Core selector(重点)
选择一个SolrCore进行详细操作,如下:
3.4.7.1 Analysis(重点)
通过此界面可以测试索引分析器和搜索分析器的执行情况。
注:solr中,分析器是绑定在域的类型中的。
3.4.7.2 dataimport
可以定义数据导入处理器,从关系数据库将数据导入到Solr索引库中。
默认没有配置,需要手工配置。
3.4.7.3 Document(重点)
通过/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
通过此菜单可以创建索引、更新索引、删除索引等操作,界面如下:
l overwrite="true" : solr在做索引的时候,如果文档已经存在,就用xml中的文档进行替换
l commitWithin="1000" : solr 在做索引的时候,每个1000(1秒)毫秒,做一次文档提交。为了方便测试也可以在Document中立即提交,</doc>后添加“<commit/>”
3.4.7.4 Query(重点)
通过/select执行搜索索引,必须指定“q”查询条件方可搜索。
3.5 多solrcore的配置
配置多solrcore的好处:
1、 在进行solrcloud的时候,必须配置多solrcore
2、 每个solrcore之间是独立的,都可以单独对外提供服务。不同的业务模块可以使用不同的solrcore来提供搜索和索引服务。
添加
第一步:复制solrhome下的collection1目录到本目录下,修改名称为collection2
第二步:修改solrcore目录下的core.properties
这样多solrcore就配置完成了。
4 Solr的基本使用
4.1 Schema.xml
在schema.xml文件中,主要配置了solrcore的一些数据信息,包括Field和FieldType的定义等信息,在solr中,Field和FieldType都需要先定义后使用。
4.1.1 Filed
定义Field域
<field name="id"type="string" indexed="true" stored="true"required="true" multiValued="false" />
Name:指定域的名称
Type:指定域的类型
Indexed:是否索引
Stored:是否存储
Required:是否必须
multiValued:是否多值,比如商品信息中,一个商品有多张图片,一个Field像存储多个值的话,必须将multiValued设置为true。
4.1.2 dynamicField
动态域
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
Name:指定动态域的命名规则
4.1.3 uniqueKey
指定唯一键
<uniqueKey>id</uniqueKey>
其中的id是在Field标签中已经定义好的域名,而且该域要设置为required为true。
一个schema.xml文件中必须有且仅有一个唯一键
4.1.4 copyField
复制域
<copyField source="cat"dest="text"/>
Source:要复制的源域的域名
Dest:目标域的域名
由dest指的的目标域,必须设置multiValued为true。
4.1.5 FieldType
定义域的类型
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <!-- in this example, we will only use synonyms at query time <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/> --> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> |
Name:指定域类型的名称
Class:指定该域类型对应的solr的类型
Analyzer:指定分析器
Type:index、query,分别指定搜索和索引时的分析器
Tokenizer:指定分词器
Filter:指定过滤器
4.2 中文分词器
使用ikanalyzer进行中文分词
第一步:将ikanalyzer的jar包拷贝到以下目录
第二步:将ikanalyzer的扩展词库的配置文件拷贝到目录
第三步:配置FieldType
第四步:配置使用中文分词的Field
第五步:重启tomcat
4.3 配置业务Field
4.3.1 需求
对京东案例中的products表的数据进行索引,所以需要先定义对应的Field域。
4.3.2 分析配置
Products的表结构
需要往索引库添加的字段有:
pid、name、catalog、catalog_name、price、description、picture
FieldType:
经分析,由于中文分词器已经配置完FieldType,所以目前FieldType已经满足需要,无需配置。
Field:
Pid:
由于pid在products表中是唯一键,而且在solr的shema.xml中已有一个id的唯一键配置,所以不需要再重新定义pid域。
Name:
<!-- 商品名称 -->
<fieldname="product_name" type="text_ik" indexed="true"stored="true"/>
Catalog、catalog_name:
<!-- 商品分类ID -->
<fieldname="product_catalog" type="string"indexed="true" stored="true"/>
<!-- 商品分类名称 -->
<fieldname="product_catalog_name" type="string"indexed="true" stored="false"/>
Price:
<!-- 商品价格 -->
<fieldname="product_price" type="float" indexed="true"stored="true"/>
Description:
<!-- 商品描述 -->
<fieldname="product_description" type="text_ik"indexed="true" stored="false"/>
Picture:
<!-- 商品图片地址 -->
<fieldname="product_picture" type="string"indexed="false" stored="true"/>
4.4 Dataimport
该插件可以将数据库中指定的sql语句的结果导入到solr索引库中。
4.4.1 第一步:添加jar包
l Dataimport的jar包
复制以下目录的jar包
添加到以下目录
修改solrconfig.xml文件,添加lib标签
<libdir="${solr.install.dir:../..}/contrib/dataimporthandler/lib" regex=".*\.jar"/>
l MySQL数据库驱动包
将mysql的驱动包,复制到以下目录
修改solrconfig.xml文件,添加lib标签
<lib dir="${solr.install.dir:../..}/contrib/db/lib"regex=".*\.jar" />
4.4.2 第二步:配置requestHandler
在solrconfig.xml中,添加一个dataimport的requestHandler
4.4.3 第三步:创建data-config.xml
在solrconfig.xml同级目录下,创建data-config.xml
4.4.4 重启tomcat
5 Solrj的使用
5.1 什么是solrj
Solrj就是solr服务器的java客户端。
5.2 环境准备
l Jdk
l Ide
l Tomcat
l Solrj
5.3 搭建工程
l Solrj的依赖包和核心包
l Solr的扩展服务包
5.4 使用solrj完成索引维护
5.4.1 添加/修改索引
在solr中,索引库中都会存在一个唯一键,如果一个Document的id存在,则执行修改操作,如果不存在,则执行添加操作。
5.4.2 删除索引
5.4.2.1 根据指定ID来删除
5.4.2.2 根据条件删除
5.4.3 查询索引
5.4.3.1 简单查询
5.4.3.2 复杂查询
5.4.3.2.1 solr的查询语法
1. q - 查询关键字,必须的,如果查询所有使用*:*。
请求的q是字符串
2. fq - (filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的,例如::
请求fq是一个数组(多个值)
过滤查询价格从1到20的记录。
也可以在“q”查询条件中使用product_price:[1TO 20],如下:
也可以使用“*”表示无限,例如:
20以上:product_price:[20TO *]
20以下:product_price:[*TO 20]
3. sort - 排序,格式:sort=<fieldname>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:
按价格降序
4. start - 分页显示使用,开始记录下标,从0开始
5. rows - 指定返回结果最多有多少条记录,配合start来实现分页。
实际开发时,知道当前页码和每页显示的个数最后求出开始下标。
6. fl - 指定返回那些字段内容,用逗号或空格分隔多个。
显示商品图片、商品名称、商品价格
7. df-指定一个搜索Field
也可以在SolrCore目录 中conf/solrconfig.xml文件中指定默认搜索Field,指定后就可以直接在“q”查询条件中输入关键字。
8. wt - (writer type)指定输出格式,可以有xml, json, php, phps, 后面 solr 1.3增加的,要用通知我们,因为默认没有打开。
9. hl 是否高亮 ,设置高亮Field,设置格式前缀和后缀。
5.4.3.2.2 代码
@Test publicvoid search02() throws Exception { // 创建HttpSolrServer HttpSolrServer server = new HttpSolrServer("http://localhost:8080/solr"); // 创建SolrQuery对象 SolrQuery query = new SolrQuery();
// 输入查询条件 query.setQuery("product_name:小黄人"); // query.set("q", "product_name:小黄人");
// 设置过滤条件 // 如果设置多个过滤条件的话,需要使用query.addFilterQuery(fq) query.setFilterQueries("product_price:[1 TO 10]");
// 设置排序 query.setSort("product_price", ORDER.asc); // 设置分页信息(使用默认的) query.setStart(0); query.setRows(10);
// 设置显示的Field的域集合 query.setFields("id,product_name,product_catalog,product_price,product_picture");
// 设置默认域 query.set("df", "product_keywords");
// 设置高亮信息 query.setHighlight(true); query.addHighlightField("product_name"); query.setHighlightSimplePre("<em>"); query.setHighlightSimplePost("</em>");
// 执行查询并返回结果 QueryResponse response = server.query(query); // 获取匹配的所有结果 SolrDocumentList list = response.getResults(); // 匹配结果总数 long count = list.getNumFound(); System.out.println("匹配结果总数:" + count);
// 获取高亮显示信息 Map<String, Map<String, List<String>>> highlighting = response .getHighlighting(); for (SolrDocument doc : list) { System.out.println(doc.get("id"));
List<String> list2 = highlighting.get(doc.get("id")).get( "product_name"); if (list2 != null) System.out.println("高亮显示的商品名称:" + list2.get(0)); else { System.out.println(doc.get("product_name")); }
System.out.println(doc.get("product_catalog")); System.out.println(doc.get("product_price")); System.out.println(doc.get("product_picture")); System.out.println("====================="); } } |
6 京东案例
6.1 需求
使用Solr实现电商网站中商品信息搜索功能,可以根据关键字、分类、价格搜索商品信息,也可以根据价格进行排序,同时还可以分页。
界面如下:
6.2 分析
6.2.1 UI分析
6.2.2 架构分析
应用服务器服务端:
表现层:使用springmvc接收前台搜索页面的查询条件等信息
业务层:调用dao层完成数据库持久化
如果数据库数据发生变化,调用solrj的客户端同步索引库。
Dao层:使用mybatis完成数据库持久化
Solrj服务器:
提供搜索和索引服务
数据库服务器:
提供数据库服务
6.3 工程搭建
l Solrj的jar包
l Solr的扩展包
l Springmvc的包
6.4 代码实现
6.4.1 Pojo
6.4.2 Service
Service接口
Service实现类
@Service publicclass ProductServiceImpl implements ProductService {
// 依赖注入HttpSolrServer @Autowired private HttpSolrServer server;
@Override public ResultModel getProducts(String queryString, String catalogName, String price, String sort, Integer page) throws Exception { // 创建SolrQuery对象 SolrQuery query = new SolrQuery();
// 输入关键字 if (StringUtils.isNotEmpty(queryString)) { query.setQuery(queryString); } else { query.setQuery("*:*"); }
// 输入商品分类过滤条件 if (StringUtils.isNotEmpty(catalogName)) { query.addFilterQuery("product_catalog_name:" + catalogName); }
// 输入价格区间过滤条件 // price的值:0-9 10-19 if (StringUtils.isNotEmpty(price)) { String[] ss = price.split("-"); if (ss.length == 2) { query.addFilterQuery("product_price:[" + ss[0] + " TO " + ss[1] + "]"); } }
// 设置排序 if ("1".equals(sort)) { query.setSort("product_price", ORDER.desc); } else { query.setSort("product_price", ORDER.asc); }
// 设置分页信息 if (page == null) page = 1;
query.setStart((page - 1) * 20); query.setRows(20);
// 设置默认域 query.set("df", "product_keywords");
// 设置高亮信息 query.setHighlight(true); query.addHighlightField("product_name"); query.setHighlightSimplePre("<font style=\"color:red\" >"); query.setHighlightSimplePost("</font>");
QueryResponse response = server.query(query); // 查询出的结果 SolrDocumentList results = response.getResults(); // 记录总数 long count = results.getNumFound();
List<Products> products = new ArrayList<>(); Products prod;
// 获取高亮信息 Map<String, Map<String, List<String>>> highlighting = response .getHighlighting(); for (SolrDocument doc : results) { prod = new Products();
// 商品ID prod.setPid(doc.get("id").toString());
List<String> list = highlighting.get(doc.get("id")).get( "product_name"); // 商品名称 if (list != null) prod.setName(list.get(0)); else { prod.setName(doc.get("product_name").toString()); }
// 商品价格 prod.setPrice(Float.parseFloat(doc.get("product_price").toString())); // 商品图片地址 prod.setPicture(doc.get("product_picture").toString());
products.add(prod); }
// 封装ResultModel对象 ResultModel rm = new ResultModel(); rm.setProductList(products); rm.setCurPage(page); rm.setRecordCount(count);
int pageCount = (int) (count / 20);
if (count % 20 > 0) pageCount++; // 设置总页数 rm.setPageCount(pageCount);
return rm; } }
|
6.4.3 Controller
6.4.3.1 代码
6.4.3.2 Jsp和静态资源
从资料中拷贝
图片信息放到以下目录
6.4.3.3 Web.xml