solr全文檢索學習

_ME發表於2020-11-14

 

序言:

前面我們說了全域性檢索Lucene,但是我們發現Lucene在使用上還是有些不方便的,例如想要看索引的內容時,就必須自己調api去查,再例如一些新增文件,需要寫的程式碼還是比較多的

另外我們之前說過Lucene只是一個全文檢索的工具包,並不算一個完整的搜尋引擎。很多功能還是需要我們自己去完善,去實現的。

solr 和 ElasticSearch 是基於Lucene開發的功能比較完備的全文檢索引擎。

 

Solr是一個高效能,採用Java開發,基於Lucene的全文搜尋伺服器。同時對其進行了擴充套件,提供了比Lucene更為豐富的查詢語言,同時實現了可配置、可擴充套件並對查詢效能進行了優化,並且提供了一個完善的功能管理介面,是一款非常優秀的全文搜尋引擎。

Solr實際上是一個Web應用,需要servlet容器,可以用Tomcat或者Jetty,新版本Solr預設是用Jetty的,如果要改Tomcat,則要自己做配置。

Solr提供了一些http介面,通過POST請求進行文件的操作,GET請求進行文件的查詢。

 

要想是用solr,我們就需要先下載 https://lucene.apache.org/solr/ 

相關文件:https://lucene.apache.org/solr/guide/8_7/getting-started.html

 

solr目錄說明:

下載後解壓目錄如下

整合Tomcat的你們自己百度,我直接用內建的Jetty

 

solr服務啟動:

啟動solr時,不要直接雙擊startup.cmd,不然視窗一閃而過,最後還是沒有啟動專案

 

 

 在命令列裡面執行,啟動後不要關閉命令列,因為預設是後臺程式,關閉命令列後伺服器也會關閉

啟動:solr start [-p 埠  -s  home目錄地址]

關閉:直接關閉命令列,或者,solr stop -all  關閉全部,或者,solr stop -p 8983   只關閉8983埠的伺服器

重啟:solr restart -p 埠

幫助:solr --help ,或者,solr start -help

 

 

 

 啟動完後直接瀏覽器訪問 http://localhost:8983/ 

 一般來說solr的管理後臺介面是不需要認證的,但是我們不可能讓所有人都可以訪問,所以我們需要修改認證

 

Jetty伺服器啟動認證:

首先在解壓包的 /server/etc 目錄中隨便建個properties檔案,檔名隨意,填入使用者密碼角色

 

 

 然後在 /server/contexts 目錄中的 solr-jetty-context.xml 複製那段程式碼,檔名要和剛剛你建的檔名一致

    <Get name="securityHandler">
        <Set name="loginService">
            <New class="org.eclipse.jetty.security.HashLoginService">
                <Set name="name">verify—name</Set>
                <Set name="config">
                    <SystemProperty name="jetty.home" default="." />/etc/verify.properties
                </Set>
            </New>
        </Set>
    </Get>

 

接著去 \server\solr-webapp\webapp\WEB-INF 目錄中的 web.xml檔案中註釋掉選中的程式碼

 

 

 緊接著加上一段程式碼,指定資源認證

<security-constraint>
      <web-resource-collection>
          <web-resource-name>Solr</web-resource-name>
          <url-pattern>/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>DELETE</http-method>
          <http-method>POST</http-method>
          <http-method>PUT</http-method>
      </web-resource-collection>
      <auth-constraint>
          <role-name>admin</role-name>
          <role-name>user</role-name>
      </auth-constraint>
  </security-constraint>

  <login-config>      
      <auth-method>BASIC</auth-method> 
      <realm-name>verify-name</realm-name>   
  </login-config>

效果:

 

 

 

 

相關參考部落格:

https://blog.csdn.net/u011561335/article/details/90695860

https://www.cnblogs.com/w1995w/p/10566218.html

 

 

整合Tomcat時開啟認證:

https://www.cnblogs.com/anny0404/p/5570666.html

 

 

solr的管理介面功能:

Dashboard:

儀表盤,顯示了該Solr例項開始啟動執行的時間、版本、系統資源、jvm等資訊

 

 

 

 Logging:

日誌資訊,還可以修改記錄的日誌級別

 

 

 Core :

core核心管理,類似與MySQL裡面表的概念

 

