全文搜索技術—Solr


1.   學習計划

  1. Solr的安裝及配置

a)    Solr整合tomcat

b)    Solr后台管理功能介紹

c)    配置中文分析器

  2. 使用Solr的后台管理索引庫

a)    添加文檔

b)     刪除文檔

c)    修改文檔

d)    查詢文檔

  3. 使用SolrJ管理索引庫

a)    添加文檔

b)    修改文檔

c)    刪除文檔

d)    查詢文檔

  4. 電商搜索案例實現

a)    案例分析

b)    案例實現

 

2.   需求分析

使用Solr實現電商網站中商品信息搜索功能,可以根據關鍵字、分類、價格搜索商品信息,也可以根據價格進行排序。

2.1. 實現方法

在一些大型門戶網站、電子商務網站等都需要站內搜索功能,使用傳統的數據庫查詢方式實現搜索無法滿足一些高級的搜索需求,比如:搜索速度要快、搜索結果按相關度排序、搜索內容格式不固定等,這里就需要使用全文檢索技術實現搜索功能

2.1.1.   使用Lucene實現

單獨使用Lucene實現站內搜索需要開發的工作量較大,主要表現在:索引維護、索引性能優化、搜索性能優化等,因此不建議采用。

2.1.2.   使用solr實現

基於Solr實現站內搜索擴展性較好並且可以減少程序員的工作量,因為Solr提供了較為完備的搜索引擎解決方案,因此在門戶、論壇等系統中常用此方案。

 

2.2. 什么是solr

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與Lucene的區別:

Lucene是一個開放源代碼的全文檢索引擎工具包,它不是一個完整的全文檢索引擎,Lucene提供了完整的查詢引擎和索引引擎,目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者以Lucene為基礎構建全文檢索引擎。

 Solr的目標是打造一款企業級的搜索引擎系統,它是一個搜索引擎服務,可以獨立運行,通過Solr可以非常快速的構建企業的搜索引擎,通過Solr也可以高效的完成站內搜索功能。

 

 

 

3.   Solr安裝及配置

3.1. Solr的下載

從Solr官方網站(http://lucene.apache.org/solr/ )下載Solr4.10.3,根據Solr的運行環境,Linux下需要下載lucene-4.10.3.tgz,windows下需要下載lucene-4.10.3.zip。

Solr使用指南可參考:https://wiki.apache.org/solr/FrontPage。

3.2. Solr的文件夾結構

將solr-4.10.3.zip解壓:

 

 

 

bin:solr的運行腳本

contrib:solr的一些貢獻軟件/插件,用於增強solr的功能。

dist:該目錄包含build過程中產生的war和jar文件,以及相關的依賴文件。

docs:solr的API文檔

example:solr工程的例子目錄:

  • example/solr:

       該目錄叫做solrHome,是一個包含了默認配置信息的Solr的Core目錄。

  • example/multicore:

       該目錄包含了在Solr的multicore中設置的多個Core目錄。

  • example/webapps:

    該目錄中包括一個solr.war,該war可作為solr的運行實例工程。

licenses:solr相關的一些許可信息

3.3. 運行環境

solr 需要運行在一個Servlet容器中,Solr4.10.3要求jdk使用1.7以上,Solr默認提供Jetty(java寫的Servlet容器),這里使用Tocmat作為Servlet容器,環境如下:

 

Solr:Solr4.10.3

Jdk:jdk1.7.0_72

Tomcat:apache-tomcat-7.0.53

3.4. Solr整合tomcat

3.4.1.   Solr Home與SolrCore

創建一個Solr home目錄,SolrHome是Solr運行的主目錄,目錄中包括了運行Solr實例所有的配置文件和數據文件,Solr實例就是SolrCore,一個SolrHome可以包括多個SolrCore(Solr實例),每個SolrCore提供單獨的搜索和索引服務。

 

example\solr是一個solr home目錄結構,如下:

 

上圖中“collection1”是一個SolrCore(Solr實例)目錄 ,目錄內容如下所示:

  

說明:

collection1:叫做一個Solr運行實例SolrCore,SolrCore名稱不固定,一個solr運行實例對外單獨提供索引和搜索接口。

solrHome中可以創建多個solr運行實例SolrCore。

一個solr的運行實例對應一個索引目錄。

conf是SolrCore的配置文件目錄 。

data目錄存放索引文件需要創建

 

3.4.2.   整合步驟

第一步:安裝tomcat。D:\temp\apache-tomcat-7.0.53

第二步:把solr的war包復制到tomcat 的webapp目錄下。

把\solr-4.10.3\dist\solr-4.10.3.war復制到D:\temp\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:\temp\0108路徑下,改名為solrhome,改名不是必須的,是為了便於理解。

  3)在solrhome下有一個文件夾叫做collection1這就是一個solrcore。就是一個solr的實例。一個solrcore相當於mysql中一個數據庫。Solrcore之間是相互隔離。  

    1. 在solrcore中有一個文件夾叫做conf,包含了索引solr實例的配置信息。
    2. 在conf文件夾下有一個solrconfig.xml。配置實例的相關信息。如果使用默認配置可以不用做任何修改。

    Xml的配置信息:

    Lib:solr服務依賴的擴展包,默認的路徑是collection1\lib文件夾,如果沒有就創建一個

    dataDir:配置了索引庫的存放路徑。默認路徑是collection1\data文件夾,如果沒有data文件夾,會自動創建。

    requestHandler

 

 

 

