我 6 年前開始爲 developerWorks 編寫 Solr 和 Lucene(參見 參考資料)。這些年來,Lucene 和 Solr 將自身建設成了一項堅如盤石的技術(Lucene 做爲 Java™ API 的基礎,Solr 做爲搜索服務)。舉例而言,它們支持着 Apple iTunes、Netflix、Wikipedia 等許多公司的基於搜索的應用程序,它們還幫助爲 IBM Watson 答問系統提供支持。數據庫
多年來,大部分人對 Lucene 和 Solr 的使用主要集中在基於文本的搜索上。與此同時,新的、有趣的大數據趨勢以及對分佈式計算和大規模分析的全新(從新)關注正在興起。大數據經常還須要實時的、大規模的信息訪問。鑑於這種轉變,Lucene 和 Solr 社區發現本身走到了十字路口:Lucene 的核心支柱開始在大數據應用程序的壓力下呈現老態,好比對 Twittersphere 的全部消息創建索引(參見 參考資料)。此外,Solr 在原生分佈式索引支持上的匱乏,使得 IT 組織愈來愈難以富有成本效益的方式擴展他們的搜索基礎架構。編程
該社區開始全面改革 Lucene 和 Solr 支柱(並在某些狀況下改革公共 API)。咱們的關注點已轉向實現輕鬆的可伸縮性、近實時的索引和搜索,以及許多 NoSQL 功能 — 同時利用核心引擎功能。此次全面改革的結晶是 Apache Lucene 和 Solr 4.x 版本。這些版本首當其衝的目標是解決下一代、大規模、數據驅動的訪問和分析問題。json
本文將介紹 4.x 的要點功能並展現一些代碼示例。可是首先,您將動手體驗一個實用的應用程序,它演示了將搜索引擎用於搜索之外的用途的一些概念。要充分理解本文,您應熟悉 Solr 和 Lucene 的基礎知識,尤爲是 Solr 請求。若是不熟悉這些知識,請參見 參考資料,獲取可幫助您瞭解 Solr 和 Lucene 的連接。bootstrap
快速入門搜索和分析實戰
搜索引擎僅用於搜索文本,對吧?不對!在其核心,搜索引擎關乎快速的、高效的過濾,而後依據某種類似性概念(一種在 Lucene 和 Solr 中靈活地定義的一個概念)對數據進行歸類。搜索引擎還會有效地處理稀疏數據和模糊數據,這些數據是現代數據應用程序的標誌性特徵。Lucene 和 Solr 可以處理數字,分析複雜的地理空間問題(您很快會看到),等等。這些功能模糊了搜索應用程序與傳統的數據庫應用程序(以及甚至 NoSQL 應用程序)之間的界線。數組
例如,Lucene 和 Solr 如今:
- 支持多種類型的聯接 (join) 和分組選項
- 擁有可選的面向列的存儲
- 提供了多種方式來處理文本,處理枚舉和數字數據類型
- 支持您定義本身的複雜數據類型、數據存儲、歸類和分析功能
一個搜索引擎不是全部數據問題的良方。但文本搜索在過去是 Lucene 和 Solr 的主要用途,這一事實不該阻止您使用它們解決如今或將來的數據需求。您能夠考慮跳出衆所周知的思惟模式(搜索),以新的方式使用搜索引擎。
爲了演示搜索引擎如何執行搜索之外的工做,本節剩餘部分展現了一個將航空相關計數攝取到 Solr 中的應用程序。該應用程序將會查詢數據(其中大部分是文本數據),並使用 D3 JavaScript 庫(參見 參考資料)處理它們,而後再顯示它們。該數據集來自美國運輸部運輸統計局的研究與創新計數管理局 (RITA) 和 OpenFlights。該數據包含某個特定時間段的全部航班的一些詳細信息,好比起飛機場、目標機場、晚點時間、晚點緣由和航空公司信息。經過使用該應用程序查詢此數據,您可分析特定機場之間的晚點、特定機場上的流量增加等信息。
首先讓該應用程序正常運行,而後查看它的一些接口。在此過程當中請牢記,該應用程序可經過各類方式查詢 Solr 與該數據進行交互。
設置
首先,您須要知足如下先決條件:
- Lucene 和 Solr。
- Java 6 或更高版本。
- 一個現代 Web 瀏覽器。(我已在 Google Chrome 和 Firefox 上進行了測試。)
- 4GB 磁盤空間 — 若是不想使用全部航班數據,可以使用更少的空間。
- 在 *nix 上使用
bash
(或相似)shell 進行終端訪問。對於 Windows,您須要使用 Cygwin。我僅在 OS X 上使用bash
shell 進行了測試。 wget
,若是您選擇使用示例代碼包中的下載腳原本下載該數據。您也能夠手動下載航班數據。- Apache Ant 1.8 及更高版本,用於編譯和打包用途,若是您想要運行任何 Java 代碼示例。
請參見 參考資料,獲取 Lucene、Solr、wget
和 Ant 下載站點的連接。
知足這些先決條件以後,執行如下步驟讓應用程序正常運行:
- 下載 本文的示例代碼 ZIP 文件,並將它解壓到您選擇的目錄。我將此目錄稱爲 $SOLR_AIR。
- 在命令行上,更改到 $SOLR_AIR 目錄:
cd $SOLR_AIR
- 啓動 Solr:
./bin/start-solr.sh
- 運行建立用來建模該數據的必要字段的腳本:
./bin/setup.sh
- 在瀏覽器中打開 http://localhost:8983/solr/#/,以顯示新的 Solr Admin UI。圖 1 顯示了一個示例:
圖 1. Solr UI
- 在終端上,查看 bin/download-data.sh 腳本的內容,瞭解將要從 RITA 和 OpenFlights 下載的內容的詳細信息。以手動方式或經過運行如下腳原本下載這些數據集:
./bin/download-data.sh
下載可能會花大量時間,具體狀況取決於您的帶寬。 - 完成下載後,爲部分或全部數據創建索引。
爲全部數據創建索引:bin/index.sh
要爲某一年中的數據創建索引,可以使用 1987 到 2008 之間的任何值做爲年份。例如:bin/index.sh 1987
- 完成索引的建立以後(這可能會花費大量時間,具體狀況取決於您的機器配置),在瀏覽器中打開 http://localhost:8983/solr/collection1/travel。您將看到一個相似圖 2 的 UI:
圖 2. Solr Air UI
瀏覽該數據
Solr Air 應用程序正常運行後,您就能夠瀏覽該數據,查看 UI 以瞭解您能夠詢問的問題類型。在瀏覽器中,您應看到兩個主要的接口點:地圖和搜索框。對於地圖,我首先從 D3 的優秀的 Airport 示例開始介紹(參見 參考資料)。我修改並擴展了該代碼,以便直接從 Solr 加載全部機場信息,而不是從 D3 示例隨帶的示例 SCV 文件進行加載。我還對每一個機場執行了一些初步的統計計算,您能夠將鼠標懸停在一個特定機場上來查看該信息。
我將使用搜索框展現一些可幫助您構建複雜的搜索和分析應用程序的重要功能。要理解該代碼,請參閱 solr/collection1/conf/velocity/map.vm 文件。
重要的關注區域包括:
- 中心點分面
- 統計功能
- 分組
- Lucene 和 Solr 擴展的地理空間支持
每一個區域均可以幫助您回答一些問題,好比抵達一個特定機場的航班的平均晚點時間,或者在兩個機場之間飛行的一個飛機的最多見的晚點時間(根據航線進行肯定,或者根據某個起飛機場與全部鄰近機場之間的距離來肯定)。該應用程序使用 Solr 的統計功能,再結合 Solr 存在已久的分面功能來繪製機場 「點」 的初始地圖並生成基本信息,好比航班總數,平均、最短和最長晚點時間。(單單此功能就是一種查找壞數據或至少查找極端異常值的出色方法。)爲了演示這些區域(並展現如何輕鬆地集成 Solr 與 D3),我實現了一些輕量型 JavaScript 代碼,以執行如下操做:
- 分析查詢。(一個具備生產品質的應用程序可能會在服務器端執行大部分查詢,甚至被用做一個 Solr 查詢分析器插件。)
- 建立各類 Solr 請求。
- 顯示結果。
結果類型包括:
- 按 3 字母機場代碼(好比
RDU
或SFO
)的查找。 - 按路線的查找,好比
SFO TO ATL
或RDU TO ATL
。(不支持多個躍點。) - 在搜索框爲空的時候單擊搜索按鈕,會顯示全部航班的各類統計數據。
- 您可使用
near
運算符查找鄰近的機場,好比near:SFO
或near:SFO TO ATL
。 - 查找各類旅行距離可能的晚點(小於 500 英里、500 到 1000 英里、1000 到 2000 英里、2000 及更遠),就像
likely:SFO
中同樣。 - 任何提供給 Solr 的
/travel
請求處理程序的任意 Solr 查詢,好比&q=AirportCity:Francisco
。
上面列表中前 3 種查詢類型都屬於同一種類型的變體。這些變體展現了 Solr 的中心點分面功能,舉例而言,顯示每條路線、每一個航空公司、每一個航班編號最多見的抵達晚點時間(好比 SFO TO ATL
)。near
選項利用新的 Lucene 和 Solr 空間功能執行大大加強的空間計算,好比複雜多邊形交集。likely
選項展現了 Solr 的分組功能,以顯示距離某個起飛機場的必定範圍內的抵達晚點超過 30 分鐘的機場。全部這些請求類型都經過少許的 D3 JavaScript 來顯示信息,這加強了地圖功能。對於列表中的最後一種請求類型,我只返回了關聯的 JSON。這種請求類型支持您自行瀏覽該數據。若是在您本身的應用程序中使用這種請求類型,那麼您天然地想要採用某種特定於應用程序的方式使用響應。
如今請自行嘗試一些查詢。例如,若是搜索 SFO TO ATL
,您應看到相似圖 3 的結果:
圖 3. 示例 SFO TO ATL 屏幕