注意,新建一個core核心,不能簡單的在頁面上操作就行,需要在本地磁碟新增一些配置檔案,否則就會報錯。 

 

 

 

首先你要去你的solr的home目錄建一個資料夾,取名隨意,建議和新core的名字一致

 

 

 

 

緊接著去 \server\solr\configsets\_default 目錄裡面複製conf這個資料夾,裡面有必須的配置檔案

 

 

回到我們剛剛新建的目錄,貼上下去,重啟solr

 

 

 點選add core,然後重新整理頁面,就出現新的了

 java properties

Solr在JVM 執行環境中的屬性資訊,包括類路徑、檔案編碼、jvm記憶體設定等資訊。

Tread Dump:

顯示Solr Server中當前活躍執行緒資訊,同時也可以跟蹤執行緒執行棧資訊。

 

Core selector

選擇一個SolrCore進行詳細操作

 

 

Overview:

概況

 

analysis:

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

 

 

Dataimport:

可以定義資料匯入處理器,從關聯式資料庫將資料匯入 到Solr索引庫中。

 

 

Documents:

通過此選單可以建立索引、更新索引、刪除索引等操作

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

 

 Files:

 

 

Plugins:

 

 

Query:

可以進行各種查詢

 

 

Replication :

 

 

Schema:

 

 

配置ik分詞器:

solr的管理介面基本就這樣了

下面我們配置以下ik分詞器

配置上和之前我們弄Lucene是一樣的,這是官網地址  https://github.com/magese/ik-analyzer-solr  

下載jar包放到 /WEB-INF/lib  資料夾中

 

 

複製幾個配置檔案和詞典到classes資料夾中

 

 

 如果要用那幾個詞典的話,要自己加上

 

 

去你要使用ik分詞器的core的約束檔案上進行修改

 

 加入以下這一段話

<fieldType name="text_ik" class="solr.TextField">
    <analyzer type="index">
      <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" conf="ik.conf" useSmart="false"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" conf="ik.conf" useSmart="true"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
  </fieldType>

 

 接下來你就可以自己給Field指定ik分詞器了

 

 同時頁面也會顯示ik分詞器了

 

 不過需要注意的是,那個core需要用ik分詞器,那個core就需要配置,無法全域性配置

 

solr配置檔案:

有幾個配置檔案我們會經常用到,所以有必要了解下:

core.properties:

裡面存放了核心的名字

 

 

 

solrconfig.xml:

這個類其實主要就是配置一些handler,下面是精簡後的一些程式碼,像

/query
/update 這樣的處理介面,也是在這裡進行配置的,包括我們後期需要加的匯入功能,也是要在這裡加一個handler
<?xml version="1.0" encoding="UTF-8" ?>
<luceneMatchVersion>8.7.0</luceneMatchVersion>
<dataDir>${solr.data.dir:}</dataDir>
<directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}" />
<codecFactory class="solr.SchemaCodecFactory" />
<indexConfig>
    <lockType>${solr.lock.type:native}</lockType>
</indexConfig>
<jmx />
<updateHandler class="solr.DirectUpdateHandler2">
    <updateLog>
        <str name="dir">${solr.ulog.dir:}</str>
        <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int>
    </updateLog>
    <autoCommit>
        <maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
        <openSearcher>false</openSearcher>
    </autoCommit>
    <autoSoftCommit>
        <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime>
    </autoSoftCommit>
</updateHandler>
<query>
    <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses>
    <filterCache class="solr.FastLRUCache" size="512" initialSize="512" autowarmCount="0" />
    <queryResultCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" />
    <documentCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" />
    <cache name="perSegFilter" class="solr.search.LRUCache" size="10" initialSize="0" autowarmCount="10" regenerator="solr.NoOpRegenerator" />
    <enableLazyFieldLoading>true</enableLazyFieldLoading>
    <queryResultWindowSize>20</queryResultWindowSize>
    <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
    <listener event="newSearcher" class="solr.QuerySenderListener">
        <arr name="queries">
        </arr>
    </listener>
    <listener event="firstSearcher" class="solr.QuerySenderListener">
        <arr name="queries">
        </arr>
    </listener>
    <useColdSearcher>false</useColdSearcher>
</query>
<circuitBreakers enabled="true">
</circuitBreakers>
<requestDispatcher>
    <httpCaching never304="true" />