第六步:告訴solr服務器配置文件也就是solrHome的位置。修改web.xml使用jndi的方式告訴solr服務器。

Solr/home名稱必須是固定的。

 

 

 

第七步:啟動tomcat

第八步:訪問http://localhost:8080/solr/

 

3.5. Solr后台管理

3.5.1.   管理界面

 

 

3.5.2.   Dashboard

儀表盤,顯示了該Solr實例開始啟動運行的時間、版本、系統資源、jvm等信息。

3.5.3.   Logging

Solr運行日志信息

3.5.4.   Cloud

Cloud即SolrCloud,即Solr雲(集群),當使用Solr Cloud模式運行時會顯示此菜單,如下圖是Solr Cloud的管理界面:

 

 

3.5.5.   Core Admin

Solr Core的管理界面。Solr Core 是Solr的一個獨立運行實例單位,它可以對外提供索引和搜索服務,一個Solr工程可以運行多個SolrCore(Solr實例),一個Core對應一個索引目錄。

 

添加solrcore:

第一步:復制collection1改名為collection2

第二步:修改core.properties。name=collection2

第三步:重啟tomcat

3.5.6.   java properties

Solr在JVM 運行環境中的屬性信息,包括類路徑、文件編碼、jvm內存設置等信息。

 

3.5.7.   Tread Dump

顯示Solr Server中當前活躍線程信息,同時也可以跟蹤線程運行棧信息。

3.5.8.   Core selector

選擇一個SolrCore進行詳細操作,如下:

 

 

3.5.9.   Analysis

 

 

通過此界面可以測試索引分析器和搜索分析器的執行情況。

3.5.10.  Dataimport

可以定義數據導入處理器,從關系數據庫將數據導入 到Solr索引庫中

3.5.11.  Document

通過此菜單可以創建索引更新索引刪除索引等操作,界面如下:

 

 

/update表示更新索引,solr默認根據id(唯一約束)域來更新Document的內容,如果根據id值搜索不到id域則會執行添加操作,如果找到則更新。

3.5.12.  Query

 

通過/select執行搜索索引,必須指定“q”查詢條件方可搜索。

3.6. 配置中文分析器

3.6.1.   Schema.xml

schema.xml,在SolrCore的conf目錄下,它是Solr數據表配置文件,它定義了加入索引的數據的數據類型。主要包括FieldTypesFields和其他的一些缺省設置。

 

 

3.6.1.1  FieldType域類型定義

