很早就聽說Lucene的大名了,但一直沒空研究,今天抽了一個下午時間,學習了一下Lucene,寫了一個簡單的Demo,總的感受是使用起來簡單方便,在使用之前,首先要理解以下關鍵的類:
Document
Document 是用來描述文檔的,這里的文檔可以指一個 HTML 頁面,一封電子郵件,或者是一個文本文件。一個 Document 對象由多個 Field 對象組成的。可以把一個 Document 對象想象成數據庫中的一個記錄,而每個 Field 對象就是記錄的一個字段。
Field
Field 對象是用來描述一個文檔的某個屬性的,比如一封電子郵件的標題和內容可以用兩個 Field 對象分別描述。
Analyzer
在一個文檔被索引之前,首先需要對文檔內容進行分詞處理,這部分工作就是由 Analyzer 來做的。Analyzer 類是一個抽象類,它有多個實現。針對不同的語言和應用需要選擇適合的 Analyzer。Analyzer 把分詞后的內容交給 IndexWriter 來建立索引。
IndexWriter
IndexWriter 是 Lucene 用來創建索引的一個核心的類,他的作用是把一個個的 Document 對象加到索引中來。
Directory
這個類代表了 Lucene 的索引的存儲的位置,這是一個抽象類,它目前有兩個實現,第一個是 FSDirectory,它表示一個存儲在文件系統中的索引的位置。第二個是 RAMDirectory,它表示一個存儲在內存當中的索引的位置。
Query
這是一個抽象類,他有多個實現,比如 TermQuery, BooleanQuery, PrefixQuery. 這個類的目的是把用戶輸入的查詢字符串封裝成 Lucene 能夠識別的 Query。
IndexSearcher
IndexSearcher 是用來在建立好的索引上進行搜索的。它只能以只讀的方式打開一個索引,所以可以有多個 IndexSearcher 的實例在一個索引上進行操作。
Hits
Hits 是用來保存搜索結果的。
理解了這些類之后,我寫了一個簡單的demo,包括索引構建和索引搜索,以后如果要做復雜的搜索,可以根據這個demo做擴展,是不是很簡單哈。
1 import java.io.File; 2 import java.io.IOException; 3 import java.util.ArrayList; 4 import java.util.List; 5 import org.apache.lucene.analysis.Analyzer; 6 import org.apache.lucene.analysis.standard.StandardAnalyzer; 7 import org.apache.lucene.document.Document; 8 import org.apache.lucene.document.Field; 9 import org.apache.lucene.document.TextField; 10 import org.apache.lucene.index.DirectoryReader; 11 import org.apache.lucene.index.IndexWriter; 12 import org.apache.lucene.index.IndexWriterConfig; 13 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 14 import org.apache.lucene.search.IndexSearcher; 15 import org.apache.lucene.search.Query; 16 import org.apache.lucene.search.ScoreDoc; 17 import org.apache.lucene.store.Directory; 18 import org.apache.lucene.store.FSDirectory; 19 import org.apache.lucene.util.Version; 20 21 /** 22 * Lucene Demo 23 * Author: herui 24 * DateTime: 2014-3-31 下午3:39:54 25 */ 26 public class LuceneDemo { 27 28 /** 29 * 根據內容,構建索引 30 * @param analyzer 31 * @param directory 32 * @param items 33 * @return 34 */ 35 private boolean buildIndexer(Analyzer analyzer, Directory directory, List<Item> items) { 36 IndexWriter iwriter = null; 37 try { 38 // 配置索引 39 iwriter = new IndexWriter(directory, new IndexWriterConfig( 40 Version.LUCENE_47, analyzer)); 41 // 刪除所有document 42 iwriter.deleteAll(); 43 // 將文檔信息存入索引 44 Document doc[] = new Document[items.size()]; 45 for (int i = 0; i < items.size(); i++) { 46 doc[i] = new Document(); 47 48 Item item = items.get(i); 49 java.lang.reflect.Field[] fields = item.getClass().getDeclaredFields(); 50 for (java.lang.reflect.Field field : fields) { 51 String fieldName = field.getName(); 52 String getMethodName = "get"+toFirstLetterUpperCase(fieldName); 53 Object obj = item.getClass().getMethod(getMethodName).invoke(item); 54 doc[i].add(new Field(fieldName, (String)obj, TextField.TYPE_STORED)); 55 } 56 57 iwriter.addDocument(doc[i]); 58 } 59 } catch (Exception e) { 60 e.printStackTrace(); 61 return false; 62 } finally { 63 try { 64 iwriter.close(); 65 } catch (IOException e) { 66 } 67 } 68 return true; 69 } 70 71 /** 72 * 根據keyword搜索索引 73 * @param analyzer 74 * @param directory 75 * @param keyword 76 * @return 77 */ 78 public List<Item> searchIndexer(Analyzer analyzer, Directory directory, String keyword) { 79 DirectoryReader ireader = null; 80 List<Item> result = new ArrayList<Item>(); 81 try { 82 // 設定搜索目錄 83 ireader = DirectoryReader.open(directory); 84 IndexSearcher isearcher = new IndexSearcher(ireader); 85 86 // 對多field進行搜索 87 java.lang.reflect.Field[] fields = Item.class.getDeclaredFields(); 88 int length = fields.length; 89 String[] multiFields = new String[length]; 90 for (int i = 0; i < length; i++) { 91 multiFields[i] = fields[i].getName(); 92 } 93 MultiFieldQueryParser parser = new MultiFieldQueryParser( 94 Version.LUCENE_47, multiFields, analyzer); 95 96 // 設定具體的搜索詞 97 Query query = parser.parse(keyword); 98 ScoreDoc[] hits = isearcher.search(query, null, 10).scoreDocs; 99 100 for (int i = 0; i < hits.length; i++) { 101 Document hitDoc = isearcher.doc(hits[i].doc); 102 Item item = new Item(); 103 for (String field : multiFields) { 104 String setMethodName = "set"+toFirstLetterUpperCase(field); 105 item.getClass().getMethod(setMethodName, String.class).invoke(item, hitDoc.get(field)); 106 } 107 result.add(item); 108 } 109 } catch (Exception e) { 110 e.printStackTrace(); 111 return null; 112 } finally { 113 try { 114 ireader.close(); 115 directory.close(); 116 } catch (IOException e) { 117 } 118 } 119 return result; 120 } 121 122 /** 123 * 首字母轉大寫 124 * @param str 125 * @return 126 */ 127 public static String toFirstLetterUpperCase(String str) { 128 if(str == null || str.length() < 2){ 129 return str; 130 } 131 return str.substring(0, 1).toUpperCase() + str.substring(1, str.length()); 132 } 133 134 public static void main(String[] args) throws Exception { 135 LuceneDemo demo = new LuceneDemo(); 136 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47); 137 138 List<Item> items = new ArrayList<Item>(); 139 items.add(new Item("1", "first", "This is the text to be greatly indexed.")); 140 items.add(new Item("2", "second", "This is great")); 141 items.add(new Item("3", "third", "I love apple and pear. ")); 142 items.add(new Item("4", "four", "我是中國人")); 143 items.add(new Item("5", "five", "我叫何瑞")); 144 145 // 索引存到內存中的目錄 146 //Directory directory = new RAMDirectory(); 147 // 索引存儲到硬盤 148 File file = new File("d:/lucene"); 149 Directory directory = FSDirectory.open(file); 150 demo.buildIndexer(analyzer, directory, items); 151 List<Item> result = demo.searchIndexer(analyzer, directory, "中國"); 152 153 for (Item item : result) { 154 System.out.println(item.toString()); 155 } 156 } 157 }
Item.java
1 public class Item { 2 3 private String id; 4 private String title; 5 private String content; 6 7 public Item() { 8 } 9 10 public Item(String id, String title, String content) { 11 this.id = id; 12 this.title = title; 13 this.content = content; 14 } 15 16 public String getId() { 17 return id; 18 } 19 public void setId(String id) { 20 this.id = id; 21 } 22 public String getTitle() { 23 return title; 24 } 25 public void setTitle(String title) { 26 this.title = title; 27 } 28 public String getContent() { 29 return content; 30 } 31 public void setContent(String content) { 32 this.content = content; 33 } 34 35 public String toString() { 36 StringBuilder sb = new StringBuilder(); 37 sb.append("[id=").append(id).append(",title=").append(title) 38 .append(",content=").append(content).append("]"); 39 return sb.toString(); 40 } 41 }