</requestDispatcher>
<requestHandler name="/select" class="solr.SearchHandler">
    <lst name="defaults">
        <str name="echoParams">explicit</str>
        <int name="rows">10</int>
    </lst>
</requestHandler>
<requestHandler name="/query" class="solr.SearchHandler">
    <lst name="defaults">
        <str name="echoParams">explicit</str>
        <str name="wt">json</str>
        <str name="indent">true</str>
    </lst>
</requestHandler>
<initParams path="/update/**,/query,/select,/spell">
    <lst name="defaults">
        <str name="df">_text_</str>
    </lst>
</initParams>
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
    <str name="queryAnalyzerFieldType">text_general</str>
    <lst name="spellchecker">
        <str name="name">default</str>
        <str name="field">_text_</str>
        <str name="classname">solr.DirectSolrSpellChecker</str>
        <str name="distanceMeasure">internal</str>
        <float name="accuracy">0.5</float>
        <int name="maxEdits">2</int>
        <int name="minPrefix">1</int>
        <int name="maxInspections">5</int>
        <int name="minQueryLength">4</int>
        <float name="maxQueryFrequency">0.01</float>
    </lst>
</searchComponent>
<requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
    <lst name="defaults">
        <str name="spellcheck.dictionary">default</str>
        <str name="spellcheck">on</str>
        <str name="spellcheck.extendedResults">true</str>
        <str name="spellcheck.count">10</str>
        <str name="spellcheck.alternativeTermCount">5</str>
        <str name="spellcheck.maxResultsForSuggest">5</str>
        <str name="spellcheck.collate">true</str>
        <str name="spellcheck.collateExtendedResults">true</str>
        <str name="spellcheck.maxCollationTries">10</str>
        <str name="spellcheck.maxCollations">5</str>
    </lst>
    <arr name="last-components">
        <str>spellcheck</str>
    </arr>
</requestHandler>
<searchComponent name="terms" class="solr.TermsComponent" />
<requestHandler name="/terms" class="solr.SearchHandler" startup="lazy">
    <lst name="defaults">
        <bool name="terms">true</bool>
        <bool name="distrib">false</bool>
    </lst>
    <arr name="components">
        <str>terms</str>
    </arr>
</requestHandler>
<searchComponent class="solr.HighlightComponent" name="highlight">
    <highlighting>
        <fragmenter name="gap" default="true" class="solr.highlight.GapFragmenter">
            <lst name="defaults">
                <int name="hl.fragsize">100</int>
            </lst>
        </fragmenter>
        <fragmenter name="regex" class="solr.highlight.RegexFragmenter">
            <lst name="defaults">
                <int name="hl.fragsize">70</int>
                <float name="hl.regex.slop">0.5</float>
                <str name="hl.regex.pattern">[-\w ,/\n\&quot;&apos;]{20,200}</str>
            </lst>
        </fragmenter>
        <formatter name="html" default="true" class="solr.highlight.HtmlFormatter">
            <lst name="defaults">
                <str name="hl.simple.pre">
                    <![CDATA[<em>]]>
                </str>
                <str name="hl.simple.post">
                    <![CDATA[</em>]]>
                </str>
            </lst>
        </formatter>
        <encoder name="html" class="solr.highlight.HtmlEncoder" />
        <fragListBuilder name="simple" class="solr.highlight.SimpleFragListBuilder" />
        <fragListBuilder name="single" class="solr.highlight.SingleFragListBuilder" />
        <fragListBuilder name="weighted" default="true" class="solr.highlight.WeightedFragListBuilder" />
        <fragmentsBuilder name="default" default="true" class="solr.highlight.ScoreOrderFragmentsBuilder">
        </fragmentsBuilder>
        <fragmentsBuilder name="colored" class="solr.highlight.ScoreOrderFragmentsBuilder">
            <lst name="defaults">
                <str name="hl.tag.pre">
                    <![CDATA[
               <b style="background:yellow">,<b style="background:lawgreen">,
               <b style="background:aquamarine">,<b style="background:magenta">,
               <b style="background:palegreen">,<b style="background:coral">,
               <b style="background:wheat">,<b style="background:khaki">,
               <b style="background:lime">,<b style="background:deepskyblue">]]>
                </str>
                <str name="hl.tag.post">
                    <![CDATA[</b>]]>
                </str>
            </lst>
        </fragmentsBuilder>
        <boundaryScanner name="default" default="true" class="solr.highlight.SimpleBoundaryScanner">
            <lst name="defaults">
                <str name="hl.bs.maxScan">10</str>
                <str name="hl.bs.chars">.,!? &#9;&#10;&#13;</str>
            </lst>
        </boundaryScanner>
        <boundaryScanner name="breakIterator" class="solr.highlight.BreakIteratorBoundaryScanner">
            <lst name="defaults">
                <str name="hl.bs.type">WORD</str>
                <str name="hl.bs.language">en</str>
                <str name="hl.bs.country">US</str>
            </lst>
        </boundaryScanner>
    </highlighting>