下邊“text_general”是Solr默認提供的FieldType,通過它說明FieldType定義的內容:

  

FieldType子結點包括:name,class,positionIncrementGap等一些參數:

name:是這個FieldType的名稱

class:是Solr提供的包solr.TextField,solr.TextField 允許用戶通過分析器來定制索引和查詢,分析器包括一個分詞器tokenizer)和多個過濾器filter

positionIncrementGap:可選屬性,定義在同一個文檔中此類型數據的空白間隔,避免短語匹配錯誤,此值相當於Lucene的短語查詢設置slop值,根據經驗設置為100。

例如:搜索big car,如果document中存的是big  red  car,就無法搜索到了, positionIncrementGap就是設置big和car中間最大的間隔距離,只要在距離內就能搜索到.

 

在FieldType定義的時候最重要的就是定義這個類型的數據在建立索引進行查詢的時候要使用的分析器analyzer,包括分詞和過濾

 

索引分析器中:使用solr.StandardTokenizerFactory標准分詞器,solr.StopFilterFactory停用詞過濾器,solr.LowerCaseFilterFactory小寫過濾器。

搜索分析器中:使用solr.StandardTokenizerFactory標准分詞器,solr.StopFilterFactory停用詞過濾器,這里還用到了solr.SynonymFilterFactory同義詞過濾器。

3.6.1.2  Field定義

  solr索引字段在solrhome\collection1\conf\schema.xml配置文件中,類似下面這些,包含在<fields>與</fields>之間的。

  在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查詢要看出返回給客戶端是數組:

   

 

3.6.1.3  uniqueKey

Solr中默認定義唯一主鍵keyid域,如下:

   

Solr在刪除、更新索引時使用id域進行判斷,也可以自定義唯一主鍵。

注意在創建索引時必須指定唯一約束。

3.6.1.4  copyField復制域

copyField復制域,可以將多個Field復制到一個Field中,以便進行統一的檢索:

比如,輸入關鍵字搜索title標題內容content,

定義title、content、text的域:

 

  

  

根據關鍵字只搜索text域的內容就相當於搜索title和content,將title和content復制到text中,如下:

 

 

3.6.1.5  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”結尾。

 

創建索引

 

 

搜索索引:

 

 

 

3.6.2.   安裝中文分詞器

使用IKAnalyzer中文分析器。

 

第一步:把IKAnalyzer2012FF_u1.jar添加到solr/WEB-INF/lib目錄下。

第二步:復制IKAnalyzer的配置文件自定義詞典停用詞詞典到solr的classpath下。

也就是在apache-tomcat-7.0.54\webapps\solr\WEB-INF目錄下新建classes目錄,將配置文件和詞典放進去.

 

第三步:在schema.xml中添加一個自定義的fieldType,使用中文分析器。

<!-- IKAnalyzer-->

    <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

測試:

 

 

3.7. 設置業務系統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"/>

 

 

4.   Solr管理索引庫

4.1. 維護索引

4.1.1.   添加/更新文檔

添加單個文檔

 

批量導入數據

使用dataimport插件批量導入數據。

第一步:把dataimport插件依賴的jar包添加到solrcore(collection1\lib)中

 

 

還需要mysql的數據庫驅動。

第二步:配置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,保存到collection1\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="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>

 

 

第四步:重啟tomcat

 

第五步:點擊“execute”按鈕導入數據

 

 

導入數據前會先清空索引庫,然后再導入。

 

4.1.2.   刪除文檔

刪除索引格式如下:

 

1) 刪除制定ID的索引

<delete>

       <id>8</id>

</delete>

 

 

2) 刪除查詢到的索引數據

<delete>

       <query>product_catalog_name:幽默雜貨</query>

</delete>

 

3) 刪除所有索引數據

 <delete>

       <query>*:*</query>

</delete>

 

4.2. 查詢索引

