2015年11月7日 星期六

認識 Solr ( 5.4 ) -- 基礎概念

這一篇文章主要是要讓未接觸過 Solr 的讀者, 了解 Solr 是怎樣的一個軟體, 它的用途是什麼? 並將它和資料庫做個比較, 因為資料庫是程式開發人員常接觸到的平台.
或許經由這樣的比較, 讀者會有一個比較清楚的印象. 筆者參考的文件是尚未 release 的 5.4 版.

1. Solr 這類的平台的用途是什麼? Language support and linguistics in Lucene, Solr and ElasticSearch, and the eco-system 這篇文章可以看出這類平台的用途在於提供多種語言的文章快速全文檢索, 並支援透過語意查詢, 而不只是文字的相似比對.

2. Solr 的兩種模式: Standalone Solr, SolrCloud Cluster. 請看下表的比較.

表 1 : Standalone Solr 和 SolrCloud Cluster 的比較

  Standalone Solr SolrCloud Cluster
Solr 的一般功能
fault tolerance
high availability
load balancing
sharding
scale out

SolrCloud 的安裝可參考 nutch 1.8 + solr 4.9.0 探討系列三 : SolrCloud 安裝 這篇文章.

3. Solr 與 relational database 的比較 :

表 2 : Solr 與 relational database 的比較