</searchComponent>
<updateProcessor class="solr.UUIDUpdateProcessorFactory" name="uuid" />
<updateProcessor class="solr.RemoveBlankFieldUpdateProcessorFactory" name="remove-blank" />
<updateProcessor class="solr.FieldNameMutatingUpdateProcessorFactory" name="field-name-mutating">
    <str name="pattern">[^\w-\.]</str>
    <str name="replacement">_</str>
</updateProcessor>
<updateProcessor class="solr.ParseBooleanFieldUpdateProcessorFactory" name="parse-boolean" />
<updateProcessor class="solr.ParseLongFieldUpdateProcessorFactory" name="parse-long" />
<updateProcessor class="solr.ParseDoubleFieldUpdateProcessorFactory" name="parse-double" />
<updateProcessor class="solr.ParseDateFieldUpdateProcessorFactory" name="parse-date">
    <arr name="format">
        <str>yyyy-MM-dd['T'[HH:mm[:ss[.SSS]][z</str>
        <str>yyyy-MM-dd['T'[HH:mm[:ss[,SSS]][z</str>
        <str>yyyy-MM-dd HH:mm[:ss[.SSS]][z</str>
        <str>yyyy-MM-dd HH:mm[:ss[,SSS]][z</str>
        <str>[EEE, ]dd MMM yyyy HH:mm[:ss] z</str>
        <str>EEEE, dd-MMM-yy HH:mm:ss z</str>
        <str>EEE MMM ppd HH:mm:ss [z ]yyyy</str>
    </arr>
</updateProcessor>
<updateProcessor class="solr.AddSchemaFieldsUpdateProcessorFactory" name="add-schema-fields">
    <lst name="typeMapping">
        <str name="valueClass">java.lang.String</str>
        <str name="fieldType">text_general</str>
        <lst name="copyField">
            <str name="dest">*_str</str>
            <int name="maxChars">256</int>
        </lst>
        <bool name="default">true</bool>
    </lst>
    <lst name="typeMapping">
        <str name="valueClass">java.lang.Boolean</str>
        <str name="fieldType">booleans</str>
    </lst>
    <lst name="typeMapping">
        <str name="valueClass">java.util.Date</str>
        <str name="fieldType">pdates</str>
    </lst>
    <lst name="typeMapping">
        <str name="valueClass">java.lang.Long</str>
        <str name="valueClass">java.lang.Integer</str>
        <str name="fieldType">plongs</str>
    </lst>
    <lst name="typeMapping">
        <str name="valueClass">java.lang.Number</str>
        <str name="fieldType">pdoubles</str>
    </lst>
</updateProcessor>
<updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}" processor="uuid,remove-blank,field-name-mutating,parse-boolean,parse-long,parse-double,parse-date,add-schema-fields">
    <processor class="solr.LogUpdateProcessorFactory" />
    <processor class="solr.DistributedUpdateProcessorFactory" />
    <processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
<queryResponseWriter name="json" class="solr.JSONResponseWriter">
    <str name="content-type">text/plain; charset=UTF-8</str>
</queryResponseWriter>
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
    <lst name="defaults">
        <str name="config">data-config.xml</str>
    </lst>
</requestHandler>
</config>

 

 

 

managed-schema:

這是一個約束檔案,用於指定有哪些Field,Field是否索引、是否分析、是否存在、是否多值,用了哪些分詞器

uniqueKey:指定一個field為主鍵,Lucene中為我們弄了個自增的id作為主鍵,但是solr選擇將id放給我們自己決定。新增文件時必須指定id

fieldType:field型別,可以指定具體是用哪個類處理,是否使用分詞器

field:定義一個field,相當於以前Lucene程式碼的  Field fileNameField = new TextField("file_name",fileName, Field.Store.YES);

dynamicField:可以使用萬用字元,當我們不確定field名字時,就可以用動態Field

copyField:拷貝Field,將多個field的值整合在一個新的field中,並對新的field進行索引,dest屬性指定的目標field必須要是多值的,否則會報錯

假設我們有這樣的一個配置,title和name的值都會拷貝一份到content_text中
<field name="my_name" type="text_ik" indexed="true" stored="true"/>
<field name="my_title" type="text_ik" indexed="true" stored="true"/>
<field name="my_content_text" type="text_ik" indexed="true" stored="true" multiValued="true"/>

<copyField source="my_name" dest="my_content_text" />
<copyField source="my_title" dest="my_content_text" />



此時我插入一條文件
{
        "id":"1",
        "my_name":"張三",
        "my_title":"法外狂徒"
  }
那他實際最後會插入成
{
        "id":"1",
        "my_name":"張三",
        "my_title":"法外狂徒",
        "my_content_text":["張三", "法外狂徒"],
}
 

 

solr將以前Lucene的程式碼實現,改成了配置檔案的形式

 

從資料庫匯入:

正常來說,沒進行過任何配置的話,是沒法使用匯入功能的

 

 所以我們需要進行一些配置。

 

 

首先我們要找到這兩個jar包

 

 把他們複製到 \webapp\WEB-INF\lib 這裡面。然後根據你要操作的資料庫,匯入對應的jdbc包,我用的是mysql

 

 

我有一堆資料

 

 

到你要配置匯入的core的配置資料夾中,建立一個data-config.xml檔案,檔名隨意

 

 

<?xml version="1.0" encoding="UTF-8" ?>  
<dataConfig>   
<dataSource type="JdbcDataSource"   
          driver="com.mysql.cj.jdbc.Driver"   
          url="jdbc:mysql://localhost:3306/sys?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8"   
          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>

 

然後修改solrconfig.xml,新增上匯入的handler

 <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
        <lst name="defaults">
            <str name="config">data-config.xml</str>
        </lst>
    </requestHandler>

 

完了後就重啟solr就可以用了

 

 

匯入的可以參考官網這個文件:https://lucene.apache.org/solr/guide/8_7/uploading-structured-data-store-data-with-the-data-import-handler.html

 

管理介面增刪改查:

 

 

 

 

 

 

 

 

 

 

客戶端增刪改查:

有兩個客戶端,solrJ 和 Spring Data Solr ,下面我們兩個都會講到

 solrJ :

先加依賴:

        <!-- solrJ客戶端 -->
        <dependency>
            <groupId>org.apache.solr</groupId>
            <artifactId>solr-solrj</artifactId>
            <version>8.7.0</version>
        </dependency>
        <!-- 編解碼的依賴,如果solr沒有開認證,可以不加 -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.14</version>
        </dependency>

 

 因為我自己solr開啟了認證,所以url地址要加上使用者名稱和密碼

 private final static String solrUrl = "http://root:root@127.0.0.1:8983/solr/new_core";

而且還要自己額外手動建立一個HttpClient
SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();
 目前新版本中solr伺服器加密碼後,solrJ我只找到這樣連結,如果還有別的方法,請留意告訴我一下,謝謝
package com.hongcheng.solrJ;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.SystemDefaultHttpClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.beans.Field;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
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.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.MultiMapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.junit.jupiter.api.Test;
import org.springframework.util.IdGenerator;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

class SolrJDemoTest {

    private final static String solrUrl = "http://root:root@127.0.0.1:8983/solr/new_core";
/**
     * 新增文件
     * 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
     * 第2步:建立一個SolrInputDocument物件,然後新增域。
     * 第3步:將SolrInputDocument新增到索引庫。
     * 第4步:提交。
     * */
    @Test
    public void addDocument() throws IOException, SolrServerException {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrUrl)
                .withHttpClient(httpclient)
                .build();
        // 第2步:建立一個SolrInputDocument物件,然後新增域。
        SolrInputDocument document = new SolrInputDocument();

        document.addField("id", UUID.randomUUID().toString().replace("-",""));
        document.addField("my_name","老鐵");
        document.addField("my_title","666");
        System.err.println(solrClient.getInvariantParams());
        solrClient.add(document);
        solrClient.commit();

        solrClient.close();
    }

    /**
     * 把一個javabean新增進去
     * */
    @Test
    public void addDocumentBean() throws IOException, SolrServerException {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrUrl)
                .withHttpClient(httpclient)
                .build();

        solrClient.addBean(new User(UUID.randomUUID().toString().replace("-",""),"李四","群中惡霸"));

        solrClient.commit();

        solrClient.close();
    }


    /**
     * 更新
     * 其實並沒有實際的更新方法,都是通過add方法新增,如果id值已經存在,就刪除再新增
     * */
    @Test
    public void updataDocument() throws IOException, SolrServerException {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrUrl)
                .withHttpClient(httpclient)
                .build();

        solrClient.addBean(new User("e2d1cdb08857452ebd13a460d49e067f","李四","李四不是群中惡霸"));

        solrClient.commit();

        solrClient.close();
    }


    /**
     * 刪除
     * */
    @Test
    public void daleteDocument() throws IOException, SolrServerException {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrUrl)
                .withHttpClient(httpclient)
                .build();

        solrClient.addBean(new User("e2d1cdb08857452ebd13a460d49e067f","李四","李四不是群中惡霸"));

        solrClient.commit();

        solrClient.close();
    }


    /**
     * 刪除
     * */
    @Test
    public void searchDocument() throws IOException, SolrServerException {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrUrl)
                .withHttpClient(httpclient)
                .build();

        //建立一個query物件
        SolrQuery query = new SolrQuery();
        //設定查詢條件,q是必須要填的,如果不填,會查不出來
        query.setQuery("*:*");
        //過濾條件,
        query.setFilterQueries("product_catalog_name:幽默雜貨");
        //排序條件
        query.setSort("product_price", SolrQuery.ORDER.asc);
        //分頁處理
        query.setStart(0);
        query.setRows(10);
        query.setFields("id","product_name","product_price","product_catalog_name","product_description");
        //設定預設搜尋域
//        query.set("df", "product_keywords");
        //高亮顯示
        query.setHighlight(true);
        //高亮顯示的域
        query.addHighlightField("product_name");
        //高亮顯示的字首
        query.setHighlightSimplePre("<span stype='color:red;'>");
        //高亮顯示的字尾
        query.setHighlightSimplePost("</span>");
        System.err.println(query);
        //執行查詢
        QueryResponse queryResponse = solrClient.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"));

        }


        solrClient.close();
    }

}


@Getter
@Setter
@AllArgsConstructor
class User{
    @Field("id")
    private String id;
    @Field("my_name")
    private String name;
    @Field("my_title")
    private String title;
}

 

 

 

Spring Data Solr:

依賴:

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-solr</artifactId>
        </dependency>

 

配置檔案:

這兩個是我們自己的自定義屬性

spring:
  data:
    solr:
      host: http://root:root@127.0.0.1:8983/solr     #因為我自己solr服務開啟了認證,所以這裡我要加上使用者名稱和密碼
      core: new_core

配置檔案:

注入bean

package com.hongcheng.solrJ;

import org.apache.http.impl.client.SystemDefaultHttpClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.solr.core.SolrTemplate;

@Configuration
public class SolrConfig {
    @Value("${spring.data.solr.host}")
    private String solrHost;

    @Value("${spring.data.solr.core}")
    private String solrCore;
    /**
     * 配置SolrTemplate
     */
    @Bean
    public SolrTemplate solrTemplate() {
        SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();

        // 第1步:和Solr伺服器建立連線。HttpSolrClient物件建立連線。
        HttpSolrClient solrClient = new HttpSolrClient.Builder()
                .withBaseSolrUrl(solrHost)
                .withHttpClient(httpclient)
                .build();
        SolrTemplate template = new SolrTemplate(solrClient);
        return template;
    }
}

 

增刪改查程式碼:

package com.hongcheng.springdatasolr;

import com.hongcheng.solrJ.SolrConfig;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.solr.client.solrj.beans.Field;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.*;
import org.springframework.data.solr.core.query.result.HighlightEntry;
import org.springframework.data.solr.core.query.result.HighlightPage;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@SpringBootTest(classes = SolrConfig.class)
public class SpringDataSolrDemoTest {

    @Resource
    private SolrTemplate solrTemplate;

    /**
     * 新增文件
     * */
    @Test
    public void addDocument(){
        solrTemplate.saveBean("new_core",new SpringDataSolrDemoTest.User(UUID.randomUUID().toString().replace("-",""),"李四","群中惡霸2222"));
        solrTemplate.commit("new_core");
    }
    /**
     * 根據id更新文件
     * */
    @Test
    public void updateDocument(){
        solrTemplate.saveBean("new_core",new SpringDataSolrDemoTest.User("d0e5d628b5724c9ab3d903ba1b88d683","李四","群中惡霸3333"));
        solrTemplate.commit("new_core");
    }
    /**
     * 根據id刪除文件
     * */
    @Test
    public void deleteDocumentById(){
        solrTemplate.deleteByIds("new_core","d0e5d628b5724c9ab3d903ba1b88d683");
        solrTemplate.commit("new_core");
    }
    /**
     * 條件刪除文件
     * */
    @Test
    public void deleteDocumentByQuery(){
        SolrDataQuery solrDataQuery = new SimpleQuery();
        solrDataQuery.addCriteria(new Criteria("my_title").is("惡霸"));

        solrTemplate.delete("new_core",solrDataQuery);
        solrTemplate.commit("new_core");
    }

    /**
     * 根據id查詢文件
     * */
    @Test
    public void searchById(){
        Optional<Product> new_core = solrTemplate.getById("new_core", "4701", Product.class);
        System.err.println(new_core.get());
        solrTemplate.commit("new_core");
    }
    /**
     * 條件查詢文件
     * */
    @Test
    public void searchByQuery(){
        SimpleHighlightQuery simpleHighlightQuery = new SimpleHighlightQuery();
        // 分頁
        simpleHighlightQuery.setPageRequest( PageRequest.of(0,10, Sort.Direction.ASC,"product_price"));
        // 設定投影
        simpleHighlightQuery.addProjectionOnField("id");
        simpleHighlightQuery.addProjectionOnField("product_catalog_name");
        simpleHighlightQuery.addProjectionOnField("product_name");
        simpleHighlightQuery.addProjectionOnField("product_price");
        // 設定條件
        // 可以使用查詢表示式
        // simpleHighlightQuery.addCriteria(new Criteria().expression("product_catalog_name:餐具  AND  product_name:水果 AND  product_price:[10 TO 50}"));
        // 也可以使用api
        simpleHighlightQuery.addCriteria(new Criteria("product_catalog_name").is("餐具"))
                .addCriteria(new Criteria("product_name").is("水果"))
                .addCriteria(new Criteria("product_price").between(10,50,true,false));

        // 設定高亮
        HighlightOptions highlightOptions = new HighlightOptions();
        highlightOptions.setSimplePrefix("<span style='color:red;'>");
        highlightOptions.setSimplePostfix("</span>");
        highlightOptions.addField("product_catalog_name","product_name");
        simpleHighlightQuery.setHighlightOptions(highlightOptions);

        // 查詢
        HighlightPage<Product> new_core = solrTemplate.queryForHighlightPage("new_core", simpleHighlightQuery, Product.class);
        // 列印結果
        System.err.println("總記錄數:" + new_core.getTotalElements());
        for (Product product : new_core.getContent()) {
            // 獲取高亮部分,是不會合併到源文件中的
            List<HighlightEntry.Highlight> highlights = new_core.getHighlights(product);
            highlights.stream().forEach((highlight)->{
                highlight.getSnipplets().forEach(System.err::print);
                System.err.println();
            });
            System.err.println(product);
            System.err.println();
        }
    }


    @Getter
    @Setter
    @AllArgsConstructor
    class User{
        @Field("id")
        private String id;
        @Field("my_name")
        private String name;
        @Field("my_title")
        private String title;
    }

    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    class Product{
        @Field("id")
        private String id;
        @Field("product_catalog_name")
        private String catalogName;
        @Field("product_price")
        private Double price;
        @Field("product_name")
        private String name;
        @Field("product_description")
        private String description;
        @Field("product_picture")
        private String picture;
    }

}

 

 

以上便是全部了,如果哪裡寫錯了,請留言說明,我進行修改,謝謝。

 

相關文章