通過/select搜索索引,Solr制定一些參數完成不同需求的搜索:

 4.2.1  q - 查詢

  1. q - 查詢字符串,必須的,如果查詢所有使用*:*。

  

 

 

  4.2.2  fq - 過濾查詢

  2. fq - (filter query過慮查詢,作用:在q查詢符合結果中同時是fq查詢符合的,例如::

   

 

  過濾查詢價格從1到20的記錄。

  也可以在“q”查詢條件中使用product_price:[1 TO 20],如下:

   

  也可以使用“*”表示無限,例如:

  20以上:product_price:[20 TO *]

  20以下:product_price:[* TO 20]

  4.2.3  sort - 排序

  3. sort - 排序,格式:sort=<field name>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:

按價格降序

   

 4.2.4  start rows - 分頁

  4. start - 分頁顯示使用,開始記錄下標,從0開始

  5. rows - 指定返回結果最多有多少條記錄,配合start來實現分頁。

   

   顯示前10條。

 4.2.5  fl - 指定返回字段

  6. fl - 指定返回那些字段內容,用逗號或空格分隔多個。

   

  顯示商品圖片、商品名稱、商品價格

 4.2.6  df - 指定搜索字段

  7. df-指定一個搜索Field

  

  也可以在SolrCore目錄 中conf/solrconfig.xml文件中指定默認搜索Field,指定后就可以直接在“q”查詢條件中輸入關鍵字。

   

4.2.7  wt - 指定出處格式 

  8. wt - (writer type)指定輸出格式,可以有 xml, json, php, phps, 后面 solr 1.3增加的,要用通知我們,因為默認沒有打開。

 

4.2.8  hl - 高亮顯示

  9. hl 是否高亮 ,設置高亮Field,設置格式前綴和后綴。

   

 

5.   使用SolrJ管理索引庫

5.1. 什么是solrJ

solrj是訪問Solr服務的java客戶端,提供索引和搜索的請求方法,SolrJ通常在嵌入在業務系統中,通過SolrJ的API接口操作Solr服務,如下圖:

 

 

 

5.2. 依賴的jar包

 

5.3. 添加文檔

5.3.1.   實現步驟

第一步:創建一個java工程

第二步:導入jar包。包括solrJ的jar包。還需要

 

第三步:使用SolrServer和Solr服務器建立連接。HttpSolrServer對象建立連接。SolrServer是抽象類,連接單機版solr使用httpSolrServer,鏈接集群版使用CloudSolrServer。

第四步:創建一個SolrInputDocument對象,然后添加域

第五步:將SolrInputDocument添加到索引庫

第六步:提交

5.3.2.   代碼實現

 1 //向索引庫中添加索引
 2 
 3      @Test
 4 
 5      public void addDocument() throws Exception {
 6 
 7          //和solr服務器創建連接
 8 
 9          //參數:solr服務器的地址
10 
11          SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
12 
13          //創建一個文檔對象
14 
15          SolrInputDocument document = new SolrInputDocument();
16 
17          //向文檔中添加域
18 
19          //第一個參數:域的名稱,域的名稱必須是在schema.xml中定義的
20 
21          //第二個參數:域的值
22 
23          document.addField("id", "c0001");
24 
25          document.addField("title_ik", "使用solrJ添加的文檔");
26 
27          document.addField("content_ik", "文檔的內容");
28 
29          document.addField("product_name", "商品名稱");
30 
31          //把document對象添加到索引庫中
32 
33          solrServer.add(document);
34 
35          //提交修改
36 
37          solrServer.commit();
38 
39         
40 
41      }

 

@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();
    }
View Code

 

5.4. 刪除文檔 

5.4.1.   根據id刪除

 1 //刪除文檔,根據id刪除
 2 
 3      @Test
 4 
 5      public void deleteDocumentByid() throws Exception {
 6 
 7          //創建連接
 8 
 9          SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
10 
11          //根據id刪除文檔
12 
13          solrServer.deleteById("c0001");
14 
15          //提交修改
16 
17          solrServer.commit();
18 
19      }

 

 

5.4.2.   根據查詢刪除 

查詢語法完全支持Lucene的查詢語法。

 1 //根據查詢條件刪除文檔
 2 
 3      @Test
 4 
 5      public void deleteDocumentByQuery() throws Exception {
 6 
 7          //創建連接
 8 
 9          SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
10 
11          //根據查詢條件刪除文檔,這里是刪除所有*:*
12 
13          solrServer.deleteByQuery("*:*");
14 
15          //提交修改
16 
17          solrServer.commit();
18 
19      }

 

  

5.5. 修改文檔

在solrJ中修改沒有對應的update方法,只有add方法,只需要添加一條新的文檔,和被修改的文檔id一致就,可以修改了。本質上就是先刪除后添加

 

5.6. 查詢文檔

5.6.1.   簡單查詢

 1 //查詢索引
 2 
 3      @Test
 4 
 5      public void queryIndex() throws Exception {
 6 
 7          //創建連接,鏈接solr服務端
 8 
 9          SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
10 
11          //創建一個query對象,solr查詢條件對象
12 
13          SolrQuery query = new SolrQuery();
14 
15          //設置查詢條件
16 
17          query.setQuery("*:*");
18 
19          //查詢,執行查詢並獲取查詢響應對象
20 
21          QueryResponse queryResponse = solrServer.query(query);
22 
23          //取查詢結果,從查詢響應中獲取查詢結果集對象
24 
25          SolrDocumentList solrDocumentList = queryResponse.getResults();
26 
27          //共查詢到商品數量
28 
29          System.out.println("共查詢到商品數量:" + solrDocumentList.getNumFound());
30 
31          //遍歷查詢的結果
32 
33          for (SolrDocument solrDocument : solrDocumentList) {
34 
35               System.out.println(solrDocument.get("id"));
36 
37               System.out.println(solrDocument.get("product_name"));
38 
39               System.out.println(solrDocument.get("product_price"));
40 
41               System.out.println(solrDocument.get("product_catalog_name"));
42 
43               System.out.println(solrDocument.get("product_picture"));
44 
45              
46 
47          }
48 
49      }

 

5.6.2.   復雜查詢 

其中包含查詢過濾分頁排序高亮顯示等處理。

例子1:

//復雜查詢索引
    @Test
    public void queryIndex2() throws Exception {
        //創建連接
        SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
        //創建一個query對象
        SolrQuery query = new SolrQuery();
        //設置查詢條件
        query.setQuery("鑽石");
        //過濾條件
        query.setFilterQueries("product_catalog_name:幽默雜貨");
        //排序條件
        query.setSort("product_price", ORDER.asc);
        //分頁處理
        query.setStart(0);
        query.setRows(10);
        //結果中域的列表
        query.setFields("id","product_name","product_price","product_catalog_name","product_picture");
        //設置默認搜索域
        query.set("df", "product_keywords");
        //高亮顯示
        query.setHighlight(true);
        //高亮顯示的域
        query.addHighlightField("product_name");
        //高亮顯示的前綴
        query.setHighlightSimplePre("<em>");
        //高亮顯示的后綴
        query.setHighlightSimplePost("</em>");
        //執行查詢
        QueryResponse queryResponse = solrServer.query(query);
        //取查詢結果
        SolrDocumentList solrDocumentList = queryResponse.getResults();
        //共查詢到商品數量
        System.out.println("共查詢到商品數量:" + solrDocumentList.getNumFound());
        //遍歷查詢的結果
        for (SolrDocument solrDocument : solrDocumentList) {
            System.out.println(solrDocument.get("id"));
            //取高亮顯示
            String productName = "";
            Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
            List<String> list = highlighting.get(solrDocument.get("id")).get("product_name");
            //判斷是否有高亮內容
            if (null != list) {
                productName = list.get(0);
            } else {
                productName = (String) solrDocument.get("product_name");
            }
            
            System.out.println(productName);
            System.out.println(solrDocument.get("product_price"));
            System.out.println(solrDocument.get("product_catalog_name"));
            System.out.println(solrDocument.get("product_picture"));
            
        }
    }
