目錄
概述
Solr 是Apache下的一個頂級開源項目,採用Java開發,它是基於Lucene的全文搜索服務器。Solr提供了比Lucene更爲豐富的查詢語言,同時實現了可配置、可擴展,並對索引、搜索性能進行了優化。
Solr可以獨立運行,運行在Jetty、Tomcat等這些Servlet容器中,Solr 索引的實現方法很簡單,用 POST 方法向 Solr 服務器發送一個描述 Field 及其內容的 XML 文檔,Solr根據xml文檔添加、刪除、更新索引 。
Solr 搜索只需要發送 HTTP GET 請求,然後對 Solr 返回Xml、json等格式的查詢結果進行解析,組織頁面佈局。Solr不提供構建UI的功能,Solr提供了一個管理界面,通過管理界面可以查詢Solr的配置和運行情況。
Solr目錄結構
bin:solr的運行腳本
contrib:solr的一些貢獻軟件/插件,用於增強solr的功能。
dist:該目錄包含build過程中產生的war和jar文件,以及相關的依賴文件。
docs:solr的API文檔
example:solr工程的例子目錄:
- example/solr:
該目錄是一個包含了默認配置信息的Solr的Core目錄。 - example/multicore:
該目錄包含了在Solr的multicore中設置的多個Core目錄。 - example/webapps:
該目錄中包括一個solr.war,該war可作爲solr的運行實例工程。
licenses:solr相關的一些許可信息
Solr與Tomcat整合配置
可以參考以下文章
https://blog.csdn.net/qq_35262405/article/details/95476021
Solr管理界面
訪問http://localhost:8080/solr,可以看到以下界面
管理界面中的頁面如下:
- Dashboard
儀表盤,顯示了該Solr實例開始啓動運行的時間、版本、系統資源、jvm等信息。
- Logging
Solr運行日誌信息
- Cloud
Cloud即SolrCloud,即Solr雲(集羣),當使用Solr Cloud模式運行時會顯示此菜單,如下圖是Solr Cloud的管理界面:
- Core Admin
Solr Core的管理界面。Solr Core 是Solr的一個獨立運行實例單位,它可以對外提供索引和搜索服務,一個Solr工程可以運行多個SolrCore(Solr實例),一個Core對應一個索引目錄。
- java properties
Solr在JVM 運行環境中的屬性信息,包括類路徑、文件編碼、jvm內存設置等信息。
- Tread Dump
顯示Solr Server中當前活躍線程信息,同時也可以跟蹤線程運行棧信息。
- Core selector
選擇一個SolrCore進行詳細操作,如下:
- Analysis(重點)
通過此界面可以測試索引分析器和搜索分析器的執行情況。
- dataimport
可以定義數據導入處理器,從關係數據庫將數據導入 到Solr索引庫中。
- Document(重點)
通過此菜單可以創建索引、更新索引、刪除索引等操作,界面如下:
/update表示更新索引,solr默認根據id(唯一約束)域來更新Document的內容,如果根據id值搜索不到id域則會執行添加操作,如果找到則更新。
- query(重點)
通過/select執行搜索索引,必須指定“q”查詢條件方可搜索。
Solr索引
scheam.xml
schema.xml,在SolrCore的conf目錄下,它是Solr數據表配置文件,它定義了加入索引的數據的數據類型的。主要包括FieldTypes、Fields和其他的一些缺省設置。
FieldType域類型定義
下邊“text_general”是Solr默認提供的FieldType,通過它說明FieldType定義的內容:
FieldType子結點包括name,class,positionIncrementGap等一些參數:
在FieldType定義的時候最重要的就是定義這個類型的數據在建立索引和進行查詢的時候要使用的分析器analyzer,包括分詞和過濾
索引分析器中:使用solr.StandardTokenizerFactory標準分詞器,solr.StopFilterFactory停用詞過濾器,solr.LowerCaseFilterFactory小寫過濾器。
搜索分析器中:使用solr.StandardTokenizerFactory標準分詞器,solr.StopFilterFactory停用詞過濾器,這裏還用到了solr.SynonymFilterFactory同義詞過濾器。
precisionStep的具體作用
1.precisionStep是在做range search的起作用的,默認值是4
2.數值類型(int float double)在Lucene裏都是以string形式存儲的,當然這個string是經過編碼的
3.經過編碼後的string保證是順序的,也就是說num1>num2,那麼strNum1>strNum2
4.precisionStep用來分解編碼後的string,例如有一個precisionStep,默認是4,也就是隔4位索引一個前綴,比如0100,0011,0001,1010會被分成下列的二進制位“0100,0011,0001,1010“,”0100,0011,0001“,0100,0011“,”0100“。這個值越大,那麼索引就越小,那麼範圍查詢的性能(尤其是細粒度的範圍查詢)也越差;這個值越小,索引就越大,那麼性能越差。
5.range search的過程參見下圖:
如果用戶希望查找423~642的記錄,如果沒有precisionStep,那麼就必須從最底層一個一個匹配了,性能肯定不行。有了precisionStep,那麼range search可以這麼做:44可以匹配445,446,448,而5可以匹配521,522;63可以匹配632,633,634;而只有423,641,642在最底層匹配。
Field定義
在fields結點內定義具體的Field,filed定義包括name,type(爲之前定義過的各種FieldType),indexed(是否被索引),stored(是否被儲存),multiValued(是否存儲多個值)等屬性。
<field name="name" type="text_general" indexed="true" stored="true"/>
<field name="features" type="text_general" indexed="true" stored="true" multiValued="true"/>
multiValued:該Field如果要存儲多個值時設置爲true,solr允許一個Field存儲多個值,比如存儲一個用戶的好友id(多個),商品的圖片(多個,大圖和小圖),通過使用solr查詢要看出返回給客戶端是數組:
uniqueKey
Solr中默認定義唯一主鍵key爲id域,如下:
Solr在刪除、更新索引時使用id域進行判斷,也可以自定義唯一主鍵。
注意在創建索引時必須指定唯一約束。
copyField複製域
copyField複製域,可以將多個Field複製到一個Field中,以便進行統一的檢索:
比如,輸入關鍵字搜索title標題內容content
定義title、content、text的域:
根據關鍵字只搜索text域的內容就相當於搜索title和content,將title和content複製到text中,如下:
dynamicField(動態字段)
動態字段就是不用指定具體的名稱,只要定義字段名稱的規則,例如定義一個 dynamicField,name 爲_i,定義它的type爲text,那麼在使用這個字段的時候,任何以_i結尾的字段都被認爲是符合這個定義的,例如:name_i,gender_i,school_i等。*
自定義Field名爲:product_title_t,“product_title_t”和scheam.xml中的dynamicField規則匹配成功,如下:
“product_title_t”是以“_t”結尾
創建索引
搜索索引
安裝中文分詞器IKAnalyzer
IKAnalyzer部署
拷貝IKAnalyzer的文件到Tomcat下Solr目錄中,將IKAnalyzer2012FF_u1.jar
拷貝到 Tomcat的webapps/solr/WEB-INF/lib
下。
在Tomcat的webapps/solr/WEB-INF/
下創建classes目錄,將IKAnalyzer.cfg.xml
、ext_stopword.dic
、mydict.dic
copy到 Tomcat的
webapps/solr/WEB-INF/classes
。
注意:ext_stopword.dic 和mydict.dic必須保存成無BOM的utf-8類型。
修改schema.xml文件
FieldType
首先需要在types結點內定義一個FieldType子結點,包括name,class等參數,name就是這個FieldType的名稱,class指向org.apache.solr.analysis包裏面對應的class名稱,用來定義這個類型的行爲。
在FieldType定義的時候最重要的就是定義這個類型的數據在建立索引和進行查詢的時候要使用的分析器analyzer,包括分詞和過濾。
<!-- IKAnalyzer-->
<fieldType name="text_ik" class="solr.TextField">
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
Field
FieldType定義好後就可以在fields結點內定義具體的field,filed定義包括name,type(即FieldType),indexed(是否被索引),stored(是否被儲存),multiValued(是否有多個值)等。
<!--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"/>
設置業務系統Field
如果不使用Solr提供的Field可以針對具體的業務需要自定義一套Field,如下是商品信息Field:
<!--product-->
<field name="product_name" type="text_ik" indexed="true" stored="true"/>
<field name="product_price" type="float" indexed="true" stored="true"/>
<field name="product_description" type="text_ik" indexed="true" stored="false" />
<field name="product_picture" type="string" indexed="false" stored="true" />
<field name="product_catalog_name" type="string" indexed="true" stored="true" />
<field name="product_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="product_name" dest="product_keywords"/>
<copyField source="product_description" dest="product_keywords"/>
索引維護
使用/update進行索引維護,進入Solr管理界面SolrCore下的Document下:
overwrite="true"
: solr在做索引的時候,如果文檔已經存在,就用xml中的文檔進行替換
commitWithin="10000"
: solr 在做索引的時候,每個10000(10秒)毫秒,做一次文檔提交。
爲了方便測試也可以在Document中立即提交,在後邊添加“”,如下:
<add>
<doc>
<field name="id">change.me</field>
<field name="title">change.me</field>
</doc>
</add>
<commit/>
添加/更新索引
solr默認根據id(唯一約束)域來更新Document的內容,如果根據id值搜索不到id域則會執行添加操作,如果找到則更新。
<add>
<doc>
<field name="id">change.me</field>
<field name="??" >??</field>
。。。
</doc>
</add>
說明:唯一標識 Field必須有,這裏使用Solr默認的id。
刪除索引
刪除制定ID的索引
<delete>
<id>8</id>
。。。
</delete>
刪除查詢到的索引數據
<delete>
<query>product_catalog_name:幽默雜貨</query>
</delete>
刪除所有索引數據
<delete>
<query>*:*</query>
</delete>
dataimport-handler
安裝dataimport-Handler從關係數據庫將數據導入到索引庫。
第一步:向SolrCore中加入jar包
在SolrCore目錄中創建lib目錄,將dataimportHandler和mysql數據庫驅動的jar拷貝至lib下。
dataimportHandler在solr安裝目錄的dist 下:
第二步:修改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文件,存放在SolrCore的conf目錄
<?xml version="1.0" encoding="UTF-8" ?>
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/lucene"
user="root"
password="mysql"/>
<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>
<field column="pid" name="id"/>
必須有一個id域,這裏使用Solr默認的id域,域值是從關係數據庫查詢的pid列值。
下邊以“product_”開頭的Field都是在schema.xml中自定義的商品信息Field。
第四步:重啓Tomcat,進入管理界面–》SolrCore–》dataimport下執行導入
如果有什麼問題可以參考在tomcat上solr服務器的搭建(5.2.0)
第五步:查看導入結果
進入管理界面–》SolrCore–》dataimport下
Solr搜索
requestHandler
可以通過get的方式直接訪問Solr的服務來查詢
<requestHandler name="/select" class="solr.SearchHandler">
<!-- 設置默認的參數值,可以在請求地址中修改這些參數-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int><!--顯示數量-->
<str name="wt">json</str><!--顯示格式-->
<str name="df">text</str><!--默認搜索字段-->
</lst>
</requestHandler>
http://localhost:8080/solr/core/select?wt=json&q=product_keywords:%E6%B5%AA%E6%BC%AB%E6%A8%B1%E8%8A%B1%20AND%20product_keywords:%E9%9F%A9%E5%9B%BD%20OR%20product_catalog_name:%E4%B8%8E%E9%92%9F%E4%B8%8D%E5%90%8C&indent=on
上面這個請求等同於
查詢語法
通過/select搜索索引,Solr制定一些參數完成不同需求的搜索
q - 查詢字符串
如果查詢所有使用*:*
fq - (filter query)過慮查詢
在q查詢符合結果中同時是fq查詢符合的
過濾查詢價格從1到20的記錄
也可以在“q”查詢條件中使用product_price:[1 TO 20],如下:
也可以使用“*”表示無限,例如:
20以上:product_price:[20 TO \*]
20以下:product_price:[* TO 20]
sort - 排序
sort=<field name>+<desc|asc>[,<field name>+<desc|asc>]…
按價格降序
start - 分頁顯示使用
開始記錄下標,從0開始
rows - 指定返回結果最多有多少條記錄
配合start來實現分頁
表示顯示前10條
fl - 指定返回那些字段內容
用逗號或空格分隔多個
顯示商品圖片、商品名稱、商品價格
df - 指定一個搜索Field
也可以在SolrCore目錄 中conf/solrconfig.xml文件中指定默認搜索Field,指定後就可以直接在“q”查詢條件中輸入關鍵字。
wt - (writer type)指定輸出格式
可以有 xml, json, php, phps, 後面 solr 1.3增加的,要用通知我們,因爲默認沒有打開
hl - 是否高亮
設置高亮Field,設置格式前綴和後綴。
SolrJ
solrj是訪問Solr服務的java客戶端,提供索引和搜索的請求方法,SolrJ通常在嵌入在業務系統中,通過SolrJ的API接口操作Solr服務,如下圖:
SolrJ對索引的維護
創建索引
使用SolrJ創建索引,通過調用SolrJ提供的API請求Solr服務,Document通過SolrInputDocument進行構建。
// 創建索引
public void testCreateIndex() throws SolrServerException, IOException {
SolrServer solrServer = new HttpSolrServer(urlString);
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "c0001");
document.addField("product_name", "傳智java教程");//商品名稱
document.addField("product_price", 86.5f);//商品價格
document.addField("product_picture", "382782828.jpg");//商品圖片
document.addField("product_description", "這是一本深入淺出講解java技術的書籍!");//商品描述
document.addField("product_catalog_name", "javabook");//商品分類
UpdateResponse response = solrServer.add(document);
// 提交
solrServer.commit();
}
說明:根據id(唯一約束)域來更新Document的內容,如果根據id值搜索不到id域則會執行添加操作,如果找到則更新。
刪除索引
// 刪除索引
@Test
public void testDeleteIndex() throws SolrServerException, IOException {
SolrServer solrServer = new HttpSolrServer(urlString);
//根據id刪除
UpdateResponse response = solrServer.deleteById("c0001");
//根據多個id刪除
// solrServer.deleteById(ids);
//自動查詢條件刪除
// solrServer.deleteByQuery("product_keywords:教程");
// 提交
solrServer.commit();
}
說明:deleteById(String id)根據id刪除索引,此方法爲重載方法,也可以傳個多個id批量刪除,也可以調用deleteByQuery() 根據查詢條件刪除。
SolrJ完成搜索
搜索索引
// 搜索
@Test
public void testSearch() throws SolrServerException {
SolrServer solr = new HttpSolrServer(urlString);
// 查詢對象
SolrQuery query = new SolrQuery();
//設置查詢條件,名稱“q”是固定的且必須 的
//搜索product_keywords域,product_keywords是複製域包括product_name和product_description
query.set("q", "product_keywords:java教程");
// 請求查詢
QueryResponse response = solr.query(query);
// 查詢結果
SolrDocumentList docs = response.getResults();
// 查詢文檔總數
System.out.println("查詢文檔總數" + docs.getNumFound());
for (SolrDocument doc : docs) {
//商品主鍵
String id = (String) doc.getFieldValue("id");
//商品名稱
String product_name = (String) doc.getFieldValue("product_name");
//商品價格
Float product_price = (Float) doc.getFieldValue("product_price");
//商品圖片
String product_picture = (String) doc.getFieldValue("product_picture");
//商品分類
String product_catalog_name = (String) doc.getFieldValue("product_catalog_name");
System.out.println("=============================");
System.out.println(id);
System.out.println(product_name);
System.out.println(product_price);
System.out.println(product_picture);
System.out.println(product_catalog_name);
}
}
組合查詢
// 根據商品分類、價格範圍、關鍵字查詢,查詢結果按照價格降序排序
@Test
public void testSearch2() throws SolrServerException {
SolrServer solr = new HttpSolrServer(urlString);
// 查詢對象
SolrQuery query = new SolrQuery();
// 搜索product_keywords域,product_keywords是複製域包括product_name和product_description
// 設置商品分類、關鍵字查詢
// query.set("q", "product_keywords:掛鉤 AND product_catalog_name:幽默雜貨");
query.setQuery("product_keywords:掛鉤 AND product_catalog_name:幽默雜貨");
// 設置價格範圍
query.set("fq", "product_price:[1 TO 20]");
// 查詢結果按照價格降序排序
// query.set("sort", "product_price desc");
query.addSort("product_price", ORDER.desc);
// 請求查詢
QueryResponse response = solr.query(query);
// 查詢結果
SolrDocumentList docs = response.getResults();
// 查詢文檔總數
System.out.println("查詢文檔總數" + docs.getNumFound());
for (SolrDocument doc : docs) {
// 商品主鍵
String id = (String) doc.getFieldValue("id");
// 商品名稱
String product_name = (String) doc.getFieldValue("product_name");
// 商品價格
Float product_price = (Float) doc.getFieldValue("product_price");
// 商品圖片
String product_picture = (String) doc
.getFieldValue("product_picture");
// 商品分類
String product_catalog_name = (String) doc
.getFieldValue("product_catalog_name");
System.out.println("=============================");
System.out.println("id=" + id);
System.out.println("product_name=" + product_name);
System.out.println("product_price=" + product_price);
System.out.println("product_picture=" + product_picture);
System.out.println("product_catalog_name=" + product_catalog_name);
}
}
分頁、高亮
// 分頁和高亮
@Test
public void testSearch3() throws SolrServerException {
SolrServer solr = new HttpSolrServer(urlString);
// 查詢對象
SolrQuery query = new SolrQuery();
// 設置商品分類、關鍵字查詢
query.setQuery("product_keywords:透明掛鉤 ");
// 分頁參數
// 每頁顯示記錄數
int pageSize = 2;
// 當前頁碼
int curPage = 2;
// 開始記錄下標
int begin = pageSize * (curPage - 1);
// 起始下標
query.setStart(begin);
// 結束下標
query.setRows(pageSize);
// 設置高亮參數
query.setHighlight(true); // 開啓高亮組件
query.addHighlightField("product_name");// 高亮字段
query.setHighlightSimplePre("<span color='red'>");// 前綴標記
query.setHighlightSimplePost("</span>");// 後綴標記
// 請求查詢
QueryResponse response = solr.query(query);
// 查詢結果
SolrDocumentList docs = response.getResults();
// 查詢文檔總數
System.out.println("查詢文檔總數" + docs.getNumFound());
for (SolrDocument doc : docs) {
// 商品主鍵
String id = (String) doc.getFieldValue("id");
// 商品名稱
String product_name = (String) doc.getFieldValue("product_name");
// 商品價格
Float product_price = (Float) doc.getFieldValue("product_price");
// 商品圖片
String product_picture = (String) doc
.getFieldValue("product_picture");
// 商品分類
String product_catalog_name = (String) doc
.getFieldValue("product_catalog_name");
System.out.println("=============================");
System.out.println("id=" + id);
System.out.println("product_name=" + product_name);
System.out.println("product_price=" + product_price);
System.out.println("product_picture=" + product_picture);
System.out.println("product_catalog_name=" + product_catalog_name);
// 高亮信息
if (response.getHighlighting() != null) {
if (response.getHighlighting().get(id) != null) {
Map<String, List<String>> map = response.getHighlighting()
.get(id);// 取出高亮片段
if (map.get("product_name") != null) {
for (String s : map.get("product_name")) {
System.out.println(s);
}
}
}
}
}
}