在 圖 3 中,地圖左側突出顯示了兩個機場。右側的 Route Stats 列表顯示了每一個航空公司的每一個航班最多見的抵達晚點時間。(我只加載了 1987 年的數據。)例如,該列表會告訴您,Delta 航班 156 有 5 次晚點五分鐘到達亞特蘭大,並且有 4 次提早 6 分鐘到達。
您能夠在瀏覽器的控制檯(好比在 Mac 上的 Chrome 中,選擇 View -> Developer -> Javascript Console)和在 Solr 日誌中查看基礎的 Solr 請求。我使用的 SFO-TO-ATL 請求(在這裏爲了格式化用途而分爲 3 行)是:
/solr/collection1/travel?&wt=json&facet=true&facet.limit=5&fq=Origin:SFO AND Dest:ATL&q=*:*&facet.pivot=UniqueCarrier,FlightNum,ArrDelay& f.UniqueCarrier.facet.limit=10&f.FlightNum.facet.limit=10
facet.pivot
參數提供了此請求中的關鍵功能。facet.pivot
從航空公司(稱爲 UniqueCarrier
)移動到 FlightNum
,再到ArrDelay
,所以提供了 圖 3 的 Route Stats 中顯示的嵌套結構。
若是嘗試一次 near
插入,如 near:JFK
中所示,您的結果將相似於圖 4:
圖 4. 示例屏幕顯示了 JFK 附近的機場

支持 near
查詢的 Solr 請求利用了 Solr 新的空間功能,本文後面將會詳細介紹這項功能。至於如今,您可能已經過查看請求自己(這裏出於格式化用途而進行了精減)而認識到這項新功能的強大之處:
... &fq=source:Airports&q=AirportLocationJTS:"IsWithin(Circle(40.639751,-73.778925 d=3))" ...
您可能已經猜到,該請求查找以緯度 40.639751 和經度 -73.778925 爲中心,以 3 度(大約爲 111 公里)爲半徑的圓圈內的全部機場。
如今您應很好地認識到 Lucene 和 Solr 應用程序可以以有趣的方式對數據(數字、文本或其餘數據)執行切塊和切片。並且由於 Lucene 和 Solr 都是開源的,因此可使用適合商用的許可,您能夠自由地添加本身的自定義。更妙的是,4.x 版的 Lucene 和 Solr 增長了您能夠插入本身的想法和功能的位置數量,您無需大動干戈修改代碼。在接下來查看 Lucene 4(編寫本文時最新版爲 4.4)的一些要點功能和隨後查看 Solr 4 要點功能時,請記住這些功能。
Lucene 4:下一代搜索和分析的基礎
對 Lucene 的一些重要的增補和更改涉及到速度和內存、靈活性、數據結構和分面等類別。(要了解 Lucene 中的變化的全部詳細信息,請查閱每一個 Lucene 發行版中包含的 CHANGES.txt 文件。)
速度和內存
儘管以前的 Lucene 版本被廣泛認爲已足夠快(具體來說,該速度是相對於相似的通常用途搜索庫而言),但 Lucene 4 中的加強使得許多操做比之前的版本快得多。
圖 5 中的圖表採集了 Lucene 索引的性能(以 GB 每小時來度量)。(感謝 Lucene 提交者 Mike McCandless 提供的夜間 Lucene 基礎測試圖表;請參見 參考資料。)圖 5 代表,在 [[?年]] 5 月的前半個月發生了巨大的性能改進:
圖 5. Lucene 索引性能
![Lucene 索引性能圖表,代表在 [[?年]] 5 月的前半個月從每小時 100GB 增加到了大約每小時 270GB](http://static.javashuo.com/static/loading.gif)
圖 5 顯示的改進來自對 Lucene 構建其索引結構和方式和它在構建它們時處理並行性的方式所作的一系列更改(以及其餘一些更改,包括 JVM 更改和固態驅動器的使用)。這些更改專一於在 Lucene 將索引寫入磁盤時消除同步;有關的詳細信息(不屬於本文的介紹範疇),請參閱 參考資料,以獲取 Mike McCandless 的博客文章的連接。
除了提升整體索引性能以外,Lucene 4 還可執行近實時 (NRT) 的索引操做。NRT 操做可顯著減小搜索引擎反映索引更改所花的時間。要使用 NRT 操做,必須在您應用程序中,在 Lucene 的 IndexWriter
和 IndexReader
之間執行必定的協調。清單 1(來自下載包的 src/main/java/IndexingExamples.java 文件的一個代碼段)演示了這種相互做用:
清單 1. Lucene 中的 NRT 搜索示例
... doc = new HashSet<IndexableField>(); index(writer, doc); //Get a searcher IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory)); printResults(searcher); //Now, index one more doc doc.add(new StringField("id", "id_" + 100, Field.Store.YES)); doc.add(new TextField("body", "This is document 100.", Field.Store.YES)); writer.addDocument(doc); //The results are still 100 printResults(searcher); //Don't commit; just open a new searcher directly from the writer searcher = new IndexSearcher(DirectoryReader.open(writer, false)); //The results now reflect the new document that was added printResults(searcher); ...
在 清單 1 中,我首先爲一組文檔創建了索引,並將提交到 Directory
,而後搜索該 Directory
— Lucene 中的傳統方法。在我繼續爲另外一個文檔創建索引時,NRT 就會派上用場:無需執行全面提交,Lucene 從 IndexWriter
建立一個新 IndexSearcher
,而後執行搜索。要運行此示例,可將目錄更改成 $SOLR_AIR 目錄並執行如下命令序列:
ant compile
cd build/classes
java -cp ../../lib/*:.IndexingExamples
備註:我將本文的多個代碼示例分組到 IndexingExamples.java 中,因此您可以使用同一個命令序列運行清單 2 和清單 4 中的示例。
打印到屏幕的輸出爲:
... Num docs: 100 Num docs: 100 Num docs: 101 ...
Lucene 4 還包含內存改進,利用了一些更高級的數據結構(我將在 有限狀態自動機和其餘好功能 中進行更詳細的介紹)。這些改進不只減小了 Lucene 的內存佔用,還大大加快了基於通配符和正則表達式的查詢速度。此外,代碼庫從處理 Java String
對象轉變爲管理字節數組的大量分配。(BytesRef
類目前在 Lucene 中彷佛隨處可見。)結果,String
開銷減少了,Java 堆上的對象數量獲得了更好的控制,這減小了致使全部工做中止的垃圾收集的發生概率。
一些 靈活性加強 還帶來了性能和存儲改進,由於您可爲您的應用程序使用的數據類型選擇更好的數據結構。例如,您接下來將會看到,可在 Lucene 中選擇一種方式來索引/存儲唯一鍵(它們是稠密的且沒有很好地壓縮),選擇一種更適合文本的稀疏性的徹底不一樣的方式來索引/存儲文本。
靈活性
Lucene 4.x 中的靈活性改進爲想要從 Lucene 中榨取最後一點質量和性能的開發人員(和研究人員)提供了大量的機會。爲了加強靈活性,Lucene 提供了兩個新的明肯定義的插件點。兩個插件點都顯著影響着開發和使用 Lucene 的方式。
第一個新插件點設計用來爲您提供對 Lucene 分段 的編碼和解碼的深刻控制。Codec
類定義了這項功能。Codec
爲您提供了控制帖子列表的格式(也就是倒排索引)、Lucene 存儲、加權因子(也稱爲範數 (norm))等的能力。
在一些應用程序中,您可能想要實現本身的 Codec
。但您可能更想要更改用於索引中的一個文檔字段子集的 Codec
。爲了理解這一點,考慮您放入應用程序中的數據類型可能會對您有所幫助。例如,標識字段(例如您的主鍵)一般是唯一的。由於主鍵在一個文檔中僅出現一次,因此您可能但願採用與編碼文章正文文本不一樣的方式對它們進行編碼。在這些狀況下,您不會實際更改 Codec
。相反,您更改的是 Codec
所委託的一個更低級類。
爲了演示之目的,我將展現一個代碼示例,其中使用了我最喜好的 Codec
SimpleTextCodec
。SimpleTextCodec
從名稱就能夠明白其含義:一個用於編碼簡單文本中的索引的 Codec
。(SimpleTextCodec
編寫並經過 Lucene 龐大的測試框架的事實,就是對 Lucene 加強的靈活性的見證。)SimpleTextCodec
太大、太慢,不適合在生產環境中使用,但它是瞭解 Lucene 索引的幕後工做原理的不錯方式,這正是我最喜好它的緣由。清單 2 中的代碼將一個 Codec
實例更改成 SimpleTextCodec
:
清單 2. 在 Lucene 中更改 Codec
實例的示例
... conf.setCodec(new SimpleTextCodec()); File simpleText = new File("simpletext"); directory = new SimpleFSDirectory(simpleText); //Let's write to disk so that we can see what it looks like writer = new IndexWriter(directory, conf); index(writer, doc);//index the same docs as before ...
經過運行 清單 2 的代碼,您會建立一個本地 build/classes/simpletext 目錄。要查看 Codec
的實際運用,可更改到 build/classes/simpletext 並在文本編輯器中打開 .cfs 文件。您可看到,.cfs 文件確實是簡單文本,就像清單 3 中的代碼段同樣:
清單 3. _0.cfs 純文本索引文件的部份內容
... term id_97 doc 97 term id_98 doc 98 term id_99 doc 99 END doc 0 numfields 4 field 0 name id type string value id_100 field 1 name body type string value This is document 100. ...
在很大程度上,只有在您處理極大量索引和查詢量時,或者若是您是一位喜好使用裸機的研究人員或搜索引擎內行,更改 Codec
纔有用。在這些狀況下更改 Codec
以前,請使用實際數據對各類可用的 Codec
執行普遍的測試。Solr 用戶可修改簡單的配置項來設置和更改這些功能。請參閱 Solr 參考指南,瞭解更多的細節(參見 參考資料)。
第二個重要的新插件點讓 Lucene 的計分模型變得徹底可插拔。您再也不侷限於使用 Lucene 的默認計分模型,一些批評者聲稱它太簡單了。若是您喜歡的話,可使用備用的計分模型,好比來自 Randomness 的 BM25 和 Divergence(參見 參考資料),或者您能夠編寫本身的模型。爲何編寫本身的模型?或許您的 「文檔」 表明着分子或基因;您想要採用一種快速方式來對它們分進行類,但術語頻率和文檔頻率並不適用。或者您可能想要試驗您在一篇研究文章中讀到的一種新計分模型,以查看它在您內容上的工做狀況。不管緣由是什麼,更改計分模型都須要您在創建索引時經過 IndexWriterConfig.setSimilarity(Similarity)
方法更改該模型,並在搜索時經過IndexSearcher.setSimilarity(Similarity)
方法更改它。清單 4 演示了對 Similarity
的更改,首先運行一個使用默認Similarity
的查詢,而後使用 Lucene 的 BM25Similarity
從新創建索引並從新運行該查詢:
清單 4. 在 Lucene 中更改 Similarity
conf = new IndexWriterConfig(Version.LUCENE_44, analyzer); directory = new RAMDirectory(); writer = new IndexWriter(directory, conf); index(writer, DOC_BODIES); writer.close(); searcher = new IndexSearcher(DirectoryReader.open(directory)); System.out.println("Lucene default scoring:"); TermQuery query = new TermQuery(new Term("body", "snow")); printResults(searcher, query, 10); BM25Similarity bm25Similarity = new BM25Similarity(); conf.setSimilarity(bm25Similarity); Directory bm25Directory = new RAMDirectory(); writer = new IndexWriter(bm25Directory, conf); index(writer, DOC_BODIES); writer.close(); searcher = new IndexSearcher(DirectoryReader.open(bm25Directory)); searcher.setSimilarity(bm25Similarity); System.out.println("Lucene BM25 scoring:"); printResults(searcher, query, 10);
運行 清單 4 中的代碼並檢查輸出。請注意,計分確實不一樣。BM25 方法的結果是否更準確地反映了一個用戶想要的結果集,最終取決於您和您用戶的決定。我建議您設置本身的應用程序,讓您可以輕鬆地運行試驗。(A/B 測試應有所幫助。)而後不只對比了 Similarity
結果,還要對比了各類查詢結構、Analyzer
和其餘許多方面的結果。
有限狀態自動機和其餘好功能
對 Lucene 的數據結構和算法的全面修改在 Lucene 4 中帶來兩個特別有趣的改進:
- DocValues(也稱爲跨列字段)。
- 有限狀態自動機 (FSA) 和有限狀態轉換器 (Finite State Transducers, FST)。本文剩餘內容將兩者都稱爲 FSA。(在技術上,一個 FST 是訪問它的節點時的輸出值,但這一區別在本文中並不重要。)
DocValues 和 FSA 都爲某些可能影響您應用程序的操做類型提供了重大的新性能優點。
在 DocValues 端,在許多狀況下,應用程序須要很是快地順序訪問一個字段的全部值。或者應用程序須要對快速查找這些值來進行排序或分面,而不會致使從索引構建內存型版本(這個過程也稱爲非反轉 (un-inverting))的成本。DocValues 設計用來知足如下需求類型。
一個沒有大量通配符或模糊查詢的應用程序應該看到使用 FSA 帶來的重大性能改進。Lucene 和 Solr 如今支持利用了 FSA 的查詢自動建議和拼寫檢查功能。並且 Lucene 默認的 Codec
顯著減小了磁盤和內存空間佔用,在幕後使用 FSA 來存儲術語字典(Lucene 在搜索期間用於查詢術語的結構)。FSA 在語言處理方面擁有許多用途,因此您也可能發現 Lucene 的 FSA 功能對其餘應用程序頗有益。
圖 6 顯示了一個使用單詞 mop、pop、moth、star、stop 和 top 以及關聯的權重,從 http://examples.mikemccandless.com/fst.py 構建的 FSA。在這個示例中,您能夠想象從 moth
等輸入開始,將它分解爲它的字符 (m-o-t-h),而後按照 FSA 中的弧線運行。
圖 6. 一個 FSA 示例

清單 5(摘自本文的示例代碼下載中的 FSAExamples.java 文件)顯示了使用 Lucene 的 API 構建您本身的 FSA 的簡單示例:
清單 5. 一個簡單的 Lucene 自動化示例
String[] words = {"hockey", "hawk", "puck", "text", "textual", "anachronism", "anarchy"}; Collection<BytesRef> strings = new ArrayList<BytesRef>(); for (String word : words) { strings.add(new BytesRef(word)); } //build up a simple automaton out of several words Automaton automaton = BasicAutomata.makeStringUnion(strings); CharacterRunAutomaton run = new CharacterRunAutomaton(automaton); System.out.println("Match: " + run.run("hockey")); System.out.println("Match: " + run.run("ha"));
在 清單 5 中,我從各類單詞構建了一個 Automaton
並將它提供給 RunAutomaton
。從名稱能夠看出,RunAutomaton
經過自動化來運行輸入,並在這種狀況下與從 清單 5 末尾的打印語句中捕獲的輸入字符串進行匹配。儘管這個示例很普通,但它爲理解我將留給讀者探索的 Lucene API 中的更多高級功能(和 DocValues)奠基了基礎。(請參見 參考資料 以獲取相關連接。)
分面
在其核心,分面生成必定數量的文檔屬性,爲用戶提供一種縮小其搜索結果的輕鬆方式,無需他們猜想要向查詢中添加哪些關鍵詞。例如,若是有人在一個購物網站上搜索電視,那麼分面功能會告訴他們哪些製造商生產了多少種電視型號。分面也經常用於加強基於搜索的業務分析和報告工具。經過使用更高級的分面功能,您爲用戶提供了以有趣方式對分面進行切片和切塊的能力。
分面好久以來都是 Solr 的標誌性特性(自 1.1 版開始)。如今 Lucene 擁有本身獨立的分面模塊可供 Lucene 應用程序使用。Lucene 的分面模塊在功能上沒有 Solr 豐富,但它確實提供了一些有趣的權衡。Lucene 的分面模塊不是動態的,由於您必須在索引時制定一些分面決策。但它是分層的,並且它沒有將字段動態非反轉 (un-invert) 到內存中的成本。
清單 6(包含在示例代碼的 FacetExamples.java 文件中)顯示了 Lucene 的一些新的分面功能:
清單 6. Lucene 分面示例
... DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(facetDir, IndexWriterConfig.OpenMode.CREATE); FacetFields facetFields = new FacetFields(taxoWriter); for (int i = 0; i < DOC_BODIES.length; i++) { String docBody = DOC_BODIES[i]; String category = CATEGORIES[i]; Document doc = new Document(); CategoryPath path = new CategoryPath(category, '/'); //Setup the fields facetFields.addFields(doc, Collections.singleton(path));//just do a single category path doc.add(new StringField("id", "id_" + i, Field.Store.YES)); doc.add(new TextField("body", docBody, Field.Store.YES)); writer.addDocument(doc); } writer.commit(); taxoWriter.commit(); DirectoryReader reader = DirectoryReader.open(dir); IndexSearcher searcher = new IndexSearcher(reader); DirectoryTaxonomyReader taxor = new DirectoryTaxonomyReader(taxoWriter); ArrayList<FacetRequest> facetRequests = new ArrayList<FacetRequest>(); CountFacetRequest home = new CountFacetRequest(new CategoryPath("Home", '/'), 100); home.setDepth(5); facetRequests.add(home); facetRequests.add(new CountFacetRequest(new CategoryPath("Home/Sports", '/'), 10)); facetRequests.add(new CountFacetRequest(new CategoryPath("Home/Weather", '/'), 10)); FacetSearchParams fsp = new FacetSearchParams(facetRequests); FacetsCollector facetsCollector = FacetsCollector.create(fsp, reader, taxor); searcher.search(new MatchAllDocsQuery(), facetsCollector); for (FacetResult fres : facetsCollector.getFacetResults()) { FacetResultNode root = fres.getFacetResultNode(); printFacet(root, 0); }
清單 6 中的重要代碼(除正常的 Lucene 索引和搜索外)包含在 FacetFields
、FacetsCollector
、TaxonomyReader
和TaxonomyWriter
類的使用中。FacetFields
在文檔中建立了合適的字段條目,在創建索引時可與 TaxonomyWriter
結合使用。在搜索時,可結合使用 TaxonomyReader
與 FacetsCollector
,以獲取每一個類別的正確計數。另請注意,Lucene 的分面模塊建立了一個輔助索引,要讓該索引生效,必須讓它與主要索引保持同步。使用您在前面示例的相同命令中使用的順序來運行 清單 6 的代碼,但將 java
命令中的 FacetExamples
替換爲 IndexingExamples
。您應該獲得:
Home (0.0) Home/Children (3.0) Home/Children/Nursery Rhymes (3.0) Home/Weather (2.0) Home/Sports (2.0) Home/Sports/Rock Climbing (1.0) Home/Sports/Hockey (1.0) Home/Writing (1.0) Home/Quotes (1.0) Home/Quotes/Yoda (1.0) Home/Music (1.0) Home/Music/Lyrics (1.0) ...
請注意,在這個特定的實現中,我未包含 Home
分面的計數,由於包含它們可能須要很高的成本。該選項可經過設置適當的FacetIndexingParams
來提供支持,這裏沒有提供有關介紹。Lucene 的分面模塊擁有我未介紹的額外功能。您能夠查閱 參考資料 中的文章,探索它們本文未涉及的其餘新的 Lucene 功能。如今,咱們來看一看 Solr 4.x。
Solr 4:大規模搜索和分析
從 API 角度講,Solr 4.x 的外觀與之前的版本很類似。可是 4.x 包含衆多加強,使它比以往更容易使用而且更容易擴展。Solr 還使您可以回答新的問題類型,同時利用我剛列出的許多 Lucene 加強。其餘更改主要針對開發人員的上手體驗。例如,全新的 Solr 參考指南(參見 參考資料)爲每一個 Solr 版本(從 4.4 版開始)提供了具備圖書質量的文檔。並且 Solr 新的無模式功能使新數據能快速添加到索引中,無需首先定義一種模式。您稍後將看到 Solr 的無模式功能。讓咱們先來看一下 Solr 中一些新的搜索、分面和相關性加強,您已在 Solr Air 應用程序中看到了其中一些功能的實際應用。
搜索、分面和相關性
一些新 Solr 4 功能設計用來在索引端和 「搜索和分面」 端更容易地構建下一代數據驅動應用程序。表 1 總結了要點功能以及適用的命令和代碼示例:
表 1. Solr 4 中的索引、搜索和分面要點功能
名稱 | 描述 | 示例 |
---|---|---|
中心點分面 | 收集全部分面的子分面計數,經過父分面過濾。請參閱 Solr Air 示例 瞭解更多詳細信息。 | 各類字段上的中心點:http://localhost:8983/solr/collection1/travel?&wt=json&facet=true&facet.limit=5&fq=&q=*:*&facet.pivot=Origin,Dest,UniqueCarrier,FlightNum,ArrDelay&indent=true |
新的相關性功能查詢 | 在一個功能查詢中訪問各類索引級統計數據,好比文檔頻率和檢索詞頻率。 | 在全部返回的文檔中添加檢索詞 Origin:SFO 的 Document 頻率:http://localhost:8983/solr/collection1/travel?&wt=json&q=*:*&fl=*, {!func}docfreq('Origin',%20'SFO')&indent=true 請注意,這個命令也使用了新的 DocTransformers 功能。 |
聯接 | 表示更復雜的文檔關係,而後在搜索時聯接它們。更復雜的聯接計劃將在將來的 Solr 版本中實現。 | 僅返回機場數據集中出現了其起飛機場代碼的航班(並將結果與一個請求對比,而不使用聯接):http://localhost:8983/solr/collection1/travel?&wt=json&indent=true&q={!join%20from=IATA%20to=Origin}*:* |
Codec 支持 |
更改索引的 Codec 和各個字段的發佈格式。 |
對一個字段使用 SimpleTextCodec :<fieldType name="string_simpletext" class="solr.StrField" postingsFormat="SimpleText" /> |
新的更新處理器 | 在創建索引以前,但在文檔發送到 Solr 以後,使用 Solr 的 Update Processor 框架插入更改文檔的代碼。 |
|
原子更新 | 發送文檔中已更新的部分,讓 Solr 負責剩餘部分。 | 從命令行,使用 cURL ,將文檔 243551 的來源更改成 FOO :curl http://localhost:8983/solr/update -H 'Content-type:application/json' -d ' [{"id":"243551","Origin":{"set":"FOO"}}]' |
您可在瀏覽器地址欄(而不是在 Solr Air UI 中)對 Solr Air 演示數據運行 表 1 中的前 3 個示例命令。
有關相關性功能、鏈接和 Codec
— 以及其餘新 Solr 4 功能 — 的更多細節,請參見 參考資料 獲取 Solr Wiki 和其餘位置的相關連接。
擴展、NoSQL 和 NRT
或許最近幾年 Solr 中最重大的變化是,構建一個多節點可擴展搜索解決方案變得簡單了許多。在 Solr 4.x 中,比以往更容易將 Solr 擴展爲數十億條記錄的權威的存儲和訪問機制 — 同時得到 Solr 已爲你們熟知的搜索和分面功能。此外,您能夠在容量需求更改時從新平衡您的集羣,並利用樂觀鎖定、內容原子更新,以及實時數據檢索,即便它還沒有創建索引。Solr 中新的分佈式功能被統稱爲 SolrCloud。
SolrCloud 是如何工做的?Solr 4 在(可選的)分佈式模式下運行時,發送給它的文檔會依據一種哈希機制來路由到集羣中的某個節點(被稱爲前導點 (leader))。前導點負責將文檔索引到一個分片 (shard)。一個分片是一個索引,由一個前導點和 0 或多個副本組成。做爲演示,咱們假設您有 4 個機器和 2 個分片。在 Solr 啓動時,4 個機器中的每個與其餘 3 個通訊。兩個機器被選爲前導點,一個前導點對應一個分片。其餘兩個節點自動成爲一個分片的副本。若是一個前導點由於某種緣由而發生故障,那麼分片副本(在此狀況下是唯一的副本)也會成爲前導點,以保證系統仍能正常運行。您能夠從這個示例得出結論,在生成系統中,必須有足夠多個節點參與其中,才能確保您可處理系統宕機。
要查看 SolrCloud 的實際應用,可以使用一個 -z
標誌運行 Solr Air 示例 中使用的 start-solr.sh 腳本,啓動一個雙節點、雙分片的系統。從 *NIX 命令行,首先關閉您的舊實例:
kill -9 PROCESS_ID
而後從新啓動系統:
bin/start-solr.sh -c -z
-c
標誌將會擦除舊索引。-z
標誌告訴 Solr 啓動 Apache Zookeeper 的一個嵌入式版本。
在瀏覽器中打開 SolrCloud 管理頁面 http://localhost:8983/solr/#/~cloud,以確認有兩個節點參與到集羣中。您如今能夠爲您的內容從新創建索引,它將分散在兩個節點上。對系統的全部查詢也會自動分佈。您對兩個節點的 「匹配全部文檔」 搜索應得到與對一個節點的搜索相同的命中數。
start-solr.sh 腳本使用如下命令對第一個節點啓動 Solr:
java -Dbootstrap_confdir=$SOLR_HOME/solr/collection1/conf -Dcollection.configName=myconf -DzkRun -DnumShards=2 -jar start.jar
該腳本告訴第二個節點 Zookeeper 位於何處:
java -Djetty.port=7574 -DzkHost=localhost:9983 -jar start.jar
嵌入式 Zookeeper 很是適合入門,但爲了確保生產系統的高可用性和容錯能力,請在您的集羣中設置一組獨立的 Zookeeper 實例。
以 SolrCloud 功能爲基礎,提供了對 NRT 和許多相似 NoSQL 的功能的支持,好比:
- 樂觀鎖定
- 原子更新
- 實時獲取(在提交一個特定文檔以前檢索它)
- 受事務日誌支持的耐久性
Solr 中的許多分佈式功能和 NoSQL 功能(好比文檔和事務日誌的自動版本控制)會開箱即用地運行。對於其餘一些功能,表 2 中的描述和示例將頗有幫助:
表 2. Solr 4 中的分佈式功能和 NoSQL 功能的總結
名稱 | 描述 | 示例 |
---|---|---|
實時獲取 | 按 ID 檢索一個文檔,不管它的索引或分發狀態如何。 | 獲取 ID 爲 243551 的文檔:http://localhost:8983/solr/collection1/get?id=243551 |
分片拆分 | 將您的索引拆分爲更小的分片,以便它們可遷移到集羣中的新節點。 | 將 shard1 拆分爲兩個分片:http://localhost:8983/solr/admin/collections?action=SPLITSHARD&collection=collection1&shard=shard1 |
NRT | 使用 NRT 搜索新內容的速度比之前的版本快得多。 | 在您的 solrconfig.xml 文件中打開 <autoSoftCommit> 。例如: <autoSoftCommit> |
文檔路由 | 指定哪些文檔位於哪些節點上。 | 確保一個用戶的全部數據位於某些機器上。請查閱 Joel Bernstein 的博客文章(參見 參考資料)。 |
集合 | 根據須要,使用 Solr 新的集合 API 以編程方式建立、刪除或更新集合。 | 建立一個名爲 hockey 的新集合:http://localhost:8983/solr/admin/collections?action=CREATE&name=hockey&numShards=2 |
無模式化
Solr 的無模式功能使得客戶端可以快速添加內容,而不會產生首先定義一個 schema.xml 文件的開銷。Solr 檢查傳入的數據,並經過一個級聯的值分析器集合傳遞該數據。值分析器猜想數據的類型,而後自動向內部模式添加字段並向索引添加內容。
典型的生產系統(也有一些例外)不該使用無模式,由於值猜想並不老是完美的。例如,Solr 第一次看到一個新字段時,它可能將該字段識別爲一個整數,進而在底層模式中定義一個整數 FieldType
。但您可能發現,在 3 星期後該字段沒法用於搜索,由於在 Solr 看到的剩餘內容中,該字段都由浮點值組成。
可是,無模式對您不多能控制其格式的內容的早期開發或索引特別有幫助。例如,表 2 包含一個使用 Solr 中的集合 API 來建立一個新集合的示例:
http://localhost:8983/solr/admin/collections?action=CREATE&name=hockey&numShards=2)
建立集合後,您可使用無模式向它添加內容。可是,首先請看看當前的模式。做爲實現無模式支持的一部分,Solr 還添加了具象狀態傳輸 (Representational State Transfer, REST) API 來訪問該模式。您可經過在瀏覽器中打開 http://localhost:8983/solr/hockey/schema/fields(或在命令行上使用 cURL
),看到爲 hockey 集合定義的全部字段。您會看到 Solr Air 示例中的全部字段。該模式使用這些字段,由於個人默認配置使用 create
選項做爲新集合的基礎。若是願意的話,您可改寫該配置。(邊注:示例代碼下載文件中包含的 setup.sh 腳本使用新的模式 API 來自動建立全部字段定義。)
要使用無模式添加到集合中,可運行:
bin/schemaless-example.sh
如下 JSON 添加到您以前建立的 hockey 集合中:
[ { "id": "id1", "team": "Carolina Hurricanes", "description": "The NHL franchise located in Raleigh, NC", "cupWins": 1 } ]
經過檢查將此 JSON 添加到集合以前的模式能夠知道,team
、description
和 cupWins
字段是新的。該腳本運行時,Solr 會自動猜想它們的類型,並在模式中建立這些字段。要驗證此操做,可在 http://localhost:8983/solr/hockey/schema/fields 上刷新結果。您如今應看到team
、description
和 cupWins
都已在字段列表中定義。
空間(而不是地理空間)改進
Solr 對基於點的空間搜索的長久支持,使您可以找到距離某個點的必定距離內的全部文檔。儘管 Solr 支持在一個 n 維空間中使用此方法,但大部分人都使用它執行地理空間搜索(例如,查找個人位置附近的全部飯店)。可是直到如今,Solr 仍不支持更加複雜的空間功能,好比對多邊形創建索引或在創建了索引的多邊形內執行搜索。新的空間包中的一些要點包括:
- 經過 Spatial4J 庫(參見 參考資料),可支持許多新的空間類型(好比矩形、圓圈、線條和任意多邊形)並支持 Well Known Text (WKT) 格式
- 已創建索引的多值字段,您可使用它們將多個點編碼到同一個字段中
- 可配置的精度,爲開發人員提供了對準確性而不是計算速度的更多控制
- 內容的快速過濾
- 對
Is Within
、Contains
和IsDisjointTo
的查詢支持 - 對 Java Topological Suite (JTS)(參見 參考資料)的可選支持
- Lucene API 和工件
Solr Air 應用程序的模式擁有多種字段類型,它們設置來利用這種新的空間功能。我定義了兩種字段類型來處理機場數據的經度和緯度:
<fieldType name="location_jts" class="solr.SpatialRecursivePrefixTreeFieldType" distErrPct="0.025" spatialContextFactory= "com.spatial4j.core.context.jts.JtsSpatialContextFactory" maxDistErr="0.000009" units="degrees"/> <fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" distErrPct="0.025" geo="true" maxDistErr="0.000009" units="degrees"/>
location_jts
字段類型顯式使用可選的 JTS 集成來定義一個點,而 location_rpt
字段類型不會這麼作。若是您但願對比簡單矩形更復雜的任何內容創建索引,則須要使用 JTS 版本。該字段的屬性有助於定義系統的準確性。在索引時須要使用這些屬性,由於 Solr 經過 Lucene 和 Spatial4j 以多種方式編碼數據,以確保可在搜索時高效地使用數據。對於您的應用程序,您可能但願對您的數據運行一些測試,以肯定要在索引大小、精度和查詢時性能上執行的權衡。
此外,Solr Air 應用程序中使用的 near
查詢使用了新的空間查詢語法(一個 Circle
上的 IsWithin
)來查找指定的來源和目標機場附近的機場。
新的管理 UI
在這個介紹 Solr 的一節最後我纔想起,我差點忘記展現更加用戶友好的現代 Solr 管理 UI 了。這個新 UI 不只在外觀上面目一新,還添加了針對 SolrCloud、文檔添加等的新功能。
對於初學者,當首次在瀏覽器中打開 http://localhost:8983/solr/#/ 時,您會看到一個儀表板,簡潔地顯示了 Solr 的許多當前狀態:內存使用、工做目錄等,如圖 7 所示:
圖 7. 示例 Solr 儀表板

若是在儀表板左側選擇 Cloud,該 UI 將會顯示 SolrCloud 的詳細信息。例如,您會得到配置狀態、活動節點和前導點,以及集羣拓撲結構的可視化的深刻信息。圖 8 顯示了一個示例。請花費一點時間瞭解一下全部雲 UI 選項。(您必須在 SolrCloud 模式下運行才能看到它們。)
圖 8. 示例 SolrCloud UI

要介紹的最後一個未綁定到特定核心/集合/索引的 UI 區域是 Core Admin 屏幕集合。這些屏幕爲核心的管理提供了即指即點式的控制,包括添加、刪除、從新加載和交換核心。圖 9 顯示了 Core Admin UI:
圖 9. Core Admin UI 的示例

經過從 Core 列表選擇一個核心,您能夠得到特定於該核心的信息和統計數據概述。圖 10 顯示了一個示例:
圖 10. 示例核心概述

大多數針對核心的功能與 4.x 之前的 UI 的功能相似(可是使用起來更加輕鬆),除了 Documents 選項以外。您可使用 Documents 選項直接從 UI 向該集合添加各類格式(JSON、CSV、XML 等)的文檔,如圖 11 所示:
圖 11. 從 UI 添加一個文檔的示例

您甚至可上傳富文當類型,好比 PDF 和 Word。花費片刻時間將一些文檔添加到您的索引中,或者瀏覽其餘針對集合的功能,好比 Query 接口或修改的 Analysis 屏幕。
結束語
下一代搜索引擎技術爲用戶提供了決定如何處理他們的數據的權力。本文詳細介紹了 Lucene 和 Solr 4 的功能,我還但願您更普遍地瞭解了搜索引擎如何解決涉及分析和建議的非基於文本的搜索問題。
Lucene 和 Solr 都在不斷演變,這得益於一個龐大的維護社區,該社區由 30 多個提交者和數百位貢獻者提供支持。該社區正在積極開發兩個主要分支:目前官方發佈的 4.x 分支和主幹 分支,它表明着下一個主要 (5.x) 版本。在官方版本分支上,該社區致力於實現向後兼容性,實現一種專一於當前應用程序的輕鬆升級的增量開發方法。在主幹分支上,該社區在確保與之前版本的兼容性方面受到的限制更少。若是您但願試驗 Lucene 或 Solr 中的前沿技術,那麼請從 Subversion 或 Git 簽出主幹分支代碼(參見 參考資料)。不管您選擇何種路徑,您均可以利用 Lucene 和 Solr 實現超越純文本搜索的基於搜索的強大分析。
致謝
感謝 David Smiley、Erik Hatcher、Yonik Seeley 和 Mike McCandless 提供幫助。
下載
描述 | 名字 | 大小 |
---|---|---|
示例代碼 | code.zip | 60.3MB |
FROM: http://www.ibm.com/developerworks/cn/java/j-solr-lucene/index.html