1. 程式人生 > >Solr全文搜尋伺服器的搭建以及在Java中的使用(solr單機版)

Solr全文搜尋伺服器的搭建以及在Java中的使用(solr單機版)

直接步入正題。。。。。。

Solr的搭建環境:

JDK:1.8.0_161

Tomcat:7.0.57

OS:CentOS 7

Solr服務搭建:

第一步:將solr的壓縮包上傳至Linux系統,下載地址:http://www.apache.org/dyn/closer.lua/lucene/solr/7.3.1    


第二步:解壓,命令:tar zxf solr-4.10.3.tgz 我這裡用的是很早之前下載的,最新版本是7.3.1,解壓完成後得到一個目錄:


第三步:將solr部署到Tomcat中,solr工程的war包位於解壓目錄下的dist資料夾下:

這裡我在/usr/local資料夾中新建了一個solr資料夾,solr資料夾中有一個資料夾tomcat,其中放了一份tomcat,命令:

cp solr-4.10.3.war /usr/local/solr/tomcat/webapps/solr.war


第四步:啟動Tomcat,然後Tomcat就會自動解壓war包,待其解壓好之後關閉tomcat,將webapps下的solr的war包刪除。

第五步:將/root/solr-4.10.3/example/lib/ext目錄下的所有jar包,新增到solr工程中


第六步:建立一個solrhome。解壓後的/example/solr目錄就是一個solrhome。複製此目錄到/usr/local/solr/solrhome


第七步:關聯solr和solrhome。修改solr工程的web.xml檔案:

位置:/usr/local/solr/tomcat/webapps/solr/WEB-INF/web.xml


需要注意的是,<env-entry>標籤原來是在註釋標籤中的,不要忘了刪除上下的註釋標記。

第八步:啟動Tomcat,訪問solr:


        至此,Solr服務的搭建就完成了,但是具體使用還需要進行一些配置:

Solr業務域的配置

        這裡我以一個具體的需求做示範,需求為商品搜尋功能的實現,首先看一下資料表:


然後看一下商品搜尋後的展示頁面:


        可以看到,需要的資訊有:商品標題、商品價格、商品圖片、商品分類,而我們下一步肯定檢視商品詳情,所以還需要商品ID,同時,對於購物網站商品的查詢,肯定不只是根據標題查詢,還會根據賣點查詢,所以賣點資訊也是需要的。

        因此,我們需要在schema.xml中定義

1.商品id

2.商品標題

3.商品賣點

4.商品價格

5.商品圖片

6.分類名稱

        而建立對應的業務域需要制定中文分詞器,這裡我用的是IKAnalyzer。

業務域的建立:

第一步:新增中文分詞器到工程中

        1.將IKAnalyzer的jar包新增到solr工程的lib目錄下

        2.在solr工程的WEB-INF目錄下建立目錄classes,並編寫擴充套件詞典與配置檔案:

                a) 建立配置檔案: touch IKAnalyzer.cfg.xml


                b) 建立擴充套件詞典和擴充套件停止詞典 touch mydict.dic 和 touch ext_stopword.dic 當分詞時遇到擴充套件詞典中的詞,不會對其進行拆分,比如安陽師範學院不會再拆分成為 安陽、師範、學院,而是作為一個整體


因為這裡涉及到了中文,所以編輯是在本地遠端完成的。

第二步:配置一個FieldType,指定使用IKAnalyzer

修改/usr/local/solr/solrhome/collection1/conf/schema.xml檔案,新增如下程式碼,放到最後的</schema>標籤之前即可:


第三步:配置業務域,也是在這個檔案中,其中type使用鋼材定義的fieldType:

<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
<field name="item_price"  type="long" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category_name" type="string" indexed="true" stored="true" />

<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_sell_point" dest="item_keywords"/>
<copyField source="item_category_name" dest="item_keywords"/>

    copyField的作用是,比如這裡我要通過item_keywords搜尋,搜尋k,那麼只要item_title或者item_sell_point或者item_category_name中含有k欄位的,都會查詢出來。

然後重啟Tomcat,使得配置生效。

分詞測試:


        可以看到,“劉強東”已經作為了一個整體。

Solr服務的使用

匯入索引

        在使用之前,我們首先要做的是將資料匯入索引庫中,這裡以Java的方式實現:

實體類:SearchItem.java:

package cn.e3mall.common.pojo;

import java.io.Serializable;

public class SearchItem implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private String id;
	private String title;
	private String sell_point;
	private long price;
	private String image;
	private String category_name;
        // 省略setter、getter方法
}

ItemMapper.java

package cn.e3mall.search.mapper;

import java.util.List;
import cn.e3mall.common.pojo.SearchItem;

public interface ItemMapper {

	List<SearchItem> getItemList();
}

ItemMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.e3mall.search.mapper.ItemMapper">
	<select id="getItemList" resultType="cn.e3mall.common.pojo.SearchItem">
		SELECT
		a.id,
		a.title,
		a.sell_point,
		a.price,
		a.image,
		b. NAME category_name
		FROM
		tb_item a
		LEFT JOIN tb_item_cat b ON a.cid = b.id
		WHERE
		a.`status` = 1
	</select>
</mapper>

Spring配置:

<!-- 單機版solrJ,引數為solr伺服器的url -->
<bean class="org.apache.solr.client.solrj.impl.HttpSolrServer">
	<constructor-arg index="0" value="http://192.168.25.130:8080/solr/collection1"/><!-- /collection1可省略 -->
</bean>

從資料庫中查詢,處理後匯入索引庫:

package cn.e3mall.search.service.impl;

import java.util.List;

import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.e3mall.common.pojo.SearchItem;
import cn.e3mall.common.utils.E3Result;
import cn.e3mall.search.mapper.ItemMapper;
import cn.e3mall.search.service.SearchItemService;

/**
 * 索引庫維護Service
 */

@Service
public class SearchItemServiceImpl implements SearchItemService {

	@Autowired
	private ItemMapper itemMapper;
	@Autowired
	private SolrServer solrServer;

	@Override
	public E3Result importAllItem() {
		try {
			// 查詢商品列表
			List<SearchItem> itemList = itemMapper.getItemList();
			// 遍歷商品列表
			for (SearchItem item : itemList) {
				// 建立文件物件
				SolrInputDocument document = new SolrInputDocument();
				// 向文件物件中新增域
				document.addField("id", item.getId());
				document.addField("item_title", item.getTitle());
				document.addField("item_sell_point", item.getSell_point());
				document.addField("item_price", item.getPrice());
				document.addField("item_image", item.getImage());
				document.addField("item_category_name", item.getCategory_name());
				// 把文件物件寫入索引庫
				solrServer.add(document);
			}
			// 提交
			solrServer.commit();
			// 返回匯入成功
			return E3Result.ok();
		} catch (Exception e) {
			e.printStackTrace();
			return E3Result.fail();
		}
	}
}
        這樣,便可以將資料庫中的資料匯入索引庫,之後查詢商品便不再經過資料庫,而是直接從Solr中檢索,不但可以大大提高查詢速率,查詢準確率也可以大大提高。

簡單查詢示例:

	@Test
	public void queryIndex() throws Exception {
		// 建立一個SolrServer物件,建立一個連結。引數solr伺服器的url
		SolrServer solrServer = new HttpSolrServer("http://192.168.25.130:8080/solr/collection1");
		// 建立一個solrQuery查詢物件
		SolrQuery query = new SolrQuery();
		// 設定查詢條件
		// query.setQuery("*:*");
		query.set("q", "*:*"); // 和上面的等同,*:*表示查詢所有
		// 執行查詢,得到queryResponse物件
		QueryResponse response = solrServer.query(query);
		// 取文件列表(當前頁文件),取查詢結果總記錄數
		SolrDocumentList solrDocumentList = response.getResults();
		long numFound = solrDocumentList.getNumFound();
		System.out.println("查詢結果總記錄數:" + numFound);
		System.out.println("-----------------------------------------------");
		// 遍歷文件列表,從文件中取域的內容。
		for (SolrDocument solrDocument : solrDocumentList) {
			System.out.println(solrDocument.get("id"));
			System.out.println(solrDocument.get("item_title"));
			System.out.println(solrDocument.get("item_sell_point"));
			System.out.println(solrDocument.get("item_price"));
			System.out.println(solrDocument.get("item_image"));
			System.out.println(solrDocument.get("item_category_name"));
			System.out.println("----------------------------------------------------------------------------------------------");
		}
	};

複雜查詢示例:

	@Test
	public void queryIndexFuZa() throws Exception {
		// 建立一個SolrServer物件,建立一個連結。引數solr伺服器的url
		SolrServer solrServer = new HttpSolrServer("http://192.168.25.130:8080/solr/collection1");
		// 建立一個solrQuery查詢物件
		SolrQuery query = new SolrQuery();
		// 設定查詢條件
		query.setQuery("三星");
		// 設定查詢的起始索引,類似於mysql的limit a,b 中的a
		query.setStart(0);
		// 設定查詢的條目數,類似於mysql的limit a,b 中的b
		query.setRows(20);
		// 設定查詢的業務域
		query.set("df", "item_title");
		// 設定開啟高亮顯示
		query.setHighlight(true);
		// 設定要高亮的列
		query.addHighlightField("item_title");
		// 設定高亮顯示的字首
		query.setHighlightSimplePre("<em>");
		// 設定高亮顯示的字尾
		query.setHighlightSimplePost("</em>");
		// 執行查詢,得到queryResponse物件
		QueryResponse queryResponse = solrServer.query(query);
		// 取文件列表(當前頁文件),取查詢結果總記錄數
		SolrDocumentList solrDocumentList = queryResponse.getResults();
		System.out.println("查詢結果總記錄數:" + solrDocumentList.getNumFound());
		System.out.println("-----------------------------------------------");
		// 遍歷文件列表,從文件中取域的內容。
		for (SolrDocument solrDocument : solrDocumentList) {
			System.out.println(solrDocument.get("id"));
			// 取高亮顯示
			Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
			List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
			String title = "";
			if (list != null && list.size() > 0) {
				title = list.get(0);
			} else {
				title = (String) solrDocument.get("item_title");
			}
			System.out.println(title);
			System.out.println(solrDocument.get("item_sell_point"));
			System.out.println(solrDocument.get("item_price"));
			System.out.println(solrDocument.get("item_image"));
			System.out.println(solrDocument.get("item_category_name"));
			System.out.println("----------------------------------------------------------------------------------------------");
		}
	};

        程式碼中幾乎每一行都加有註釋,在這裡就不一一解釋了,最後再說一下Solr查詢返回的結果的格式:


Java程式碼與響應的對照:

  • QueryResponse queryResponse = solrServer.query(query); 對應的是整個響應
  • SolrDocumentList solrDocumentList = queryResponse.getResults();對應的是響應的查詢結果,所以可以通過solrDocumentList.getNumFound()方法獲得總共查詢到的結果數。

這裡我為了方便解釋高亮查詢的結果,再查詢一個:


        盡力了,哈哈,就這樣吧