Solr 是Apache的顶级开源项目,采用Java开发,基于Lucene的全文搜索服务器。可独立运行在Jetty、Tomcat等Servlet容器中,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr 根据XML 文档添加、删除、更新索引。Solr 搜索只须要发送 HTTP GET 请求,而后对 Solr返回XML 、JSON等格式的查询结果进行解析,组织页面布局。php
(1)Lucene是全文检索引擎工具包,不能独立运行;Solr全文检索引擎,可独立运行前端
(2)Lucene开发工做量大(索引维护、索引性能优化、搜索性能优化);Solr能够快速的构建企业的搜索引擎java
应用:站内搜索mysql
一、目录结构web
bin:solr的运行脚本算法
contrib:solr的一些贡献软件/插件,用于加强solr的功能spring
dist:该目录包含build过程当中产生的war和jar文件,以及相关的依赖文件sql
docs:solr的API文档数据库
example:solr工程的例子目录apache
----solr:包含了默认配置信息的Solr的Core目录
----multicore:包含了在Solr的multicore中设置的多个Core目录
----webapps:包括一个solr.war,该war可做为solr的运行实例工程
licenses:solr相关的一些许可信息
二、运行环境
须要运行在一个Servlet容器中,Solr4.10.3要求jdk使用1.7以上。Solr默认提供Jetty(java)
3.1 SolrHome和SolrCare
复制example/solr到磁盘根目录更名SolrHome(单独的文件夹,不能放到Tomcat下),SolrHome是Solr运行的主目录,包括了运行Solr实例全部的配置文件和数据文件,Solr实例就是
SolrCore,一个SolrHome能够包括多个SolrCore(Solr实例),每一个SolrCore提供单独的搜索和索引服务
SolrHome目录结构:
collection1:SolrCore(Solr实例)目录,SolrCore名称不固定,一个solr运行实例对外单独提供索引和搜索接口。solrHome中能够建立多个solr运行实例SolrCore。一个solr的运行
实例对应一个索引目录
conf:SolrCore的配置文件目录
data:存放索引文件须要建立
第一步:安装tomcat。D:\apache-tomcat-7.0.53
第二步:把solr的war包复制到tomcat 的webapp目录下
把\solr-4.10.3\dist\solr-4.10.3.war复制到D:\apache-tomcat-7.0.53\webapps下。
更名为solr.war
第三步:solr.war解压。使用压缩工具解压或者启动tomcat自动解压。解压以后删除solr.war
第四步:把\solr-4.10.3\example\lib\ext目录下的全部的jar包添加到solr工程中
第五步:配置solrHome和solrCore
(1)建立一个solrhome(存放solr全部配置文件的一个文件夹)。solr-4.10.3\example\solr目录就是一个标准的solrhome
(2)把\solr-4.10.3\example\solr文件夹复制到D:\根目录下,更名为solrhome
(3)在solrhome下有一个文件夹叫作collection1这就是一个solrcore。就是一个solr的实例。一个solrcore至关于mysql中一个数据库。Solrcore之间是相互隔离
1.在solrcore中有一个文件夹叫作conf,包含了索引solr实例的配置信息
2.在conf文件夹下有一个solrconfig.xml。配置实例的相关信息。若是使用默认配置能够不作任何修改
Lib:solr服务依赖的扩展包,默认的路径是collection1\lib文件夹,若是没有就建立一个
dataDir:配置了索引库的存放路径。默认路径是collection1\data文件夹,若是没有data文件夹,会自动建立
requestHandler
第六步 solr服务器配置文件(solrHome)的位置
tomcat\webapps\solr\WEB-INF\web.xml
打开<env-entry>的注释,配置solrHome目录
第七步:启动tomcat
E:\Program\apache-tomcat-7.0.52test\bin\startup.bat
第八步:访问http://localhost:8080/solr
四、Solr后台管理
(1)Analysis:测试索引分析器和搜索分析器的执行状况
(2)Document:建立索引、更新索引、删除索引
/update表示更新索引,solr默认根据id(惟一约束)域来更新Document的内容,若是根据id值搜索不到id域则会执行添加操做,若是找到则更新
(3)Query:经过/select执行搜索索引,必须指定“q”查询条件才能搜索
schema.xml,在SolrCore的solrHome\collection1\conf目录下,数据表配置文件,定义了加入索引的数据的数据类型的。主要包括FieldTypes、Fields和其余的一些缺省设置
域(Filed)的分类
普通域(Filed):string long 等
动态域(DynamicFiled):起到模糊匹配的效果,能够模糊匹配没有定义过的域名
例如:xxxx这个域名没有定义,可是xxxx_s这个域名模糊匹配了*_s这个域,因此至关于xxxx_s这个域定义了
主键域(uniqueKey):<uniqueKey>id</uniqueKey> 通常主键域就用默认的这个就能够不须要更改或者添加
复制域(copyField): 复制域用于查询的时候从多个域中进行查询,这样能够将多个域复制到某一个统一的域中,而后搜索的时候从这个统一的域中进行查询,就至关于从多个域中查询了
安装中文分词器
第一步:IKAnalyzer2012FF_u1.jar导入tomcat/webapps/solr/WEB-INF/lib
第二步:IKAnalyzer的配置文件IKAnalyzer.cfg.xml、停用字典stopword.dic、扩展字典ext.dic都复制到solr的classpath(WEB-INF)新建的classes目录下tomcat/webapps/solr/WEB-INF/classes
第三步:solrHome\collection1\conf下的schema.xml加入
自定义的fieldType,使用中文分析器
<!-- IKAnalyzer Filed Type--> <fieldType name="text_ik" class="solr.TextField"> <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType>
定义field,指定field的type属性为text_ik
<!--IKAnalyzer Field--> <field name="title_ik" type="text_ik" indexed="true" stored="true" /> <field name="content_ik" type="text_ik" indexed="true" stored="false" multiValued="true"/>
第四步:重启Tomcat
访问http://localhost:8080/solr,Analysis便可显示效果
是否存储和是否索引无关, 索引后就能查询,不索引就不能根据这个域搜索。存储后就能取出来里面的内容,不存储就取不出这个域内容
一、维护索引
1.1 单个添加
document中添加
1.2 批量导入
使用dataimport插件
从solr/dist中把solr-dataimporthandler-4.10.3.jar、solr-dataimporthandler-extras-4.10.3.jar、数据库驱动包mysql-connector-java-5.1.7-bin.jar 导入到solrHome\collection1\lib新建的
lib包下
在solrHome\collection1\conf\solrconfig.xml中添加requestHandler
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
在相同目录下新建data-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <dataConfig> <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/solr" user="root" password="root"/> <document> <entity name="product" query="SELECT pid,name,catalog_name,price,description,picture FROM products "> <field column="pid" name="id"/> <field column="name" name="product_name"/> <field column="catalog_name" name="product_catalog_name"/> <field column="price" name="product_price"/> <field column="description" name="product_description"/> <field column="picture" name="product_picture"/> </entity> </document> </dataConfig>
Dataimport ----> Execute,勾选自动刷新,在query中就能够查询到批量导入的数据
二、删除文档
document界面
2.1 删除指定ID的索引
<delete> <id>8</id> </delete> <commit />
2.2 删除查询到的索引数据
<delete> <query>product_catalog_name:幽默杂货</query> </delete> <commit />
2.3 删除全部索引数据
<delete> <query>*:*</query> </delete> <commit />
Query界面
1. q :查询字符串,必须的,若是查询全部使用*:*
2. fq:(filter query)过虑查询,做用:在q查询符合结果中同时是fq查询符合的
过滤查询价格从1到20的记录
// 域名:条件
product_price : [1 TO 20]
能够在“q”查询条件中使用product_price:[1 TO 20]
可使用“*”表示无限
3. sort : 排序
product_price desc/asc
四、start:分页显示使用,开始记录下标,从0开始
五、rows :指定返回结果最多有多少条记录,配合start来实现分页
六、fl :指定返回那些域内容,用逗号或空格分隔多个
七、df:指定一个搜索Field
product_keywords
也能够在SolrCore目录 中conf/solrconfig.xml文件中的SearchHandler指定默认搜索Field,指定后就能够直接在“q”查询条件中输入关键字。
八、wt - (writer type)指定输出格式,能够有 xml, json, php, php
九、hl 是否高亮 ,设置高亮域Field,设置格式前缀和后缀
Solr用于服务器端,SolrJ做为客户端
导包solr-4.10.3\dist\solr-solrj-4.10.3.jar、solrj-lib\*、日志包solr-4.10.3\example\lib\ext\*
一、增删改
没有专门的改,增长的时候改
public class IndexManagerTest { @Test public void testIndexCreate() throws Exception{ //建立和Solr服务端链接 SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr"); //建立solr文档对象 SolrInputDocument doc = new SolrInputDocument(); //域要先定义后使用,还有注意必需要有id主键域 //solr中没有专用的修改方法, 会自动根据id进行查找,若是找到了则删除原来的将新的加入就是修改,若是没找到,将新的直接加入则就是新增 doc.addField("id", "a001"); doc.addField("product_name", "台灯1`111"); doc.addField("product_price", "12.5"); //将文档加入solrServer对象中 solrServer.add(doc); //提交 solrServer.commit(); } @Test public void testIndexDel() throws Exception{ //建立和Solr服务端链接 SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr"); //根据主键id进行删除 //solrServer.deleteById("a001"); //根据查询删除,这里是删除全部*:* solrServer.deleteByQuery("*:*"); //提交 solrServer.commit(); } }
二、查询
public class IndexSearchTest { @Test public void testIndexSearch1() throws Exception{ //链接solr服务端 SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr"); //建立solr查询条件对象 SolrQuery solrQuery = new SolrQuery(); //查询全部 solrQuery.setQuery("*:*"); //查询并获取查询响应对象 QueryResponse queryResponse = solrServer.query(solrQuery); //从查询响应中获取查询结果集对象 SolrDocumentList results = queryResponse.getResults(); //打印一共查询到多少条记录,也就是记录总数 System.out.println("=====count====" + results.getNumFound()); //遍历查询结果集 for(SolrDocument doc : results){ System.out.println("============="+doc.get("id")); System.out.println("============="+doc.get("product_name")); System.out.println("============="+doc.get("product_price")); System.out.println("===================================================="); } } @Test public void testIndexSearch2() throws Exception{ //链接solr服务端 SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr"); //建立solr查询条件对象 SolrQuery solrQuery = new SolrQuery(); //查询关键字输入 solrQuery.setQuery("台灯"); //设置默认搜索域 solrQuery.set("df", "product_keywords"); //设置过滤查询 solrQuery.addFilterQuery("product_price:[1 TO 100]"); //设置排序,这里是降序 solrQuery.setSort("product_price", ORDER.desc); //=======设置分页======== //设置起始条数 solrQuery.setStart(0); //设置查询多少条 solrQuery.setRows(50); //========设置高亮显示======= //高亮默认是关闭的,因此要手动开启 solrQuery.setHighlight(true); //设置须要高亮显示的域 solrQuery.addHighlightField("product_name"); //设置高亮前缀 solrQuery.setHighlightSimplePre("<span style=\"color:red\">"); //设置高亮后缀 solrQuery.setHighlightSimplePost("</span>"); //===================查询并获取查询响应对象===================================== QueryResponse queryResponse = solrServer.query(solrQuery); //从查询响应中获取查询结果集对象 SolrDocumentList results = queryResponse.getResults(); //打印一共查询到多少条记录,也就是记录总数 System.out.println("=====count====" + results.getNumFound()); //遍历查询结果集 for(SolrDocument doc : results){ System.out.println("============="+doc.get("id")); //获取高亮 Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); List<String> list = highlighting.get(doc.get("id")).get("product_name"); if(list != null && list.size() > 0){ String hlName = list.get(0); System.out.println("=======high lighting=====" + hlName); } System.out.println("============="+doc.get("product_name")); System.out.println("============="+doc.get("product_price")); System.out.println("===================================================="); } } }
1.solr是一个全文检索引擎系统,经过部署到tomcat下就能够独立运行,经过http协议对外提供全文检索服务,就是索引和文档的正删改查服务
2. solr直接操做索引库和文档库, 咱们的业务系统中可使用solrJ(solr的客户端,就是一堆jar包)来调用solr服务端,让solr服务端操做文档库和索引库,完成正删改查的任务,将结果返回
给solrJ客户端,咱们在业务系统中就能够,获取到结果真后返回给客户在浏览器中显示
3. solrHome:solrhome就是solr最核心的目录, 一个solrhome中能够有多个solr实例
4. solrCore:一个solrCore就是一个solr实例,solr中实例与实例之间他们的索引库和文档库是相互隔离的。每一个实例对外单独的提供索引和文档的增删改查服务,默认实例collection1
5. 文档和索引的增长和修改必需要有id, 主键域,没有会报错
6. 域名和类型必须先定义后使用,若是没有定义就使用会报错
7. 域的分类
普通域:string long 等
动态域:起到模糊匹配的效果,能够模糊匹配没有定义过的域名
例如:xxxx这个域名没有定义,可是xxxx_s这个域名模糊匹配了*_s这个域,因此至关于xxxx_s这个域定义了
主键域:<uniqueKey>id</uniqueKey> 通常主键域就用默认的这个就能够不须要更改或者添加
复制域: 复制域用于查询的时候从多个域中进行查询,这样能够将多个域复制到某一个统一的域中,而后搜索的时候从这个统一的域中进行查询,就至关于从多个域中查询了
6.是否存储和是否索引无关, 索引后就能查询,不索引就不能根据这个域搜索,,存储后就能取出来里面的内容,不存储就取不出这个域内容
7. 通常企业中将数据所有放入数据库中, 因为查询的时候须要使用like模糊查询,模糊查询数据库中使用的是全表扫描算法,这样效率低级,因此须要使用全文检索,来优化查询速度.
一、系统架构
二、导包
SpringMVC包、solrJ的包、日志包example/lib/*
2.1 web.xml
前端控制器、POST乱码
<!-- 1.配置前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 指定springmvc配置文件的路径 若是不指定默认为:/WEB-INF/${servlet-name}-servlet.xml --> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> <!-- 2.解决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> </web-app>
2.2 SpringMVC.xml
包扫描、注解驱动、视图解析器、SolrServer
<!-- 1.包扫描,controller、service、dao所有扫描--> <context:component-scan base-package="com.guojie"/> <!-- 2.配置注解驱动,若是配置此标签能够不用配置处理器映射器和适配器 --> <mvc:annotation-driven/> <!-- 3.配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 4.SolrServer的配置 --> <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer"> <!-- index=0表明调用有一个构造参数的solrServer的构造方法 --> <constructor-arg index="0" value="http://localhost:8080/solr"/> </bean>
三、POJO
// 商品对象模型 public class ProductModel { // 商品编号 private String pid; // 商品名称 private String name; // 商品分类名称 private String catalog_name; // 价格 private float price; // 商品描述 private String description; // 图片名称 private String picture; }
// 返回值对象模型 public class ResultModel { // 商品列表 private List<ProductModel> productList; // 商品总数 private Long recordCount; // 总页数 private int pageCount; // 当前页 private int curPage; }
四、DAO
功能:接收Service层传过来的参数,根据参数查询索引库,返回查询结果
参数:SolrQuery对象
返回值:一个商品列表List<ProductModel>,还须要返回查询结果的总数量
返回:ResultModel
方法:ResultModel queryProduct(SolrQuery query) throws Exception
@Repository public class ProductDaoImpl implements ProductDao { @Autowired private SolrServer solrServer; @Override public ResultModel queryProducts(SolrQuery solrQuery) throws Exception { //查询并获取查询响应 QueryResponse queryResponse = solrServer.query(solrQuery); //从响应中获取查询结果集 SolrDocumentList docList = queryResponse.getResults(); //建立返回结果对象 ResultModel resultModel = new ResultModel(); List<ProductModel> productList = new ArrayList<ProductModel>(); //遍历结果集 if(docList != null){ //获取总记录数 resultModel.setRecordCount(docList.getNumFound()); for(SolrDocument doc : docList){ ProductModel product = new ProductModel(); product.setPid(String.valueOf(doc.get("id"))); //获取高亮 Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); if(highlighting != null){ List<String> list = highlighting.get(doc.get("id")).get("product_name"); // 判断list不为空,且list的大小不为0 if(list != null && list.size() > 0){ product.setName(list.get(0)); } else { product.setName(String.valueOf(doc.get("product_name"))); } } else { product.setName(String.valueOf(doc.get("product_name"))); } // String不为NULL且不为"",Java中没有sizeof if(doc.get("product_price") != null && !"".equals(doc.get("product_price"))){ product.setPrice(Float.valueOf(doc.get("product_price").toString())); } product.setCatalog_name(String.valueOf(doc.get("product_catalog_name"))); product.setPicture(String.valueOf(doc.get("product_picture"))); productList.add(product); } resultModel.setProductList(productList); } return resultModel; } }
五、Service
封装查询条件
功能:接收action传递过来的参数,根据参数拼装一个查询条件,调用dao层方法,查询商品列表。接收返回的商品列表和商品的总数量,根据每页显示的商品数量计算总页数
参数:
1.查询条件:字符串
2.商品分类的过滤条件:商品的分类名称,字符串
3.商品价格区间:传递一个字符串,知足格式:“0-100、101-200、201-*”
4.排序条件:页面传递过来一个升序或者降序就能够,默认是价格排序。0:升序1:降序
5.分页信息:每页显示的记录条数建立一个常量60条。传递一个当前页码就能够了
业务逻辑:
1.根据参数建立查询对象
2.调用dao执行查询
3.根据总记录数计算总页数
返回值:ResultModel
方法定义:ResultModel queryProduct(String queryString, String caltalog_name, String price,String sort, Integer page) throws Exception;
@Service public class ProductServiceImpl implements ProductService { private static final Integer PAGE_SIZE = 60; @Autowired private ProductDao productDao; @Override public ResultModel query(String queryString, String catalog_name, String price, String sort, Integer page) throws Exception { //建立查询条件对象 SolrQuery solrQuery = new SolrQuery(); //设置默认搜索域 solrQuery.set("df", "product_keywords"); //设置查询关键字 if(queryString != null && !"".equals(queryString)){ solrQuery.setQuery(queryString); } else { solrQuery.setQuery("*:*"); } //设置过滤条件按照分类名称进行过滤 if(catalog_name != null && !"".equals(catalog_name)){ solrQuery.addFilterQuery("product_catalog_name:" + catalog_name); } //设置过滤条件按照价格进行过滤 if(price != null && !"".equals(price)){ String[] split = price.split("-"); if(split != null && split.length > 1){ solrQuery.addFilterQuery("product_price:["+split[0]+" TO "+split[1]+"]"); } } //设置排序 if("1".equals(sort)){ solrQuery.addSort("product_price", ORDER.asc); } else { solrQuery.addSort("product_price", ORDER.desc); } //设置分页 if(page == null){ page = 1; } Integer start = (page - 1) * PAGE_SIZE; //从第几天记录开始查 solrQuery.setStart(start); //每页显示多少条 solrQuery.setRows(PAGE_SIZE); //设置高亮显示 solrQuery.setHighlight(true); //设置高亮显示的域 solrQuery.addHighlightField("product_name"); //设置高亮前缀 solrQuery.setHighlightSimplePre("<span style=\"color:red\">"); //设置高亮后缀 solrQuery.setHighlightSimplePost("</span>"); //查询返回结果 ResultModel resultModel = productDao.queryProducts(solrQuery); resultModel.setCurPage(Long.parseLong(page.toString())); //计算总页数 Long pageCount = resultModel.getRecordCount() / PAGE_SIZE; if(resultModel.getRecordCount() % PAGE_SIZE > 0){ pageCount ++; } resultModel.setPageCount(pageCount); return resultModel; } }
六、Controller
功能:接收页面传递过来的参数调用service查询商品列表。将查询结果返回给jsp页面,还须要查询参数的回显
参数
1.查询条件:字符串
2.商品分类的过滤条件:商品的分类名称,字符串
3.商品价格区间:传递一个字符串,知足格式:“0-100、101-200、201-*
4.排序条件:页面传递过来一个升序或者降序就能够,默认是价格排序。0:升序1:降序
5.分页信息:每页显示的记录条数建立一个常量60条。传递一个当前页码就能够了
6.Model:至关于request
返回结果:String类型,就是一个jsp的名称
方法:String queryProduct(String queryString, String caltalog_name, String price, String sort, Integer page, Model model) throws Exception;
@Controller public class ProductsController { @Autowired private ProductService productService; @RequestMapping("/list") public String list(String queryString, String catalog_name, String price, String sort, Integer page, Model model) throws Exception{ ResultModel result = productService.query(queryString, catalog_name, price, sort, page); //返回查询结果 model.addAttribute("result", result); model.addAttribute("queryString", queryString); model.addAttribute("catalog_name", catalog_name); model.addAttribute("price", price); model.addAttribute("sort", sort); return "product_list"; } }
Java Project编译后的文件在bin下
Web项目编译后的文件在classes下