relational database Solr
relational database 是將資料正規化, 儲存在 database 裏的 table. 比如說公司收到多張訂單, 訂單裏有訂單編號, 客戶的名稱, 送貨地址, 訂單的明細等等.正規化後, 會將訂單明細存到訂單明細 table, 而將其他資料存到訂單 table, 2 個 table 之間用訂單編號做關聯, 而 1 筆訂單 table 的資料可能關連到 1 到多筆訂單明細的資料. 如果公司有將客戶的資料儲存到客戶的 table, 那訂單 table 可能用客戶代號跟客戶 table 做關聯. 而客戶 table 除了地址之外, 可能還有聯絡電話, 聯絡人等等資料. 當我們要查詢一張訂單, 就可以用這些關聯的欄位, 將整張訂單的資料取出來或只取其中一些欄位的資料. 我們也可以用 SQL 語法一次查詢多張訂單的資料. Solr 是全文檢索軟體. 我們可以想像一個活頁簿, 當我們將一張張食譜加入這個活頁簿, 會將新加入食譜的某一欄位或多個欄位的各個詞語加入到活頁簿的第一頁做索引, 這個索引很特別, 記錄著各個詞語分別發生在那些食譜. 想像我們在 google 輸入一個或多個關鍵字時, 它就會列出有這些關鍵字的文章.
資料庫的資料儲存架構由大至小為 database(-->schema or user)-->table-->column 不像 relational database 一個資料庫有多個 table, 一個 core(SolrCloud 改稱為 collection)會有各個欄位的定義, 請想像上述所說的, 一張張的食譜有同樣的料理名稱, 食材, 製作步驟等欄位, 而這些食譜(documents)就是一個 collecton. 一個 solr server 可以有許多 core(在 SolrCloud 稱為 collection), 可能有食譜的 core, 人才資源庫的 core, 而這些 core 不像 relational database 的 table 可以做 join, 它們是各自獨立的.
relational database 使用 SQL 語法中的資料定義語言(Data Definition Language, DDL) 來建立或修改 database, schema, user, table, index, stored procedure, function, trigger 等等. 而使用資料操縱語言(Data Manipulation Language, DML) 來新增, 刪除, 修改 table 的資料. 另資料庫軟體常提供指令來 import /export 資料庫的資料及 schema, 也提供 backup/restore 資料庫的指令. 也提供圖形化介面方便使用者做上述的所有操作. Solr schema(field 等)的定義儲存在 schema.xml 中, 除了在啟動 server 前修改, 啟動 server 後亦可透過 Schema API 讀取或修改 schema. Solr server 主要的 configuration file solrconfig.xml 儲存 server 的 configuration. 而 solr.xml, core.properties 定義 core 的 configuration. Solr 提供 Configuration APIs 修改 solrconfig.xml 的設定. Solr 提供很多方式將資料加入 index. Solr 亦提供 Client APIs(有多種程式語言版本) 來執行 query, index, delete, commit, and optimize. Solr 亦提供 Backup/Restore API, 也提供簡易的 Admin 介面, 但大多是讀取系統狀態, 訊息而無法進行管理. 上述種種 APIs 都用 http request 來與 server 溝通執行.
relational database 的 table 的 column 需指定 data type, user 可以定義自己的 data type, 且每一 column 可以設定預設值. 除了在 insert 或 update data 的同時可以對資料做處理再存入 table, 也可以在 table 設定 trigger, 在資料 insert, update, delete 的前後做一些這個 table or 其他 table 資料的處理. 上述機制中除了呼叫 server 本身提供的預設 function 以外, 也可以呼叫使用者自訂 stored procedure 或 function 來處理資料. Solr 的 field 需指明是哪一種 field type. field type 也可自己定義. 一般而言, field type 定義時, class 選為 solr.TextField 時會定義 analyzer. analyzer 是由一個 charFilter(非必要), 一個 tokenizer(必要), 一至多個 filter(非必要) 依序來組成, 每一個都是一個 Java Class 實作. tokenizer 用來將field 傳入的文章斷詞, 分成一個個詞語. filter 則用來對詞語做進一步的處理, 如篩選不要的文字, 將字母轉成小寫, 同意詞的轉換, 不同國家語言的特別處理等等. 讀者可以參考以下的說明, 進一步了解. Char Filters can add, change, or remove characters while preserving the original character offsets to support features like highlighting. The job of a tokenizer is to break up a stream of text into tokens, where each token is (usually) a sub-sequence of the characters in the text. An analyzer is aware of the field it is configured for, but a tokenizer is not. Tokenizers read from a character stream (a Reader) and produce a sequence of Token objects (a TokenStream). Like tokenizers, filters consume input and produce a stream of tokens. Filters also derive from org.apache.lucene.analysis.TokenStream. Unlike tokenizers, a filter's input is another TokenStream. The job of a filter is usually easier than that of a tokenizer since in most cases a filter looks at each token in the stream sequentially and decides whether to pass it along, replace it or discard it.
relational database 的 table column 可以定義為 primary key, 確保該欄位的資料不會重複, 也可多個 column 共同定義為 primary key, 則這些 column 組合的資料在 table 裏不會重複. Solr 每一個 core 可以定義一個field 為 unique key, 也可以不設 unique key. 但一般會設 unique key, 且 unique key 只能為單一 field. 讀者可參考以下說明:The uniqueKey element specifies which field is a unique identifier for documents. Although uniqueKey is not required, it is nearly always warranted by your application design. For example, uniqueKey should be used if you will ever update a document in the index. 大部分 data import 時建 index, 都會檢查有沒有設 unique key. 筆者試出使用 Data Import Handler 的方式可以去掉 unique key.

field 的定義

<field name="my_text" type="text_general" indexed="true" stored="true"/>

field Type 的定義 -- index, query 使用不同的 analyzer

<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-FoldToASCII.txt"/>
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
    <!-- in this example, we will only use synonyms at query time
    <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
    -->
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
  <analyzer type="query">
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
    <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>

field Type 的定義 -- index, query 使用相同的 analyzer

<fieldType name="nametext" class="solr.TextField">
  <analyzer>
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StandardFilterFactory"/>
    <filter class="solr.LowerCaseFilterFactory"/>
    <filter class="solr.StopFilterFactory"/>
    <filter class="solr.EnglishPorterFilterFactory"/>
  </analyzer>
</fieldType>

field Type 的定義 -- 使用一個 analyzer(Java class) 來完成 charFilter, tokenizer, filter 對文字的的處理.

<fieldType name="nametext" class="solr.TextField">
  <analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>
</fieldType>

沒有留言:

張貼留言