1. solr介绍
solr是apache开源项目,基于lucene,并默认运行在jetty这个serlvet容器中的优秀的全文检索服务器。可通过http请求实现索引和搜索功能。
solr 和lucene区别
lucene是一个全文检索的工具包,不能够进行独立的部署,对外提供服务,必须嵌入到系统中,高度耦合依托于系统,并提供搜索服务。
solr 是一个全文检索的搜索引擎(服务器/软件系统),能够独立部署,可以对外单独提供搜索服务。
solr 可以降低耦合度,lucene不可以。
下载地址:
Solr官方网站:
http://lucene.apache.org/solr/
下载地址:
http://archive.apache.org/dist/lucene/solr/
解压后的目录结构:
example下的目录结构:
1. tomcat整合solr运行
第一步:解压
1. 在D盘新建一个文件夹solr,将一个新的tomcat与solr工程解压到该文件夹
2. 在解压后的solr-4.10.3\example\webapps目录下拷贝solr.war到tomcat的webapp目录下解压,为方便统一管理,解压到solr的文件夹。解压完毕删除war包
第二步:拷贝jar包
3. 拷贝D:\solr\solr-4.10.3\example\lib\ext下的所有jar包到D:\solr\apache-tomcat-7.0.52\webapps\solr\WEB-INF\lib目录下
第三步:solrhome
4. 在solr目录下新建文件夹solrhome。
概念:SolrHome是Solr运行的主目录,目录中包括了运行Solr实例所有的配置文件和数据文件,Solr实例就是SolrCore。example\solr下就是一个solr home目录结构。
拷贝D:\solr\solr-4.10.3\example\solr下的所有目录到D:\solr\solrhome下,完成一个solrCore的创建。可以在该目录下创建多个实例
5. 最后,修改D:\solr\apache-tomcat-7.0.52\webapps\solr\WEB-INF,告诉它solrhome的位置
到D:\solr\apache-tomcat-7.0.52\bin中启动tomcat,访问http://localhost:8080/solr,能够成功访问代表搭建成功
如果默认开启的tomcat不是当前路径下的tomcat,删除环境变量里的CATALINA_HOME的配置
1.1 新建solrcore
添加solrcore:
第一步:在D:\solr\solrhome复制collection1改名为collection2
第二步:修改core.properties。设置name=collection2
第三步:重启tomcat
1.2 使用IKAnalyzer中文分析器。
第一步:把IKAnalyzer2012FF_u1.jar添加到tomcat中的solr/WEB-INF/lib目录下
第二步:复制IKAnalyzer的配置文件和自定义词典和停用词词典到solr项目的classpath下。
在WEB-INF下如果没有classes 需要创建一个。
第三步:在schema.xml中添加一个自定义的fieldType,使用中文分析器。
第四步:定义field,指定field的type属性为text_ik
第五步:重启tomcat,在analyze下输入中文进行测试
2. Solr管理索引库
2.1 维护索引
1. 添加单个文档(略)
2. 批量导入数据
使用dataimport插件批量导入数据。如果collection1下没有lib 需要自己创建一个。
第一步:在D:\solr\solr-4.10.3\dist目录下把dataimport插件依赖的jar包添加到solrcore(collection1\lib)中,没有目录就新建目录
因为要批量根据数据库的数据创建索引,因此还需要导入数据库驱动
第二步:配置collection1\conf\下的solrconfig.xml文件,添加一个requestHandler。
data-config.xml
第三步:在同级目录下创建一个data-config.xml,保存到collection1\conf\目录下
3. 上面name对应的是field域,自定义的域要自己在schema.xml中配置
如果不使用Solr提供的Field可以针对具体的业务需要自定义一套Field,如下是商品信息Field:
4. 重启tomcat,完成批量索引的创建
2.2 删除文档
删除索引格式如下:
1) 删除指定ID的索引
8
需要加上
2) 删除查询到的索引数据
product_catalog_name:幽默杂货
3) 删除所有索引数据
*:*
2.3 查询索引
通过/select搜索索引,Solr制定一些参数完成不同需求的搜索:
1. q - 查询字符串,必须的,如果查询所有使用*:*。
2. fq - (filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的
也可以在“q”查询条件中使用product_price:[1 TO 20]
也可以使用“*”表示无限,例如:
20以上:product_price:[20 TO *]
20以下:product_price:[* TO 20]
3. sort - 排序
product_price desc/asc
4. start - 分页显示使用,开始记录下标,从0开始
5. rows - 指定返回结果最多有多少条记录,配合start来实现分页。
6. fl - 指定返回那些字段内容,用逗号或空格分隔多个
7. df-指定一个默认搜索Field
8. wt - (writer type)指定输出格式,可以有 xml, json, php, phps,
9. hl 是否高亮 ,设置高亮Field,设置格式前缀和后缀
3. 使用soloJ管理索引库
solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,SolrJ通常在嵌入在业务系统中,通过SolrJ的API接口操作Solr服务,如下图:
3.1 依赖的jar包
依赖solrj及solrj依赖包.
以及lib下的扩展依赖包
3.1 使用solrj添加文档
第一步:创建一个java工程
第二步:导入jar包。包括solrJ的jar包,依赖包,扩展包(如上图所示)
代码开发步骤:
第三步:和Solr服务器建立连接。HttpSolrServer对象建立连接。
第四步:创建一个SolrInputDocument对象,然后添加域。
第五步:将SolrInputDocument添加到索引库。
第六步:提交。
//向索引库中添加索引
@Test
public void addDocument() throws Exception {
//和solr服务器创建连接
//参数:solr服务器的地址
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//创建一个文档对象
SolrInputDocument document = new SolrInputDocument();
//向文档中添加域
//第一个参数:域的名称,域的名称必须是在schema.xml中定义的
//第二个参数:域的值
document.addField("id", "c0001");
document.addField("title_ik", "使用solrJ添加的文档");
document.addField("product_name", "商品名称");
//把document对象添加到索引库中
solrServer.add(document);
//提交修改
solrServer.commit();
}
3.2 删除文档
根据id删除
//删除文档,根据id删除
@Test
public void deleteDocumentByid() throws Exception {
//创建连接
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//根据id删除文档
solrServer.deleteById("c0001");
//提交修改
solrServer.commit();
}
根据查询删除
//根据查询条件删除文档
@Test
public void deleteDocumentByQuery() throws Exception {
//创建连接
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//根据查询条件删除文档
solrServer.deleteByQuery("*:*");
//提交修改
solrServer.commit();
}
3.3 修改文档
在solrJ中修改没有对应的update方法,只有add方法,只需要添加一条新的文档,和被修改的文档id一致就可以修改了。
本质上就是先删除后添加。
@Test
public void testUpdate() throws IOException, SolrServerException {
//连接SolrServer服务器
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//获取一个文档对象,添加域
SolrInputDocument document = new SolrInputDocument();
document.addField("id","002");
document.addField("name","testtest22222");
//更新
solrServer.add(document);
//提交修改
solrServer.commit();
}
4. 查询文档
4.1 简单查询
//查询索引
@Test
public void queryIndex() throws Exception {
//创建连接
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//创建一个query对象
SolrQuery query = new SolrQuery();
//设置查询条件
query.setQuery("*:*");
//执行查询
QueryResponse queryResponse = solrServer.query(query);
//取查询结果
SolrDocumentList solrDocumentList = queryResponse.getResults();
//共查询到商品数量
System.out.println("共查询到商品数量:" + solrDocumentList.getNumFound());
//遍历查询的结果
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("product_name"));
System.out.println(solrDocument.get("product_price"));
System.out.println(solrDocument.get("product_catalog_name"));
System.out.println(solrDocument.get("product_picture"));
}
}
4.2 复杂查询
@Test
//其中包含查询、过滤、分页、排序、高亮显示等处理。
public void testQuery2() throws SolrServerException {
//获取SolrServer连接
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
//获取查询对象
SolrQuery query = new SolrQuery();
//设置查询条件
//query.set("q","id:1");
//query.setQuery("1");
query.setQuery("情侣");
//设置过滤条件
query.setFilterQueries("product_catalog_name:幽默杂货");
//排序(按价格从低到高排序)
query.addSort("product_price", SolrQuery.ORDER.asc);
//分页处理
query.setStart(0);
query.setRows(20);
//结果中显示域的列表
query.setFields("id", "product_name", "product_price", "product_catalog_name", "product_picture");
//设置默认搜索域
query.set("df", "product_name");//这里以商品名称作为默认搜索域
//高亮显示,开启高亮开关
query.setHighlight(true);
//高亮显示的域
query.addHighlightField("product_keywords");
//高亮显示的前缀
query.setHighlightSimplePost("");
//高亮显示的后缀
query.setHighlightSimplePre("");
//执行查询
QueryResponse queryResponse = solrServer.query(query);
//获取查询的结果集
SolrDocumentList solrDocumentList = queryResponse.getResults();
//供查询到商品数量
System.out.println(solrDocumentList.getNumFound());
//遍历结果集
for (SolrDocument document : solrDocumentList) {
//高亮显示的存储位置不同,取出高亮显示的商品名
Map>> highlighting = queryResponse.getHighlighting();
//以id存在大Map中,再根据高亮显示的域从小Map中取出高亮显示的List
List list = highlighting.get(document.get("id")).get(document.get("product_name"));
//判断是否有高亮内容
String product_name = null;
if (list != null) {//有高亮显示内容
product_name = list.get(0);
} else {//没有,就从document里面取
product_name = (String) document.get("product_name");
}
System.out.println(product_name);
System.out.println(document.get("id"));
System.out.println(document.get("product_price"));
System.out.println(document.get("product_catalog_name"));
System.out.println(document.get("product_picture"));
}
}
4. solr案例:模拟京东站内搜索
4.1 使用上面已经创建的索引库
4.2 搭建java程序环境
第一步:创建一个web工程导入jar包
1、springmvc的相关jar包
2、solrJ的核心包和依赖包
3、Example\lib\ext下的jar包
4、拷贝静态资源到工程项目webContent下
分析,此案例暂时不用到持久化相关的比如mybatis相关的配置项。而是使用solr索引库
所以可以使用springmvc.xml作为spring容器进行配置,因为springmvc是spring的子容器。
第二步:配置文件与包结构的创建
包结构:
web.xml:
encoding
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
encoding
/*
jd-springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/springmvc.xml
1
jd-springmvc
*.action
--------------------------------------------------------------------------------
springmvc.xml:
第二步:Pojo:
商品对象模型:
public class ProductModel {
// 商品编号
private Integer pid;
// 商品名称
private String name;
// 商品分类名称
private String catalog_name;
// 价格
private Float price;
// 图片名称
private String picture;
}
返回值对象模型
public class ResultModel {
// 商品列表
private List productList;
// 商品总数
private Long recordCount;
// 总页数
private Integer pageCount;
// 当前页
private Integer curPage;
// 每页的行数
private Integer rows;
}
查询条件对象QueryVo:
public class QueryVo implements Serializable {
private String queryString;//关键字搜索
private String catalog_name;//分类搜索
private String price;//价格区间搜索
private String sort;//排序
private Integer page;//当前页
private Integer rows;//每页显示条数
}
环境搭建完成。
4.2 代码开发
从dao层开始开发:
功能:接收service层传递过来的参数,根据参数查询索引库,返回查询结果。
参数:SolrQuery对象
返回值:一个商品列表List,还需要返回查询结果的总数量。
返回:ResultModel
方法定义:ResultModel search (SolrQuery query) throws Exception;
SearchDao:
/**
* 根据查询条件进行搜索
*
* @param solrQuery
* @return
*/
ResultModel search(SolrQuery solrQuery) throws Exception;
SearchDaoImpl :
@Repository
public class SearchDaoImpl implements SearchDao {
@Autowired
private SolrServer solrServer;
@Override
public ResultModel search(SolrQuery solrQuery) throws Exception {
//1. 连接SolrServer索引库服务器,交由Spring管理
//2. 根据查询条件执行查询
QueryResponse queryResponse = solrServer.query(solrQuery);
//根据查询条件获取结果集
SolrDocumentList solrDocumentList = queryResponse.getResults();
//获取高亮内容
Map>> highlighting = queryResponse.getHighlighting();
//构建ResultModel中的商品列表的ProductList集合
List productList = new ArrayList<>();
ProductModel productModel = null;
//遍历结果集
for(SolrDocument document : solrDocumentList) {
productModel = new ProductModel();
//获取高亮
List list = highlighting.get(document.get("id")).get("product_name");
String product_name = null;
if(list != null && list.size() >0) { //有高亮
product_name = list.get(0);
}else {
product_name = (String) document.get("product_name");
}
//获取ProductModel的其他内容
//获取id
Integer id = null;
Object idObj = document.get("id");
if(idObj != null) {
id = Integer.parseInt(idObj.toString());
}
//获取分类名称
String product_catalog_name = (String)document.get("product_catalog_name");
//获取价格
Float product_price = (Float) document.get("product_price");
//获取图片名称
String product_picture = (String) document.get("product_picture");
//设置到ProductModel中
productModel.setPid(id);
productModel.setName(product_name);
productModel.setCatalog_name(product_catalog_name);
productModel.setPrice(product_price);
productModel.setPicture(product_picture);
//将该ProductModel添加到List中
productList.add(productModel);
}
//3. 构建ResultModel实例将查询结果设置进去
ResultModel model = new ResultModel();
//设置总页数
//设置当前页
//设置每页的行数
//-------以上在service层设置----------
//设置商品列表
model.setProductList(productList);
//设置商品总数
model.setRecordCount(solrDocumentList.getNumFound());
return model;
}
}
---------------------------------------------------------------------------
ProductService :
public interface ProductService {
/**
* 根据查询条件查询ResultMoedl
*
* @param vo
* @return
*/
ResultModel getResultModelByQuery(QueryVo vo) throws Exception;
}
ProductServiceImpl 实现类:
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private SearchDao searchDao;
@Override
public ResultModel getResultModelByQuery(QueryVo vo) throws Exception {
//构建查询条件,封装查询条件
SolrQuery solrQuery = new SolrQuery();
//判断queryString是否为空
if (!StringUtils.isEmpty(vo.getQueryString())) {
//根据关键字搜索
solrQuery.setQuery(vo.getQueryString());
} else {
solrQuery.setQuery("*:*");
//solrQuery.set("q", "*:*");
}
//判断分类搜索条件是否为空
if (!StringUtils.isEmpty(vo.getCatalog_name())) {
//分类搜索不为空
solrQuery.setFilterQueries("product_catalog_name:" + vo.getCatalog_name());
}
//判断价格搜索
if (!StringUtils.isEmpty(vo.getPrice())) {
//价格搜索不为空
String[] split = vo.getPrice().split("-");
solrQuery.setFilterQueries("product_price:[" + split[0] + " TO " + split[1] + "]");
}
//判断排序
String sort = vo.getSort();
if (!StringUtils.isEmpty(sort)) {
if ("0".equals(sort)) {//升序
solrQuery.addSort("product_price", SolrQuery.ORDER.asc);
} else {//降序
solrQuery.addSort("product_price", SolrQuery.ORDER.desc);
}
}
//指定默认搜索域
solrQuery.set("df", "product_keywords");
//设置分页条件查询
Integer page = vo.getPage();//当前页
Integer rows = vo.getRows();//每页显示条数
//设置第几条开始查询
Integer start = (page - 1) * rows;
solrQuery.setStart(start);
//设置差多少条
solrQuery.setRows(rows);
//开启高亮
solrQuery.setHighlight(true);
//指定高亮显示的域
solrQuery.addHighlightField("product_name");
//设置高亮显示文本的前缀
solrQuery.setHighlightSimplePre("");
//设置高亮显示文本的后缀
solrQuery.setHighlightSimplePost("");
//-------查询条件封装完毕:包括默认域查询,过滤查询,排序,分页-------
//调用dao查询结果,获取结果模型
ResultModel resultModel = searchDao.search(solrQuery);
//------在service层设置---------
//设置当前页
resultModel.setCurPage(page);
//设置每页的行数
resultModel.setRows(rows);
//设置总页数
Long recordCount = resultModel.getRecordCount();//总记录数
Long pageCount = (recordCount + rows - 1) / rows;
resultModel.setPageCount(pageCount);
return resultModel;
}
}
@Controller
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("list.action")
public String listByQuery(QueryVo vo, Model model) {
try {
//如果页面未传值,给一定的值给定默认值
if(vo.getPage() == null) {
vo.setPage(1);
}
if(vo.getRows() == null) {
vo.setRows(16);
}
if(vo.getSort() == null) {
vo.setSort("1");
}
ResultModel resultModel = productService.getResultModelByQuery(vo);
//添加数据,根据页面确定key
model.addAttribute("result",resultModel);
//回显查询条件,也是根据页面确定key
model.addAttribute("queryString",vo.getQueryString());
model.addAttribute("catalog_name",vo.getCatalog_name());
model.addAttribute("price",vo.getPrice());
model.addAttribute("page",vo.getPage());
model.addAttribute("sort",vo.getSort());
} catch (Exception e) {
e.printStackTrace();
}
return "product_list";
}
}
-----
代码开发完成
4.3 注意的问题
1. solr启动的tomcat可能会与程序运行的tomcat冲突,在solr启动的tomcat中修改端口号,我这里修改了8090.
要注意的是:这里修改之后,springmvc.xml中配置的solrServer连接服务器的配置记得更改:指定到具体的solrCore实例,否则会报错:
Expected mime type application/octet-stream but got text/html
springmvc.xml配置:
2. 使用价格条件筛选的时候,格式是 product_pric:[10 TO *],闭括号代表包括,大括号代表不包括,星号代表无穷大或无穷小。
要注意的是:条件拼接要注意 TO 的两边要有空格
展示效果: