搜索引擎(Solr-索引詳解2)

學習目標

1.掌握SolrJ的使用。
2.掌握索引API
3.掌握結構化數據導入DIHhtml

 

SolrJ介紹

SolrJ是什麼?java

Solr提供的用於JAVA應用中訪問solr服務API的客戶端jar。在咱們的應用中引入solrj:mysql

<dependency>
  <groupId>org.apache.solr</groupId>
  <artifactId>solr-solrj</artifactId>
  <version>7.3.0</version>
</dependency>

SolrJ的核心APIweb

SolrClient、SolrRequest、SolrResponsespring

SolrClient 的子類sql

HttpSolrClient – 與指定的一個solr節點通訊的客戶端
LBHttpSolrClient –負載均衡地訪問一組節點的客戶端
CloudSolrClient – 訪問solrCloud的客戶端
ConcurrentUpdateSolrClient –併發更新索引用的客戶端數據庫

建立客戶端時通用的配置選項apache

Base URL 
Timeoutsjson

建立客戶端時通用的配置選項服務器

Base URL    基礎URL

http://hostname:8983/solr/core1
http://hostname:8983/solr

Timeouts

final String solrUrl = "http://localhost:8983/solr";
return new HttpSolrClient.Builder(solrUrl)
    .withConnectionTimeout(10000)
    .withSocketTimeout(60000)
    .build();

用SolrJ索引文檔

final SolrClient client = getSolrClient();

final SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", UUID.randomUUID().toString());
doc.addField("name", "Amazon Kindle Paperwhite");

final UpdateResponse updateResponse = client.add("techproducts", doc);
// Indexed documents must be committed
client.commit("techproducts");

用SolrJ查詢

final SolrClient client = getSolrClient();

final Map<String, String> queryParamMap = new HashMap<String, String>();
queryParamMap.put("q", "*:*");
queryParamMap.put("fl", "id, name");
queryParamMap.put("sort", "id asc");
MapSolrParams queryParams = new MapSolrParams(queryParamMap);

final QueryResponse response = client.query("techproducts", queryParams);
final SolrDocumentList documents = response.getResults();

print("Found " + documents.getNumFound() + " documents");
for(SolrDocument document : documents) {
  final String id = (String) document.getFirstValue("id");
  final String name = (String) document.getFirstValue("name");

  print("id: " + id + "; name: " + name);
}

Java 對象綁定

public static class TechProduct {
  @Field public String id;
  @Field public String name;

  public TechProduct(String id, String name) {
    this.id = id;  this.name = name;
  }

  public TechProduct() {}
}

索引

final SolrClient client = getSolrClient();

final TechProduct kindle = new TechProduct("kindle-id-4", "Amazon Kindle Paperwhite");
final UpdateResponse response = client.addBean("techproducts", kindle);

client.commit("techproducts");

查詢

final SolrClient client = getSolrClient();

final SolrQuery query = new SolrQuery("*:*");
query.addField("id");
query.addField("name");
query.setSort("id", ORDER.asc);

final QueryResponse response = client.query("techproducts", query);
final List<TechProduct> products = response.getBeans(TechProduct.class);

SolrClient的API

SolrRequest 的API

method   path   queryParams  響應解析器 是否使用V2版API 認證信息

SolrRequest 的子類

SolrResponse 的API

SolrResponse 的子類

public class SolrJClientDemo {

	// baseSolrUrl 示例
	private static String baseSolrUrl = "http://localhost:8983/solr/";
	private static String baseSolrUrlWithCollection = "http://localhost:8983/solr/techproducts";

	/**
	 * HttpSolrClient:與一個solr Server 經過http進行通訊
	 */
	public static SolrClient getHttpSolrClient(String baseSolrUrl) {
		return new HttpSolrClient.Builder(baseSolrUrl)
				.withConnectionTimeout(1000).withSocketTimeout(6000).build();
	}

	public static SolrClient getHttpSolrClient() {
		return new HttpSolrClient.Builder(baseSolrUrl)
				.withConnectionTimeout(1000).withSocketTimeout(6000).build();
	}

	/**
	 * LBHttpSolrClient: 負載均衡的httpSolrClient <br>
	 * 負載均衡方式: 輪詢給定的多個solr server url。
	 * 當某個url不通時,url地址會從活躍列表移到死亡列表中,用下一個地址再次發送請求。<br>
	 * 對於死亡列表中的url地址,會按期(默認每隔1分鐘,可設置)去檢測是否變活了,再加入到活躍列表中。 <br>
	 * 注意: <br>
	 * 一、不可用於主從結構master/slave 的索引場景,由於主從結構必須經過主節點來更新。 <br>
	 * 二、對於SolrCloud(leader/replica),使用CloudSolrClient更好。
	 * 在solrCloud中可用它來進行索引更新,solrCloud中的節點會將請求轉發到對應的leader。
	 */
	public static SolrClient getLBHttpSolrClient(String... solrUrls) {
		return new LBHttpSolrClient.Builder().withBaseSolrUrls(solrUrls)
				.build();
	}

	private static String baseSolrUrl2 = "http://localhost:7001/solr/";

	public static SolrClient getLBHttpSolrClient() {
		return new LBHttpSolrClient.Builder()
				.withBaseSolrUrls(baseSolrUrl, baseSolrUrl2).build();
	}

	/**
	 * 訪問SolrCloud集羣用CloudSolrClient<br>
	 * CloudSolrClient 實例經過訪問zookeeper獲得集羣中集合的節點列表,<br>
	 * 而後經過LBHttpSolrClient來負載均衡地發送請求。<br>
	 * 注意:這個類默認文檔的惟一鍵字段爲「id」,若是不是的,經過 setIdField(String)方法指定。
	 */
	public static SolrClient getCloudSolrClient(List<String> zkHosts,
			Optional<String> zkChroot) {
		return new CloudSolrClient.Builder(zkHosts, zkChroot).build();
	}

	private static String zkServerUrl = "localhost:9983";

	public static SolrClient getCloudSolrClient() {
		List<String> zkHosts = new ArrayList<String>();
		zkHosts.add(zkServerUrl);
		Optional<String> zkChroot = Optional.empty();
		return new CloudSolrClient.Builder(zkHosts, zkChroot).build();
	}

	public static void main(String[] args) throws Exception {

		// HttpSolrClient 示例:
		SolrClient client = SolrJClientDemo.getHttpSolrClient();

		SolrInputDocument doc = new SolrInputDocument();
		doc.addField("id", UUID.randomUUID().toString());
		doc.addField("name", "HttpSolrClient");

		UpdateResponse updateResponse = client.add("techproducts", doc);
		// 記得要提交
		client.commit("techproducts");

		System.out.println("------------ HttpSolrClient ------------");
		System.out.println("add doc:" + doc);
		System.out.println("response: " + updateResponse.getResponse());

		client.close();

		// LBHttpSolrClient 示例
		client = SolrJClientDemo.getLBHttpSolrClient();
		doc.clear();
		doc.addField("id", UUID.randomUUID().toString());
		doc.addField("name", "LBHttpSolrClient");

		updateResponse = client.add("techproducts", doc);
		// 記得要提交
		client.commit("techproducts");
		System.out.println("------------ LBHttpSolrClient ------------");
		System.out.println("add doc:" + doc);
		System.out.println("response: " + updateResponse.getResponse());

		client.close();

		// CloudSolrClient 示例
		client = SolrJClientDemo.getCloudSolrClient();
		doc.clear();
		doc.addField("id", UUID.randomUUID().toString());
		doc.addField("name", "CloudSolrClient");

		updateResponse = client.add("techproducts", doc);
		// 記得要提交
		client.commit("techproducts");
		System.out.println("------------ CloudSolrClient ------------");
		System.out.println("add doc:" + doc);
		System.out.println("response: " + updateResponse.getResponse());

		client.close();
	}

}

 

索引 API 詳解

Solr提供的數據提交方式簡介

Solr中數據提交進行索引都是經過http請求,針對不一樣的數據源solr提供了幾種方式來方便提交數據。

1.基於Apache  Tika 的 solr cell(Solr Content Extraction Library ),來提取上傳文件內容進行索引。
2.應用中經過Index handler(即 index API)來提交數據。
3.經過Data Import Handler 來提交結構化數據源的數據

Post工具:提交服務器上的xml、json、csv數據文件及pdf、excel等富文本文件。

Index handler 是什麼?

Index handler 索引處理器,是一種Request handler 請求處理器。
solr對外提供http服務,每類服務在solr中都有對應的request handler來接收處理,solr中提供了默認的處理器實現,若有須要咱們也可提供咱們的擴展實現,並在conf/solrconfig.xml中進行配置。

在 conf/solrconfig.xml中,requestHandler的配置就像咱們在web.xml中配置servlet-mapping(或spring mvc 中配置controller 的requestMap)同樣:配置該集合/內核下某個請求地址的處理類。    

Solrconfig中經過updateHandler元素配置了一個統一的更新請求處理器支持XML、CSV、JSON和javabin更新請求(映射地址爲/update),它根據請求提交內容流的內容類型Content-Type將其委託給適當的ContentStreamLoader來解析內容,再進行索引更新。

配置一個requestHandler示例

<requestHandler name=「/update" class="solr.UpdateRequestHandler" />

Xml 格式數據索引更新

請求頭中設置 Content-type: application/xml or Content-type: text/xml

添加、替換文檔

<add> 操做,支持兩個可選屬性: commitWithin:限定在多少毫秒內完成 overwrite:指定當惟一鍵已存在時是否覆蓋,默認true。

<add>
  <doc>
    <field name="authors">Patrick Eagar</field>
    <field name="subject">Sports</field>
    <field name="dd">796.35</field>
    <field name="numpages">128</field>
    <field name="desc"></field>
    <field name="price">12.40</field>
    <field name="title">Summer of the all-rounder</field>
    <field name="isbn">0002166313</field>
    <field name="yearpub">1982</field>
    <field name="publisher">Collins</field>
  </doc>
  <doc>
  ...
  </doc>
</add>

刪除文檔

<delete>
  <id>0002166313</id>
  <id>0031745983</id>
  <query>subject:sport</query>
  <query>publisher:penguin</query>
</delete>

<delete> 操做,支持兩種刪除方式:
一、根據惟一鍵
二、根據查詢

組合操做

<update>
  <add>
    <doc><!-- doc 1 content --></doc>
  </add>
  <add>
    <doc><!-- doc 2 content --></doc>
  </add>
  <delete>
    <id>0002166313</id>
  </delete>
</update>
<response>
  <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">127</int>
  </lst>
</response>

響應的結果: Status=0表示成功 Qtime是耗時

提交、優化、回滾操做

<commit waitSearcher="false"/>
<commit waitSearcher="false" expungeDeletes="true"/>
<optimize waitSearcher="false"/>
<rollback/>

commit、optimize 屬性說明:
waitSearcher:默認true,阻塞等待打開一個新的IndexSearcher並註冊爲主查詢searcher,來讓提交的改變可見。
expungeDeletes: (commit only) 默認false,合併刪除文檔量佔比超過10%的段,合併過程當中刪除這些已刪除的文檔。
maxSegments: (optimize only) 默認1,優化時,將段合併爲最多多少個段

JSON 格式數據索引更新

請求頭中設置 Content-Type: application/json or Content-Type: text/json

添加、替換一個文檔

{
  "id": "1",
  "title": "Doc 1"
}

添加、替換多個文檔

[
  {
    "id": "1",
    "title": "Doc 1"
  },
  {
    "id": "2",
    "title": "Doc 2"
  }
]

在json中指定操做

{
  "add": {
    "doc": {
      "id": "DOC1",
      "my_field": 2.3,
      "my_multivalued_field": [ "aaa", "bbb" ]   
    }
  },
  "add": {
    "commitWithin": 5000, 
    "overwrite": false,  
    "doc": {
      "f1": "v1", 
      "f1": "v2"
    }
  },
  "commit": {},
  "optimize": { "waitSearcher":false },
  "delete": { "id":"ID" },  
  "delete": { "query":"QUERY" } 
}

根據惟一鍵刪除的簡寫方式:

{ "delete":"myid" }
{ "delete":["id1","id2"] }

針對 JSON 格式數據提供的兩個專用path

不須要在請求頭中設置 Content-Type: application/json or Content-Type:text/json

 

結構化數據導入DIH

Solr結構化數據導入簡介

Solr支持從關係數據庫、基於http的數據源(如RSS和ATOM提要)、電子郵件存儲庫和結構化XML 中索引內容。

從外而內,咱們來分析該如何實現數據導入。

一、咱們如何觸發solr進行數據導入?

須要在solrconfig.xml配置一個requestHandler,經過發出http請求來觸發,這個requestHander稱爲Data import Handler (DIH)

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
  <lst name="defaults">
    <str name="config">/path/to/my/DIHconfigfile.xml</str>
  </lst>
</requestHandler>

一、DataImportHandler這個類所在jar並無包含在類目錄中,咱們須要在solrconfig.xml中引入這個jar;

二、在solrconfig.xml中引入DataImportHandler的jar

在solrconfig.xml中找到<lib>的部分,加入下面的

<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-dataimporthandler-.*\.jar" />

二、solr接到請求後,它如何知道該從何處取什麼數據進行索引?

這就須要一個配置文件來定義這些了:

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

配置文件能夠是絕對路徑、或相對集合conf/的相對路徑。

思考:你以爲在DIH的配置文件中須要配置些什麼?以從數據庫導入爲例

從哪取數據?
取什麼數據?
如何取?
取出的數據是否須要經中間處理?
取出的數據如何與模式中的字段對應?

DIH核心概念

Datasource       數據源
Entity             實體:數據庫中的表、視圖
Processor             處理器:執行從實體中加載數據
Transformer     轉換器:對處理器加載的數據進行轉換處理
field     定義實體中的列與模式字段的對應。

字段對應規則說明:

一、自動進行名字相同配對;
二、對於名字不一樣的經過顯式配置 field的column、name屬性指定

來看一些配置示例

在solr安裝目錄中的example/example-DIH/solr/ 下能夠看到好幾個導入示例。
一、請查看各示例的solrconfig.xml中經過<lib>導入了哪些數據導入相關的jar。
二、請查看各示例的DIH配置文件的定義。
三、請重點看看從關係數據庫導入的示例。

從關係數據庫導入實踐

數據庫:mysql 表結構以下

create table t_product(
	prod_id	varchar(64) PRIMARY key,
	name 	varchar(200)	not null,
	simple_intro LONGTEXT,
	price bigint,
	uptime datetime,
	brand_id varchar(64),
	last_modify_time datetime
);

create table t_brand(
	id varchar(64) PRIMARY key,
	name varchar(200) not null,
	last_modify_time datetime
);

create table t_cat(
	id varchar(64) PRIMARY key,
	name varchar(200) not null,
	last_modify_time datetime
);

create table t_prod_cat(
	prod_id varchar(64),
	cat_id	varchar(64) ,
	last_modify_time datetime
);

從關係數據庫導入實踐-步驟

1.拷貝mysql的驅動jar包到solr中。問,能夠放到哪些目錄下?
2.在myproducts集合的solrconfig.xml中配置DIH及<lib>
3.在myproducts集合的conf/目錄下建立dih配置文件dih-data-config.xml
4.配置數據源

 

dataSource 數據源說明

單數據源

<dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/dbname" user="db_username" password="db_password"/>

多數據源

<dataSource type="JdbcDataSource" name="ds-1" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db1-host/dbname" user="db_username" password="db_password"/>
<dataSource type="JdbcDataSource" name="ds-2" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://db2-host/dbname" user="db_username" password="db_password"/>

說明:

1.name、type 是通用屬性,type默認是jdbcDataSource
2.其餘屬性是非固定的,不一樣type可有不一樣的屬性,可隨意擴展。

配置咱們的數據源

<dataSource driver="com.mysql.jdbc.Driver" 
	url="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT" 
		user="root" password="root" />

說明: serverTimezone=GMT是爲解決引入最新版mysql驅動jar報時區錯誤而加入的鏈接請求參數。

 

從關係數據庫導入實踐-步驟 

5.配置document對應的實體(咱們要導入的數據)

<document> 下可包含一個或多個 <entity>數據實體

entity 數據實體通用屬性說明

name (required) : 標識實體的惟一名
processor : 當數據源是非RDBMS 時,必須指定處理器。(默認是SqlEntityProcessor)
transformer : 要應用在該實體上的轉換器。
dataSource : 當配置了多個數據源時,指定使用的數據源的名字。
pk : 實體的主鍵列名。只有在增量導入時才須要指定主鍵列名。和模式中的惟一鍵是兩個不一樣的東西。
rootEntity : 默認document元素的子entity是rootEntity,若是把rootEntity屬性設爲false值,則它的子會被做爲rootEntity(依次類推)。rootEntity返回的每一行會建立一個document。
onError : (abort|skip|continue) . 當處理entity的行爲document的過程當中發生異常該如何處理:默認是 abort,放棄導入。skip:跳過這個文檔,continue:繼續索引該文檔。
preImportDeleteQuery : 在全量導入前,如須要進行索引清理cleanup,能夠經過此屬性指定一個清理的索引刪除查詢,不然用的是‘*:*’(刪除全部)。只有<document>的直接子Entity設置此屬性有效。
postImportDeleteQuery : 指定全量導入後須要進行索引清理的delete查詢。只有<document>的直接子Entity設置此屬性有效.

SqlEntityProcessor 的 entity 屬性說明

query (required) : 從數據庫中加載實體數據用的SQL語句。
deltaQuery : 僅用於增量導入,指定增量數據pk的查詢SQL。
parentDeltaQuery : 指定增量關聯父實體的pk的查詢SQL。
deletedPkQuery :僅用於增量導入,被刪除實體的pk查詢SQL。
deltaImportQuery : (僅用於增量導入) .指定增量導入實體數據的查詢SQL。若是沒有指定該查詢語句,solr將使用query屬性指定的語句,經修改後來查詢加載增量數據(這很容易出錯)。在該語句中每每須要引用deltaQuery查詢結果的列值,經過 ${dih.delta.<column-name>} 來引用,如:select * from tbl where id=${dih.delta.id}

 

從關係數據庫導入實踐-步驟

6.配置查詢數據的SQL

7.配置獲取關聯表數據

entity 關係表示

實體關係(一對1、一對多、多對1、多對多),用子實體來表示,在子實體的SQL查詢語句中可用${parentEntity.columnName}來引用父實體數據的列值。
一個實體可包含多個子實體。

導入API請求參數command(操做)的選項值說明

full-import : 請求啓動全量導入:

http://<host>:<port>/solr/dataimport?command=full-import 返回導入正在進行中的狀態信息,導入會在一個新線程中開啓(可能會須要必定時間完成導入)。導入完成後,導入的開始時間將存入到conf/dataimport.properties 文件中,用於後面的增量導入。增量導入完成後也會存入增量開始的時間到這個文件,用於下一次增量導入。全量導入期間並不會阻塞solr查詢。

可附加的參數有:
entity : 指定導入<document> 下的哪些實體(必須是直接子). 若是沒有給定該參數,則是其下全部子entity。
clean : (default 'true') 指定是否在導入前清理索引
commit : (default ‘true’) 指定導入後是否提交
optimize : (default ‘true’ up to Solr 3.6, ‘false’ afterwards). 是否進行優化。
debug : (default ‘false’). 是否以調試模式運行,開發下使用。
調試模式下不會提交,除非明確指定commit=true. 

delta-import : 請求啓動增量導入http://<host>:<port>/solr/dataimport?command=delta-import . 支持的附加參數: clean, commit, optimize and debug 同 full-import。
status : 導入是異步進行的,經過下面的請求得到導入的進度、結果狀態信息:http://<host>:<port>/solr/dataimport.
reload-config : 請求從新加載dih配置文件:http://<host>:<port>/solr/dataimport?command=reload-config .
abort : 請求停止當前的操做:http://<host>:<port>/solr/dataimport?command=abort .

8.配置增量導入的SQL

增量導入中的特殊變量${dataimporter.last_index_time} 說明

這個變量是上次導入的開始時間。默認存儲在conf/請求處理器名.properties文件中。咱們能夠在<dataConfig>下配置一個propertyWriter元素來設置它。

<propertyWriter dateFormat="yyyy-MM-dd HH:mm:ss" type="SimplePropertiesWriter" directory="data" filename="my_dih.properties" locale="en_US" />

說明:type在非cloud模式下默認是SimplePropertiesWriter,在cloud模式下默認是ZKPropertiesWriter

DIH配置文件中使用請求參數

若是你的DIH配置文件中須要使用請求時傳人的參數,可用${dataimporter.request.paramname}表示引用請求參數。

配置示例:

<dataSource driver="org.hsqldb.jdbcDriver" url="${dataimporter.request.jdbcurl}" user="${dataimporter.request.jdbcuser}" password="${dataimporter.request.jdbcpassword}" />

請求傳參示例:

http://localhost:8983/solr/dih/dataimport?command=full-import&jdbcurl=jdbc:hsqldb:./example-DIH/hsqldb/ex&jdbcuser=sa&jdbcpassword=secret

小結

1.Solr支持從不少種數據源導入數據
2.實現了不少的處理器及轉換器

實際工做過程當中如碰到了別的數據源的導入,必定要想到solr中應該有對應的解決方案。參考地址:
http://lucene.apache.org/solr/guide/7_3/uploading-structured-data-store-data-with-the-data-import-handler.html
https://wiki.apache.org/solr/DataImportHandle
 

DIH操做參考配置

<dataConfig>
    <dataSource driver="org.mysql.jdbc.Driver" 
	url="jdbc:mysql://127.0.0.1:3306/example?useUnicode=true"
	 user="sa" password="123456"/>
    <document>
        <entity name="product" query="select * from t_product" pk="prod_id"
         <!-- deltaQuery :增量操做時進行的查詢  -->
         deltaQuery ="select prod_id from t_product where last_modify_time > '${dataimporter.last_index_time}'" 
         <!-- deltaImportQuery:deltaQuery 以後進行的查詢 prod_id 是entity的pk   -->
         deltaImportQuery ="select * from t_product where prod_id ='${dih.delta.prod_id}'"
        >
	    <field column="prod_id" name="prodId" />
	    <field column="simple_intro" name="simpleIntro" />            
		<!-- 對應品牌 -->
            <entity name="brand"  pk='id'
                 query="select name from t_brand where id='${product.brand_id}'"
                     <!-- deltaQuery :增量操做時進行的查詢  -->
                 deltaQuery ="select id from t_brand  where last_modify_time > '${dataimporter.last_index_time}'" 
                     <!-- parentDeltaQuery: 子操做更新完 父級折行的操做  -->
                 parentDeltaQuery ="select prod_id from t_product where brand_id='#{brand.id}' " 
             >
                <field name="name" column="brand" />
            </entity>
            	<!-- 商品類別 -->
            <entity name="productCat"
                    query="select prod_id,cat_id from t_product_cat where prod_id='${product.prod_id}'">
                <entity name="cat"
                        query="select name from t_cat where id = '${productCat.cat_id}'">
                    <field column="name" name="cat" />
                </entity>
            </entity>
        </entity>
    </document>
</dataConfig>
相關文章
相關標籤/搜索