首先我們做一個demo,向數據庫中插入10w條數據,總共778M。
接下來,我們搜索下新聞內容中包含“流行”的記錄。
mmd,檢索一下要78s,是誰都要砸了面前的破機子。
下面我們來看看lucene的效果怎麼樣。下載地址:http://incubator.apache.org/lucene.net/download.html
代碼如下 | 複製代碼 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Lucene.Net.Index; using Lucene.Net.Store; using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; using System.Data; using System.Diagnostics; using Lucene.Net.Search; using Lucene.Net.QueryParsers; namespace First { class Program { static string path = @"D:Sample"; static void Main(string[] args) { //創建索引 CreateIndex(); var watch = Stopwatch.StartNew(); //搜索 IndexSearcher search = new IndexSearcher(path); //查詢表達式 QueryParser query = new QueryParser(string.Empty, new StandardAnalyzer()); //query.parse:注入查詢條件 var hits = search.Search(query.Parse("Content:流行")); for (int i = 0; i < hits.Length(); i++) { Console.WriteLine("當前內容:{0}", hits.Doc(i).Get("Content").Substring(0, 20) + "..."); } watch.Stop(); Console.WriteLine("搜索耗費時間:{0}", watch.ElapsedMilliseconds); } static void CreateIndex() { //創建索引庫目錄 var directory = FSDirectory.GetDirectory(path, true); //創建一個索引,採用StandardAnalyzer對句子進行分詞 IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer()); var reader = DbHelperSQL.ExecuteReader("select * from News"); while (reader.Read()) { //域的集合:文檔,類似於表的行 Document doc = new Document(); //要索引的字段 doc.Add(new Field("ID", reader["ID"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.Add(new Field("Title", reader["Title"].ToString(), Field.Store.NO, Field.Index.ANALYZED)); doc.Add(new Field("Content", reader["Content"].ToString(), Field.Store.YES, Field.Index.ANALYZED)); indexWriter.AddDocument(doc); } reader.Close(); //對索引文件進行優化 indexWriter.Optimize(); indexWriter.Close(); } } } |
我靠,448ms,頓時78s黯然失色,當然這個時間是不包含"創建索引“的時間,從時間複雜度上來說,這種預加載索引算是常量。
作爲入門,簡單的介紹下lucene的實現過程,首先lucene主要分成兩步:"索引"和"搜索"。
一:索引:
相信大家對索引還是比較熟悉的,lucene能夠將我們內容切分成很多詞,然後將詞作爲key,建立“倒排索引”,然後放到索引庫中,在上面
的例子中,我們看到了索引過程中使用到了IndexWriter,FSDirectory,StandardAnalyzer,Document和Field這些類,下面簡要分析下。
1:IndexWriter
我們看到該類有一個AddDocument方法,所以我們認爲該類實現了索引的寫入操作。
2:FSDirectory
這個就更簡單了,提供了索引庫的存放位置,比如我們這裏的D:Sample,或許有人問,能不能存放在內存中,在強大的lucene面前當然
可以做到,lucene中的RAMDirectory就可以實現,當然我們的內存足夠大的話,還是可以用內存承載索引庫,進而提高搜索的效率。
3:StandardAnalyzer
這個算是索引過程中最最關鍵的一步,也是我們使用lucene非常慎重考慮的東西,之所以我們能搜索秒殺,關鍵在於我們如何將輸入的內容
進行何種形式的切分,當然不同的切分形式誕生了不同的分析器,StandardAnalyzer就是一個按照單字分詞的一種分析器,詳細的介紹後續文
章分享。
4:Document
在上面的例子可以看到,他是承載field的集合,然後添加到IndexWriter中,有點類似表中的行的概念。
5: Field
提供了對要分析的字段進行何種處理,以KV形式呈現。
①:Field.Store.YES, Field.Index.NOT_ANALYZED 表示對索引字段採取:原樣保存並且不被StandardAnalyzer進行切分。
②: Field.Store.NO, Field.Index.ANALYZED 不保存但是要被StandardAnalyzer切分。
二:搜索
這個比較容易,根據我們輸入的詞lucene能夠在索引庫中快速定位到我們要找的詞,同樣我們可以看到IndexSearcher,QueryParser,Hits。
1:IndexSearcher
這個我們可以理解成以只讀的形式打開由IndexWriter創建的索引庫,search給QueryParser提供了查詢的橋樑。
2:QueryParser
這玩意提供了一個parse方法能夠將我們要查找的詞轉化爲lucene能夠理解了查詢表達式。
3:Hits
這個就是獲取匹配結果的一個指針,優點類似C#中的延遲加載,目的都是一樣,提高性能。