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下