View Code

 

 

例子2

 

@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("====================================================");
        }
    }
View Code

 

 

 

6.   案例實現

6.1. 原型分析

 

 

6.2. 系統架構

 

 

6.3. 工程搭建

創建一個web工程導入jar包

  1、springmvc的相關jar包

  2、solrJ的jar包

  3、Example\lib\ext下的jar包

 

6.3.1.   Springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
        
    <context:component-scan base-package="com.itheima.jd"/>
    <!-- 配置注解驅動,如果配置此標簽可以不用配置處理器映射器和適配器 -->
    <mvc:annotation-driven/>
        <!-- 配置視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- SolrServer的配置 -->
    <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
        <constructor-arg index="0" value="http://localhost:8080/solr"/>
    </bean>
</beans>
View Code

 

6.3.2.   Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>solr-jd</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 配置前段控制器 -->
  <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>
  <!-- 解決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>
View Code

 

 

6.4. Dao

功能:接收service層傳遞過來的參數,根據參數查詢索引庫,返回查詢結果。

參數:SolrQuery對象

返回值:一個商品列表List<ProductModel>,還需要返回查詢結果的總數量。

返回:ResultModel

方法定義:ResultModel queryProduct(SolrQuery query) throws Exception;

 

 

商品對象模型:

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

@Repository
public class ProductDaoImpl implements ProductDao {

    @Autowired
    private SolrServer solrServer;
    
    @Override
    public ResultModel queryProduct(SolrQuery query) throws Exception {
        
        ResultModel resultModel  = new ResultModel();
        //根據query對象查詢商品列表
        QueryResponse queryResponse = solrServer.query(query);
        SolrDocumentList solrDocumentList = queryResponse.getResults();
        //取查詢結果的總數量
        resultModel.setRecordCount(solrDocumentList.getNumFound());
        List<ProductModel> productList = new ArrayList<>();
        //遍歷查詢結果
        for (SolrDocument solrDocument : solrDocumentList) {
            //取商品信息
            ProductModel productModel = new ProductModel();
            productModel.setPid((String) solrDocument.get("id"));
            //取高亮顯示
            String productName = "";
            Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
            List<String> list = highlighting.get(solrDocument.get("id")).get("product_name");
            if (null != list) {
                productName = list.get(0);
            } else {
                productName = (String) solrDocument.get("product_name");
            }
            productModel.setName(productName);
            if(null == doc.get("product_price")){
                productModel.setPrice(0f);
            }else{
                productModel.setPrice((float)doc.get("product_price"));
            }
            productModel.setCatalog_name((String) solrDocument.get("product_catalog_name"));
            productModel.setPicture((String) solrDocument.get("product_picture"));
            //添加到商品列表
            productList.add(productModel);
        }
        //商品列表添加到resultmodel中
        resultModel.setProductList(productList);
        return resultModel;
    }

}
View Code

 

6.5. 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 {

    @Autowired
    private ProductDao productDao;
    
    @Override
    public ResultModel queryProduct(String queryString, String caltalog_name,
            String price, String sort, Integer page) throws Exception {
        //拼裝查詢條件
        SolrQuery query = new SolrQuery();
        //查詢條件
        if (null != queryString && !"".equals(queryString)) {
            query.setQuery(queryString);
        } else {
            query.setQuery("*:*");
        }
        //商品分類名稱過濾
        if (null != caltalog_name && !"".equals(caltalog_name)) {
            query.addFilterQuery("product_catalog_name:" + caltalog_name);
        }
        //價格區間過濾
        if (null != price && !"".equals(price)) {
            String[] strings = price.split("-");
            query.addFilterQuery("product_price:["+strings[0]+" TO "+strings[1]+"]");
        }
        //排序條件
        if ("1".equals(sort)) {
            query.setSort("product_price", ORDER.desc);
        } else {
            query.setSort("product_price", ORDER.asc);
        }
        //分頁處理
        if (null == page) {
            page = 1;
        }
        //start
        int start = (page-1) * Commons.PAGE_SIZE;
        query.setStart(start);
        query.setRows(Commons.PAGE_SIZE);
        //設置默認搜索域
        query.set("df", "product_keywords");
        //高亮設置
        query.setHighlight(true);
        query.addHighlightField("product_name");
        query.setHighlightSimplePre("<span style=\"color:red\">");
        query.setHighlightSimplePost("</span>");
        
        //查詢商品列表
        ResultModel resultModel = productDao.queryProduct(query);
        //計算總頁數
        long recordCount = resultModel.getRecordCount();
        int pages = (int) (recordCount/Commons.PAGE_SIZE);
        if (recordCount % Commons.PAGE_SIZE > 0) {
            pages ++;
        }
        resultModel.setPageCount(pages);
        resultModel.setCurPage(page);
        
        return resultModel;
    }

}
View Code

 

