轉載請注明出處:http://www.cnblogs.com/zhuxiaojie/p/5764680.html
本教程基於solr5.5
前言
至於為什么要用solr5.5,因為最新的6.10,沒有中文的分詞器支持,這里使用的是ik分詞器,剛好支持到5.5
ik分詞器下載地址 :https://github.com/EugenePig/ik-analyzer-solr5 , 下載完之后使用maven命令, mvn package 即可生成jar文件,或者下載我編譯好的 http://pan.baidu.com/s/1o7P0846
solr5.5下載地址: http://apache.fayea.com/lucene/solr/5.5.2/
tomcat8下載,下載地址就不說了
一:安裝配置
下載完成之后,解壓solr文件,解壓tomcat
1.1 在tomcat安裝solr,並且建立solrCore
- 把solr5.5目錄下的server/solr-webapp/webapp 重命名為solr,並且放置到tomcat/webapp的目錄下。
- 打開tomcat/webapp/solr/WEB-INF/web.xml
- 新建一個文件夾,不要中文目錄,用來做solrHome,也就是solrCore的實例存放位置
- 在tomcat/webapp/solr/WEB-INF/web.xml中配置solr的地址
-
在tomcat/webapp/solr/WEB-INF/文件夾中,建立classes目錄
- 把solr5.5/server/resource/log4j.properties 復制到上一步建立的classes目錄中
- 把solr5.5/server/lib/ext/目錄下的所有jar文件復制到tomcat/webapp/solr/WEB-INF/lib/中,這是一些日志用的jar包,不然啟動報錯。
- 這個時候,可以輸入http://127.0.0.1:8080/solr/admin.html來訪問到solr的控制界面了。
- 接下來就是創建solrCore。
- 目前solrHome目錄是空的,我們創建一個空文件夾core1,這個就是我們的一個實例,然后把solr5.5/server/solr/configsets/sample_techproducts_configs/conf/ 這個文件復制到solrHome/core1中。
- 把solr5.5/server/solr/solr.xml復制到solrHome目錄下。
- 在solr的管理控制台界面,添加一個core1
- 這下就創建成功了一個實例core1,yge 請注意我打碼的部分,需要先執行第11步操作,否則的話,會無法創建solr core,也就是會有錯誤信息,這是solr的一個bug,但是至今沒有修復。
1.2 安裝ik中文分詞器
- 准備好ik分詞器的jar包,可以自己編譯,也可以下載我生成的。然后把它復制到tomcat/webapp/solr/WEB-INF/lib里面。(千萬不要復制到tomcat/lib中,這樣會找不到lucene的類)
- 打開solrHome/core1/conf/managed-schema文件,在最下方,追加如下配置
-
<fieldType name="text_ik" class="solr.TextField"> <analyzer type="index" useSmart="false" class="org.wltea.analyzer.lucene.IKAnalyzer" /> <analyzer type="query" useSmart="true" class="org.wltea.analyzer.lucene.IKAnalyzer" /> </fieldType>
- 啟動tomcat,即可看到text_ik分詞
1.3 插入的文檔必須與域相匹配
域,我個人也稱它為字段,它在solr中有特定的含義,就類似數據庫中表的列一樣,規范着寫入的數據,我們先來做個例子。
可以看到,我這次插入的文檔,有id,title當然,在solr中,每一條記錄都必須有着一個唯一的id,它就類似數據庫中的主鍵,不可重復。這條記錄的插入是成功的。
但是,如果我把title改成title1,這就與定義的字段不一樣了,就會報錯,如下圖所示
可以看到,這里提示,未知的字段 title1.
1.4 域的定義 field
先拿出一條配置來看一下
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
認識一下這些屬性
name:域名
type:域的類型,必須匹配類型,不然會報錯
indexed:是否要作索引
stored:是否要存儲
required:是否必填,一般只有id才會設置
multiValued:是否有多個值,如果設置為多值,里面的值就采用數組的方式來存儲,比如商品圖片地址(大圖,中圖,小圖等)
1.5 配置動態域 dynamicField
同樣的,也先拿出一條來看看
<dynamicField name="*_i" type="string" indexed="true" stored="true" multiValued="true" />
何謂動態域呢?就是這個域的名稱,是由表達式組成的,只要名稱滿足了這個 表達式,就可以用這個域
同樣的認識一下這些屬性
name:域的名稱,該域的名稱是通過一個表達式來指定的,只要符合這這個規則,就可以使用這個域。比如 aa_i,bb_i,13_i等等,只要滿足這個表達式皆可
type:對應的值類型,相應的值必須滿足這個類型,不然就會報錯
indexed:是否要索引
stored:是否要存儲
...其它的屬性與普通的域一至
1.6 主鍵域 uniqueKey
給出一條配置
<uniqueKey>id</uniqueKey>
指定一個唯一的主鍵,每一個文檔中,都應該有一個唯一的主鍵,這個值不要隨便改
1.7 復制域 copyField
給出一條配置
<copyField source="cat" dest="text"/>
說明一下相應的屬性
source:源域
dest:目標域
復制域,將源域的內容復制到目標域中
注意:目標域必須是允許多值的,如下,nultiValued必須為true,因為可能多個源域對應一個目標域,所以它需要以數組來存儲
<field name="text" type="string" indexed="true" stored="true" multiValued="true"/>
1.8 域的類型 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:過濾器
1.9 業務字段的實際配置
經過上面的學習,差不多了解了一些常用的配置,如今我們用field來配置實際的業務字段,有屬性如下
當然,中文分詞還是要用的,因為我們在前面的 1.2 章節中,已經配置了一個fieldType的中文分詞,所以我們現在一律用中文分詞的域類型
主鍵的id就不需要配置了,默認已經把id配置為主鍵了,默認的配置如下
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
商品名稱(需要分詞,需要存儲)
<field name="name" type="text_ik" indexed="true" stored="true" />
商品分類(不需要分詞,需要存儲)
<field name="catalog" type="int" indexed="false" stored="true" />
商品分類名稱(需要分詞,需要存儲)
<field name="catalog_name" type="text_ik" indexed="true" stored="true" />
商品價格(不分詞,需要存儲)
<field name="price" type="double" indexed="false" stored="true" />
商品描述(需要分詞,不需要存儲)
<field name="description" type="text_ik" indexed="true" stored="false" />
商品圖片(不需要分詞,需要存儲)
<field name="picture" type="string" indexed="false" stored="true" />
復制域的應用
前面我們了解了復制域,但是卻不知道它的應用場景,現在我們結合實際情況來講一下復制域
用戶在搜索框搜索的時候,有可能輸入的是商品名稱,也有可能輸入的是商品描述,也有可能輸入的是一個商品類型,那么這些值的搜索,肯定在后台是對應一個域的,那么既然如此,我們就可以把這些域合並成一個,這樣在后台只需要單獨的對這一個域進行搜索就可以了
先定義一個目標域
<field name="keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
復制域,把商品名稱,商品描述,商品類型名稱復制到上面的這個域中
<copyField source="name" dest="keywords"/> <copyField source="catalog_name" dest="keywords"/> <copyField source="description" dest="keywords"/>
1.10 dataimport 導入數據庫數據
solr默認是沒有開啟dataimport這個功能的,所以我們要經過一點配置來開啟它
- 首先找到solr5.5/dist/solr-dataimporthandler-5.5.2.jar,把這個文件復制到tomcat/webapp/solr/WEB-INF/lib/下,並且找到相應數據庫的驅動包,也同樣放到該目錄。我這里用的是mysql的驅動包。
- 找到solr5.5/example/example-DIH/solr/db/conf/db-data-config.xml,把其復制到solrHome/core1/conf/下,並改名為data-config.xml.
- 找到solrHome/core1/conf/solrconfig.xml,並打開,在里面添加一段內容,如下
-
<requestHandler name="/dataimport" class="solr.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
- 打開並編輯data-config.xml,完整的配置文件如下
-
<dataConfig> <!-- 這是mysql的配置,學會jdbc的都應該看得懂 --> <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/solr/useUnicode=true&characterEncoding=utf-8" user="root" password="密碼"/> <document> <!-- name屬性,就代表着一個文檔,可以隨便命名 --> <!-- query是一條sql,代表在數據庫查找出來的數據 --> <entity name="product" query="select * from products"> <!-- 每一個field映射着數據庫中列與文檔中的域,column是數據庫列,name是solr的域(必須是在managed-schema文件中配置過的域才行) --> <field column="pid" name="id"/> <field column="name" name="product_name"/> <field column="catalog" name="product_catalog"/> <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>
- 重啟tomcat,然后會看到如下頁面
- 點擊藍色的按鈕,則開始導入,導入過程依據數量量的大小,需要的時間也不同,可以點擊右邊的Refresh status來刷新狀態,可以查看當前導入了多少條。
- 導入成功如下
二:solrj的使用
代碼地址 https://github.com/zxj19951029/useSolrj
上面一章節已經講完了solr的安裝與配置,現在說一下使用solrj來維護solr的索引及操作,solrj就是一個java的客戶端,是一個jar包的使用
首先引入MAVEN的依賴,solrj的版本號要對應solr的版本號
<dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>5.5.2</version> </dependency>
2.1 增加及修改
首先說明,在solr中,增加與修改都是一回事,當這個id不存在時,則是添加,當這個id存在時,則是修改
代碼很好理解,直接給出
private String serverUrl = "http://192.168.1.4:8080/solr/core1"; /** * 增加與修改<br> * 增加與修改其實是一回事,只要id不存在,則增加,如果id存在,則是修改 * @throws IOException * @throws SolrServerException */ @Test public void upadteIndex() throws SolrServerException, IOException{ //已廢棄的方法 //HttpSolrServer server = new HttpSolrServer("http://192.168.1.4:8080/solr/core1"); //創建 HttpSolrClient client = new HttpSolrClient(serverUrl); SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "zxj1"); doc.addField("product_name", "javaWEB技術"); doc.addField("product_catalog", "1"); doc.addField("product_catalog_name", "書籍"); doc.addField("product_price", "11"); doc.addField("product_description", "這是一本好書"); doc.addField("product_picture", "圖片地址"); client.add(doc); client.commit(); client.close(); }
2.2 刪除索引
刪除的代碼也直接給出,看代碼里面的注釋就可以了
/** * 刪除索引 * @throws Exception */ @Test public void deleteIndex()throws Exception{ HttpSolrClient client = new HttpSolrClient(serverUrl); //1.刪除一個 client.deleteById("zxj1"); //2.刪除多個 List<String> ids = new ArrayList<>(); ids.add("1"); ids.add("2"); client.deleteById(ids); //3.根據查詢條件刪除數據,這里的條件只能有一個,不能以逗號相隔 client.deleteByQuery("id:zxj1"); //4.刪除全部,刪除不可恢復 client.deleteByQuery("*:*"); //一定要記得提交,否則不起作用 client.commit(); client.close(); }
2.3 查詢
查詢稍微復雜一點,但是與solr管理界面的條件一致
這里先給出一些查詢的說明,建議查看這篇文章,個人感覺還是不錯的http://blog.csdn.net/gufengshanyin/article/details/21098879
- q - 查詢字符串,如果查詢所有*:* (id:1)
- fq - (filter query)過慮查詢,過濾條件,基於查詢出來的結果
- fl - 指定返回那些字段內容,用逗號或空格分隔多個。
- start - 分頁開始
- rows - 分頁查詢數據
- sort - 排序,格式:sort=<field name>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:(score desc, price asc)表示先 “score” 降序, 再 “price” 升序,默認是相關性降序。
- wt - (writer type)指定輸出格式,可以有 xml, json, php, phps。
- fl表示索引顯示那些field( *表示所有field,如果想查詢指定字段用逗號或空格隔開(如:Name,SKU,ShortDescription或Name SKU ShortDescription【注:字段是嚴格區分大小寫的】))
- q.op 表示q 中 查詢語句的 各條件的邏輯操作 AND(與) OR(或)
- hl 是否高亮 ,如hl=true
- hl.fl 高亮field ,hl.fl=Name,SKU
- hl.snippets :默認是1,這里設置為3個片段
- hl.simple.pre 高亮前面的格式
- hl.simple.post 高亮后面的格式
- facet 是否啟動統計
- facet.field 統計field
1. “:” 指定字段查指定值,如返回所有值*:*
2. “?” 表示單個任意字符的通配
3. “*” 表示多個任意字符的通配(不能在檢索的項開始使用*或者?符號)
4. “~” 表示模糊檢索,如檢索拼寫類似於”roam”的項這樣寫:roam~將找到形如foam和roams的單詞;roam~0.8,檢索返回相似度在0.8以上的記錄。
5. 鄰近檢索,如檢索相隔10個單詞的”apache”和”jakarta”,”jakarta apache”~10
6. “^” 控制相關度檢索,如檢索jakarta apache,同時希望去讓”jakarta”的相關度更加好,那么在其后加上”^”符號和增量值,即jakarta^4 apache
7. 布爾操作符AND、||
8. 布爾操作符OR、&&
9. 布爾操作符NOT、!、- (排除操作符不能單獨與項使用構成查詢)
10. “+” 存在操作符,要求符號”+”后的項必須在文檔相應的域中存在
11. ( ) 用於構成子查詢
12. [] 包含范圍檢索,如檢索某時間段記錄,包含頭尾,date:[200707 TO 200710]
給出基本的代碼看一下,僅僅作為一個基本的查詢,高級的查詢,各位要自己結合文檔
package zxj.solrj; import java.util.List; import java.util.Map; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.junit.Test; /** * 搜索 * @author Administrator * */ public class IndexSearch { private String serverUrl = "http://192.168.1.4:8080/solr/core1"; @Test public void search()throws Exception{ HttpSolrClient client = new HttpSolrClient(serverUrl); //創建查詢對象 SolrQuery query = new SolrQuery(); //q 查詢字符串,如果查詢所有*:* query.set("q", "product_name:小黃人"); //fq 過濾條件,過濾是基於查詢結果中的過濾 query.set("fq", "product_catalog_name:幽默雜貨"); //sort 排序,請注意,如果一個字段沒有被索引,那么它是無法排序的 // query.set("sort", "product_price desc"); //start row 分頁信息,與mysql的limit的兩個參數一致效果 query.setStart(0); query.setRows(10); //fl 查詢哪些結果出來,不寫的話,就查詢全部,所以我這里就不寫了 // query.set("fl", ""); //df 默認搜索的域 query.set("df", "product_keywords"); //======高亮設置=== //開啟高亮 query.setHighlight(true); //高亮域 query.addHighlightField("product_name"); //前綴 query.setHighlightSimplePre("<span style='color:red'>"); //后綴 query.setHighlightSimplePost("</span>"); //執行搜索 QueryResponse queryResponse = client.query(query); //搜索結果 SolrDocumentList results = queryResponse.getResults(); //查詢出來的數量 long numFound = results.getNumFound(); System.out.println("總查詢出:" + numFound + "條記錄"); //遍歷搜索記錄 //獲取高亮信息 Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); for (SolrDocument solrDocument : results) { System.out.println("商品id:" + solrDocument.get("id")); System.out.println("商品名稱 :" + solrDocument.get("product_name")); System.out.println("商品分類:" + solrDocument.get("product_catalog")); System.out.println("商品分類名稱:" + solrDocument.get("product_catalog_name")); System.out.println("商品價格:" + solrDocument.get("product_price")); System.out.println("商品描述:" + solrDocument.get("product_description")); System.out.println("商品圖片:" + solrDocument.get("product_picture")); //輸出高亮 Map<String, List<String>> map = highlighting.get(solrDocument.get("id")); List<String> list = map.get("product_name"); if(list != null && list.size() > 0){ System.out.println(list.get(0)); } } client.close(); } }
注意:沒有索引的域,是不能用作排序的
三:電商平台的應用