6.6. 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 ProductAction {

    

     @Autowired

     private ProductService productService;

    

     @RequestMapping("/list")

     public String queryProduct(String queryString, String catalog_name, String price,

              String sort, Integer page, Model model) throws Exception {

         //查詢商品列表

         ResultModel resultModel = productService.queryProduct(queryString, catalog_name, price, sort, page);

         //列表傳遞給jsp

         model.addAttribute("result", resultModel);

         //參數回顯

         model.addAttribute("queryString", queryString);

         model.addAttribute("caltalog_name", catalog_name);

         model.addAttribute("price", price);

         model.addAttribute("sort", sort);

         model.addAttribute("page", page);

         

         return "product_list";

     }

}
View Code

7.  總結

  • 7.1.  solr是一個全文檢索引擎系統,通過部署到tomcat下就可以獨立運行,通過http協議對外提供全文檢索服務,就是索引和文檔的正刪改查服務

  • 7.2.  solr直接操作索引庫和文檔庫, 我們的業務系統中可以使用solrJ(solr的客戶端,就是一堆jar包)來調用solr服務端,讓solr服務端操作文檔庫和索引庫,完成正刪改查的任務,將結果返回給solrJ客戶端,我們在業務系統中就可以,獲取到結果然后返回給客戶在瀏覽器中顯示.

  • 7.3.  solrHome:solrhome就是solr最核心的目錄, 一個solrhome中可以有多個solr實例

  • 7.4.  solrCore:一個solrCore就是一個solr實例,solr中實例與實例之間他們的索引庫和文檔庫是相互隔離的每個實例對外單獨的提供索引和文檔的增刪改查服務,默認實例叫做collection1

  • 7.5.  文檔和索引的增加和修改必須要有id, 主鍵域,沒有會報錯

  • 7.6.  域名和類型必須先在schema.xml中定義后使用,如果沒有定義就使用會報錯

  • 7.7.  域的分類

      普通域:string long 等

      動態域:起到模糊匹配的效果,可以模糊匹配沒有定義過的域名
        例如:xxxx這個域名沒有定義,但是xxxx_s這個域名模糊匹配了*_s這個域,所以相當於xxxx_s這個域定義了

      主鍵域:<uniqueKey>id</uniqueKey> 一般主鍵域就用默認的這個就可以不需要更改或者添加

      復制域: 復制域用於查詢的時候從多個域中進行查詢,這樣可以將多個域復制到某一個統一的域中,然后搜索的時候從這個統一的域中進行查詢,就相當於從多個域中查詢了.

  • 7.8.  是否存儲和是否索引無關, 索引后就能查詢,不索引就不能根據這個域搜索, 存儲后就能取出來里面的內容,不存儲就取不出這個域內容

  • 7.9.  一般企業中將數據全部放入數據庫中, 由於查詢的時候需要使用like模糊查詢,模糊查詢數據庫中使用的是全表掃描算法,這樣效率低級,所以需要使用全文檢索,來優化查詢速度.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM