?
?
?
官網: ??? http://lucene.apache.org/
?lucene中國: ?? lucene.com.cn
?
?
1. 簡介
??Lucene 是 apache 軟件基金會 4 jakarta 項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,
? 提供了 ? 完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。 Lucene 的目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現
? 全文檢索的功能,或者是以此為基礎建立起完整的全文檢索引擎。
2. 下載
? ? http://labs.renren.com/apache-mirror//lucene/java/3.0.3/
3. 測試
? 在 eclipse 中邪見項目 Lucence3.0Test, 將一下包導入到 build 路徑中
? ? ?lucene-core-3.0.2.jar
? ? ?lucene-demos-3.0.2.jar
? ? lucene-analyzers-3.0.2.jar
? ? lucene-fast-vector-highlighter-3.0.2.jar
? ? lucene-highlighter-3.0.2.jar
? ? lucene-memory-3.0.2.jar
? ? 在任意目錄下新建兩個文件夾:
? ? 用來存放 lucence 進行分詞的文件和生成的 index 。如下圖: file1 用來存放進行分詞的文件,里面存放有 N 個 txt 文件, txt 文件的內容任意,如:我們都是中國人, index 文件夾是新建的,里面不要有任何的文件,是用來存儲生成的 index 的
? ?
4. 代碼
? ? 新建創建索引的類,以及測試
import
java.io.BufferedReader;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.IOException;
import
java.io.InputStreamReader;
import
java.util.Date;
import
org.apache.lucene.analysis.Analyzer;
import
org.apache.lucene.document.DateTools;
import
org.apache.lucene.document.Document;
import
org.apache.lucene.document.Field;
import
org.apache.lucene.index.IndexWriter;
import
org.apache.lucene.store.FSDirectory;
import
org.wltea.analyzer.lucene.IKAnalyzer;
public
class
IndexerOK {
private
static
String INDEX_DIR = "D:\\nutchwork\\LucenceTestDir\\index";
//
索引存放目錄
private
static
String DATA_DIR = "D:\\nutchwork\\LucenceTestDir\\file1";
//
小文件存放的目錄
public
static
void
main(String[] args)
throws
Exception {
??
long
start =
new
Date().getTime();
??
int
numIndexed = index(
new
File(INDEX_DIR),
new
File(DATA_DIR));
//
調用
index
方法
??
long
end =
new
Date().getTime();
??
System.out.println("Indexing " + numIndexed + " files took "
????
+ (end - start) + " milliseconds");
}
/**
*
索引
dataDir
下的
.txt
文件,并儲存在
indexDir
下,返回索引的文件數量
*
*
@param
indexDir
*
@param
dataDir
*
@return
int
*
@throws
IOException
*/
public
static
int
index(File indexDir, File dataDir)
throws
IOException {
??
if
(!dataDir.exists() || !dataDir.isDirectory()) {
???
throw
new
IOException(dataDir
?????
+ " does not exist or is not a directory");
??
}
??
Analyzer analyzer =
new
IKAnalyzer();
//
采用的分詞器
??
//
第三個參數 為
true
表示新建,
false
表示添加到原有索引中
??
IndexWriter writer =
new
IndexWriter(FSDirectory.open(indexDir),
????
analyzer,
true
, IndexWriter.MaxFieldLength.LIMITED);
??
indexDirectory(writer, dataDir);
//
調用
indexDirectory
方法
??
int
numIndexed = writer.numDocs();
??
writer.optimize();
??
writer.close();
??
return
numIndexed;
}
/**
*
循環遍歷目錄下的所有
.txt
文件并進行索引
*
*
@param
writer
*
@param
dir
*
@throws
IOException
*/
private
static
void
indexDirectory(IndexWriter writer, File dir)
???
throws
IOException {
??
File[] files = dir.listFiles();
??
for
(
int
i = 0; i < files.length; i++) {
???
File f = files[i];
???
if
(f.isDirectory()) {
????
indexDirectory(writer, f);
// recurse
???
}
else
if
(f.getName().endsWith(".txt")) {
????
indexFile(writer, f);
???
}
??
}
}
/**
*
對單個
txt
文件進行索引
*
*
@param
writer
*
@param
f
*
@throws
IOException
*/
private
static
void
indexFile(IndexWriter writer, File f)
???
throws
IOException {
??
if
(f.isHidden() || !f.exists() || !f.canRead()) {
???
return
;
??
}
??
System.out.println("Indexing " + f.getCanonicalPath());
??
Document doc =
new
Document();
??
// doc.add(new Field("contents", new FileReader(f)));
??
doc.add(
new
Field("filename", f.getCanonicalPath(), Field.Store.YES, Field.Index.ANALYZED));
??
String temp = FileReaderAll(f.getCanonicalPath(), "GBK");
??
System.out.println(temp);
??
doc.add(
new
Field("TTT", temp, Field.Store.YES, Field.Index.ANALYZED));
??
doc.add(
new
Field("path", f.getPath(), Field.Store.YES,
????
Field.Index.ANALYZED));
??
doc.add(
new
Field("modified", DateTools.timeToString(f.lastModified(),
????
DateTools.Resolution.MINUTE), Field.Store.YES,
????
Field.Index.ANALYZED));
??
FileInputStream fis =
new
FileInputStream(f);
??
//
按照
UTF-8
編碼方式將字節流轉化為字符流
??
InputStreamReader isr =
new
InputStreamReader(fis, "utf-8");
??
//
從字符流中獲取文本并進行緩沖
??
BufferedReader br =
new
BufferedReader(isr);
??
doc.add(
new
Field("contents", br));
??
writer.setUseCompoundFile(
false
);
??
writer.addDocument(doc);
}
public
static
String FileReaderAll(String FileName, String charset)
???
throws
IOException {
??
BufferedReader reader =
new
BufferedReader(
new
InputStreamReader(
????
new
FileInputStream(FileName), charset));
??
String line =
new
String();
??
String temp =
new
String();
??
while
((line = reader.readLine()) !=
null
) {
???
temp += line;
??
}
??
reader.close();
??
return
temp;
}
}
運行結果:
? ? ??
Indexing D:\nutchwork\LucenceTestDir\file1\1.txt
我們是中國人
Indexing D:\nutchwork\LucenceTestDir\file1\2.txt
我們是中國人
Indexing D:\nutchwork\LucenceTestDir\file1\3.txt
我們是中國人
Indexing D:\nutchwork\LucenceTestDir\file1\4.txt
我們是中國人
Indexing 4 files took 2293 milliseconds
新建查詢的類以及測試:
? ?
import
java.io.File;
import
java.io.StringReader;
import
java.util.Date;
import
java.util.List;
import
org.apache.lucene.analysis.Analyzer;
import
org.apache.lucene.analysis.TokenStream;
import
org.apache.lucene.document.Document;
import
org.apache.lucene.document.Fieldable;
import
org.apache.lucene.search.IndexSearcher;
import
org.apache.lucene.search.Query;
import
org.apache.lucene.search.ScoreDoc;
import
org.apache.lucene.search.Sort;
import
org.apache.lucene.search.SortField;
import
org.apache.lucene.search.TopDocs;
import
org.apache.lucene.search.highlight.Highlighter;
import
org.apache.lucene.search.highlight.QueryScorer;
import
org.apache.lucene.search.highlight.SimpleFragmenter;
import
org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import
org.apache.lucene.store.FSDirectory;
import
org.wltea.analyzer.lucene.IKAnalyzer;
import
org.wltea.analyzer.lucene.IKQueryParser;
import
org.wltea.analyzer.lucene.IKSimilarity;
public
class
SearchQueryOK {
private
static
String INDEX_DIR = "D:\\nutchwork\\LucenceTestDir\\index";
//
索引所在的路徑
private
static
String KEYWORD = "
中國人
";
//
關鍵詞
private
static
int
TOP_NUM = 100;
//
顯示前
100
條結果
public
static
void
main(String[] args)
throws
Exception {
??
File indexDir =
new
File(INDEX_DIR);
??
if
(!indexDir.exists() || !indexDir.isDirectory()) {
???
throw
new
Exception(indexDir
?????
+ " does not exist or is not a directory.");
??
}
??
search(indexDir, KEYWORD);
//
調用
search
方法進行查詢
}
/**
*
查詢
*
*
@param
indexDir
*
@param
q
*
@throws
Exception
*/
public
static
void
search(File indexDir, String q)
throws
Exception {
??
IndexSearcher is =
new
IndexSearcher(FSDirectory.open(indexDir),
true
);
// read-only
??
String[] field = {"TTT","modified","filename"};
??
long
start =
new
Date().getTime();
// start time
??
//
高亮設置
??
Analyzer analyzer =
new
IKAnalyzer();
//
設定分詞器
??
Query query2 = IKQueryParser.parseMultiField(field, KEYWORD);
??
??
??
//
實例化搜索器
??
IndexSearcher isearcher1 =
new
IndexSearcher(FSDirectory.open(indexDir));
??
//
在索引器中使用
IKSimilarity
相似度評估器
??
??
isearcher1.setSimilarity(
new
IKSimilarity());
?
??
??
??
??
Sort sort =
new
Sort(
new
SortField("path", SortField.DOC,
false
));
??
//TermQuery q1 = new TermQuery(new Term("filename", "1"));
??
//
搜索相似度最高的記錄
??
TopDocs topDocs1 = isearcher1.search(query2,
null
, TOP_NUM,sort);
?
??
ScoreDoc[] hits3 = topDocs1.scoreDocs;
??
SimpleHTMLFormatter simpleHtmlFormatter =
new
SimpleHTMLFormatter(
????
"<span style='color:#ff0000'>", "</span>");
//
設定高亮顯示的格式,也就是對高亮顯示的詞組加上前綴后綴
??
Highlighter highlighter =
new
Highlighter(simpleHtmlFormatter,
????
new
QueryScorer(query2));
??
for
(
int
i = 0; i < hits3.length; i++) {
???
Document doc = is.doc(hits3[i].doc);
???
String docTTT = doc.get("TTT");
???
highlighter.setTextFragmenter(
new
SimpleFragmenter(docTTT.length()));
//
設置每次返回的字符數
.
想必大家在使用搜索引擎的時候也沒有一并把全部數據展示出來吧,當然這里也是設定只展示部分數據
???
TokenStream tokenStream = analyzer.tokenStream("",
?????
new
StringReader(docTTT));
???
String str = highlighter.getBestFragment(tokenStream, docTTT);
???
System.out.println("
高亮設置
: " + str );
??
???
String docModified = doc.get("filename");
???
highlighter.setTextFragmenter(
new
SimpleFragmenter(docModified.length()));
??
???
TokenStream tokenStream2 = analyzer.tokenStream("",
?????
new
StringReader(docModified));
???
String str2 = highlighter.getBestFragment(tokenStream2, docModified);
???
System.out.println("
高亮設置
: " + str2 );
??
?
??
???
???
???
List<Fieldable> list = doc.getFields();
???
for
(
int
j = 0; j < list.size(); j++) {
????
Fieldable fieldable = list.get(j);
????
System.out.println(fieldable.name() + " : "
??????
+ fieldable.stringValue() + "<br>");
???
}
??
}
??
??
?
?
??
??
long
end =
new
Date().getTime();
// end time
??
System.out.println("Found " + hits3.length
????
+ " document(s) (in " + (end - start)
????
+ " milliseconds) that matched query '" + q + "':");
}
}
對索引的操作類:
? ??
import
java.io.File;
import
java.io.IOException;
import
java.sql.Connection;
import
java.sql.SQLException;
import
org.apache.lucene.analysis.Analyzer;
import
org.apache.lucene.analysis.standard.StandardAnalyzer;
import
org.apache.lucene.document.Document;
import
org.apache.lucene.document.Field;
import
org.apache.lucene.index.IndexReader;
import
org.apache.lucene.index.IndexWriter;
import
org.apache.lucene.index.Term;
import
org.apache.lucene.store.Directory;
import
org.apache.lucene.store.FSDirectory;
import
org.apache.lucene.util.Version;
import
org.wltea.analyzer.lucene.IKAnalyzer;
public
class
ManageIndexFile {
private
static
String INDEX_DIR = "D:\\nutchwork\\LucenceTestDir\\index";
//
索引存放目錄
//
刪除索引
public
static
void
DeleteIndex(SearchDocBean bean)
throws
IOException {
??
Directory dir = FSDirectory.open(
new
File(INDEX_DIR));
??
IndexReader reader = IndexReader.open(dir,
false
);
??
Term term =
new
Term("modified", bean.getId());
??
int
count = reader.deleteDocuments(term);
??
reader.close();
??
System.out.println("Successful Delete " + count + " path==" + bean.getId());
}
public
static
void
DeleteIndex(
int
[] posIDS)
throws
IOException {
??
Directory dir = FSDirectory.open(
new
File(INDEX_DIR));
??
IndexReader reader = IndexReader.open(dir,
false
);
??
for
(
int
i = 0; i < posIDS.length; i++) {
???
Term term =
new
Term("posID", Integer.toString(posIDS[i]));
???
reader.deleteDocuments(term);
??
}
??
reader.close();
}
//
更新索引
public
static
void
UpdateIndex(SearchDocBean bean)
throws
IOException {
??
Directory dir = FSDirectory.open(
new
File(INDEX_DIR));
??
IndexReader reader = IndexReader.open(dir,
false
);
??
Term term =
new
Term("modified", bean.getId());
??
reader.deleteDocuments(term);
??
reader.close();
??
IndexWriter writer =
new
IndexWriter(FSDirectory.open(
new
File(
????
INDEX_DIR)),
new
StandardAnalyzer(Version.LUCENE_CURRENT),
????
true
, IndexWriter.MaxFieldLength.LIMITED);
??
Document doc =
new
Document();
??
doc.add(
new
Field("modified", bean.getId(), Field.Store.YES,
????
Field.Index.NOT_ANALYZED));
??
writer.addDocument(doc);
??
writer.optimize();
??
writer.close();
}
//
增加索引
public
static
void
AddIndex(SearchDocBean bean,
???
Connection conn)
throws
IOException, SQLException {
??
Analyzer analyzer =
new
IKAnalyzer();
//
采用的分詞器
??
IndexWriter writer =
new
IndexWriter(FSDirectory.open(
new
File(
????
INDEX_DIR)), analyzer,
false
,
????
IndexWriter.MaxFieldLength.LIMITED);
??
Document doc =
new
Document();
??
doc.add(
new
Field("filename", bean.getFileName(), Field.Store.YES,
????
Field.Index.ANALYZED));
??
doc.add(
new
Field("path", bean.getPath(), Field.Store.YES,
????
Field.Index.ANALYZED));
??
doc.add(
new
Field("dateTime", bean.getId(), Field.Store.YES,
????
Field.Index.ANALYZED));
??
doc.add(
new
Field("TTT", bean.getContents(), Field.Store.YES, Field.Index.ANALYZED));
??
writer.setUseCompoundFile(
false
);
??
writer.addDocument(doc);
??
writer.optimize();
??
writer.close();
}
}
封裝起來的查詢結果:
? ?
public
class
SearchDocBean {
???
private
String id;
???
private
String path;
???
private
String contents;
???
private
String dateTime;
???
public
String getId() {
???????
return
id;
???
}
???
public
void
setId(String id) {
???????
this
.id = id;
???
}
???
public
String getPath() {
???????
return
path;
???
}
???
public
void
setPath(String path) {
???????
this
.path = path;
???
}
???
public
String getContents() {
???????
return
contents;
???
}
???
public
void
setContents(String contents) {
???????
this
.contents = contents;
???
}
???
public
String getDateTime() {
???????
return
dateTime;
???
}
???
public
void
setDateTime(String dateTime) {
???????
this
.dateTime = dateTime;
???
}
???
public
String getFileName() {
???????
return
fileName;
???
}
???
public
void
setFileName(String fileName) {
???????
this
.fileName = fileName;
???
}
???
private
String fileName;
}
? ? 下面是 serach 中國人的 結果:
? ?
?
高亮設置
: <span style='color:#ff0000'>
中國人
</span>
?
高亮設置
: null
filename : D:\nutchwork\LucenceTestDir\file1\1.txt<br>
TTT :
我們是中國人
<br>
path : D:\nutchwork\LucenceTestDir\file1\1.txt<br>
modified : 201107161115<br>
?
高亮設置
: <span style='color:#ff0000'>
中國人
</span>
?
高亮設置
: null
filename : D:\nutchwork\LucenceTestDir\file1\2.txt<br>
TTT :
我們是中國人
<br>
path : D:\nutchwork\LucenceTestDir\file1\2.txt<br>
modified : 201107161115<br>
?
高亮設置
: <span style='color:#ff0000'>
中國人
</span>
?
高亮設置
: null
filename : D:\nutchwork\LucenceTestDir\file1\3.txt<br>
TTT :
我們是中國人
<br>
path : D:\nutchwork\LucenceTestDir\file1\3.txt<br>
modified : 201107161115<br>
?
高亮設置
: <span style='color:#ff0000'>
中國人
</span>
?
高亮設置
: null
filename : D:\nutchwork\LucenceTestDir\file1\4.txt<br>
TTT :
我們是中國人
<br>
path : D:\nutchwork\LucenceTestDir\file1\4.txt<br>
modified : 201107161115<br>
Found 4 document(s) (in 717 milliseconds) that matched query '
中國人
':
整個工程:基本上是從網上找到的代碼,運行了下,算是有一個大概的了解。
?
?
?
?
?
?
?
Lucene 簡介
Lucene 是一個基于 Java 的全文信息檢索工具包,它不是一個完整的搜索應用程序,而是為你的應用程序提供索引和搜索功能。 Lucene 目前是 Apache Jakarta 家族中的一個開源項目。也是目前最為流行的基于 Java 開源全文檢索工具包。
目前已經有很多應用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的幫助系統的搜索功能。 Lucene 能夠為文本類型的數據建立索引,所以你只要能把你要索引的數據格式轉化的文本的, Lucene 就能對你的文檔進行索引和搜索。比如你要對一些 HTML 文檔, PDF 文檔進行索引的話你就首先需要把 HTML 文檔和 PDF 文檔轉化成文本格式的,然后將轉化后的內容交給 Lucene 進行索引,然后把創建好的索引文件保存到磁盤或者內存中,最后根據用戶輸入的查詢條件在索引文件上進行查詢。不指定要索引的文檔的格式也使 Lucene 能夠幾乎適用于所有的搜索應用程序。
圖 1 表示了搜索應用程序和 Lucene 之間的關系,也反映了利用 Lucene 構建搜索應用程序的流程:
索引是現代搜索引擎的核心,建立索引的過程就是把源數據處理成非常方便查詢的索引文件的過程。為什么索引這么重要呢,試想你現在要在大量的文 檔中搜索含有某個關鍵詞的文檔,那么如果不建立索引的話你就需要把這些文檔順序的讀入內存,然后檢查這個文章中是不是含有要查找的關鍵詞,這樣的話就會耗 費非常多的時間,想想搜索引擎可是在毫秒級的時間內查找出要搜索的結果的。這就是由于建立了索引的原因,你可以把索引想象成這樣一種數據結構,他能夠使你 快速的隨機訪問存儲在索引中的關鍵詞,進而找到該關鍵詞所關聯的文檔。 Lucene 采用的是一種稱為反向索引( inverted index )的機制。反向索引就是說我們維護了一個詞 / 短語表,對于這個表中的每個詞 / 短語,都有一個鏈表描述了有哪些文檔包含了這個詞 / 短語。這樣在用戶輸入查詢條件的時候,就能非常快的得到搜索結果。我們將在本系列文章的第二部分詳細介紹 Lucene 的索引機制,由于 Lucene 提供了簡單易用的 API ,所以即使讀者剛開始對全文本進行索引的機制并不太了解,也可以非常容易的使用 Lucene 對你的文檔實現索引。
對文檔建立好索引后,就可以在這些索引上面進行搜索了。搜索引擎首先會對搜索的關鍵詞進行解析,然后再在建立好的索引上面進行查找,最終返回和用戶輸入的關鍵詞相關聯的文檔。
Lucene 軟件包分析
Lucene 軟件包的發布形式是一個 JAR 文件,下面我們分析一下這個 JAR 文件里面的主要的 JAVA 包,使讀者對之有個初步的了解。
Package: org.apache.lucene.document
這個包提供了一些為封裝要索引的文檔所需要的類,比如 Document, Field 。這樣,每一個文檔最終被封裝成了一個 Document 對象。
Package: org.apache.lucene.analysis
這個包主要功能是對文檔進行分詞,因為文檔在建立索引之前必須要進行分詞,所以這個包的作用可以看成是為建立索引做準備工作。
Package: org.apache.lucene.index
這個包提供了一些類來協助創建索引以及對創建好的索引進行更新。這里面有兩個基礎的類: IndexWriter 和 IndexReader ,其中 IndexWriter 是用來創建索引并添加文檔到索引中的, IndexReader 是用來刪除索引中的文檔的。
Package: org.apache.lucene.search
這個包提供了對在建立好的索引上進行搜索所需要的類。比如 IndexSearcher 和 Hits, IndexSearcher 定義了在指定的索引上進行搜索的方法, Hits 用來保存搜索得到的結果。
假設我們的電腦的目錄中含有很多文本文檔,我們需要查找哪些文檔含有某個關鍵詞。為了實現這種功能,我們首先利用 Lucene 對這個目錄中的文檔建立索引,然后在建立好的索引中搜索我們所要查找的文檔。通過這個例子讀者會對如何利用 Lucene 構建自己的搜索應用程序有個比較清楚的認識。
為了對文檔進行索引, Lucene 提供了五個基礎的類,他們分別是 Document, Field, IndexWriter, Analyzer, Directory 。下面我們分別介紹一下這五個類的用途:
Document
Document 是用來描述文檔的,這里的文檔可以指一個 HTML 頁面,一封電子郵件,或者是一個文本文件。一個 Document 對象由多個 Field 對象組成的。可以把一個 Document 對象想象成數據庫中的一個記錄,而每個 Field 對象就是記錄的一個字段。
Field
Field 對象是用來描述一個文檔的某個屬性的,比如一封電子郵件的標題和內容可以用兩個 Field 對象分別描述。
Analyzer
在一個文檔被索引之前,首先需要對文檔內容進行分詞處理,這部分工作就是由 Analyzer 來做的。 Analyzer 類是一個抽象類,它有多個實現。針對不同的語言和應用需要選擇適合的 Analyzer 。 Analyzer 把分詞后的內容交給 IndexWriter 來建立索引。
IndexWriter
IndexWriter 是 Lucene 用來創建索引的一個核心的類,他的作用是把一個個的 Document 對象加到索引中來。
Directory
這個類代表了 Lucene 的索引的存儲的位置,這是一個抽象類,它目前有兩個實現,第一個是 FSDirectory ,它表示一個存儲在文件系統中的索引的位置。第二個是 RAMDirectory ,它表示一個存儲在內存當中的索引的位置。
熟悉了建立索引所需要的這些類后,我們就開始對某個目錄下面的文本文件建立索引了,清單 1 給出了對某個目錄下的文本文件建立索引的源代碼。
package TestLucene; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.util.Date; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; /** * This class demonstrate the process of creating index with Lucene * for text files */ public class TxtFileIndexer { ???? public static void main(String[] args) throws Exception{ ???? //indexDir is the directory that hosts Lucene's index files ???? File ?? indexDir = new File("D:\\luceneIndex"); ???? //dataDir is the directory that hosts the text files that to be indexed ???? File ?? dataDir ? = new File("D:\\luceneData"); ???? Analyzer luceneAnalyzer = new StandardAnalyzer(); ???? File[] dataFiles ? = dataDir.listFiles(); ???? IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true); ???? long startTime = new Date().getTime(); ???? for(int i = 0; i < dataFiles.length; i++){ ????????? if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){ ?????????????? System.out.println("Indexing file " + dataFiles[i].getCanonicalPath()); ?????????????? Document document = new Document(); ?????????? ???? Reader txtReader = new FileReader(dataFiles[i]); ?????????????? document.add(Field.Text("path",dataFiles[i].getCanonicalPath())); ?????????????? document.add(Field.Text("contents",txtReader)); ?????????????? indexWriter.addDocument(document); ???? ????? } ???? } ???? indexWriter.optimize(); ???? indexWriter.close(); ???? long endTime = new Date().getTime(); ??????? ???? System.out.println("It takes " + (endTime - startTime) ???????? + " milliseconds to create index for the files in directory " ???????? + dataDir.getPath()); ??????? ???? } } |
?
在清單 1 中,我們注意到類 IndexWriter 的構造函數需要三個參數,第一個參數指定了所創建的索引要存放的位置,他可以是一個 File 對象,也可以是一個 FSDirectory 對象或者 RAMDirectory 對象。第二個參數指定了 Analyzer 類的一個實現,也就是指定這個索引是用哪個分詞器對文擋內容進行分詞。第三個參數是一個布爾型的變量,如果為 true 的話就代表創建一個新的索引,為 false 的話就代表在原來索引的基礎上進行操作。接著程序遍歷了目錄下面的所有文本文檔,并為每一個文本文檔創建了一個 Document 對象。然后把文本文檔的兩個屬性:路徑和內容加入到了兩個 Field 對象中,接著在把這兩個 Field 對象加入到 Document 對象中,最后把這個文檔用 IndexWriter 類的 add 方法加入到索引中去。這樣我們便完成了索引的創建。接下來我們進入在建立好的索引上進行搜索的部分。
利用 Lucene 進行搜索就像建立索引一樣也是非常方便的。在上面一部分中,我們已經為一個目錄下的文本文檔建立好了索引,現在我們就要在這個索引上進行搜索以找到包含某 個關鍵詞或短語的文檔。 Lucene 提供了幾個基礎的類來完成這個過程,它們分別是呢 IndexSearcher, Term, Query, TermQuery, Hits. 下面我們分別介紹這幾個類的功能。
Query
這是一個抽象類,他有多個實現,比如 TermQuery, BooleanQuery, PrefixQuery. 這個類的目的是把用戶輸入的查詢字符串封裝成 Lucene 能夠識別的 Query 。
Term
Term 是搜索的基本單位,一個 Term 對象有兩個 String 類型的域組成。生成一個 Term 對象可以有如下一條語句來完成: Term term = new Term(“fieldName”,”queryWord”); 其中第一個參數代表了要在文檔的哪一個 Field 上進行查找,第二個參數代表了要查詢的關鍵詞。
TermQuery
TermQuery 是抽象類 Query 的一個子類,它同時也是 Lucene 支持的最為基本的一個查詢類。生成一個 TermQuery 對象由如下語句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的構造函數只接受一個參數,那就是一個 Term 對象。
IndexSearcher
IndexSearcher 是用來在建立好的索引上進行搜索的。它只能以只讀的方式打開一個索引,所以可以有多個 IndexSearcher 的實例在一個索引上進行操作。
Hits
Hits 是用來保存搜索的結果的。
介紹完這些搜索所必須的類之后,我們就開始在之前所建立的索引上進行搜索了,清單 2 給出了完成搜索功能所需要的代碼。
? package TestLucene; ? import java.io.File; ? import org.apache.lucene.document.Document; ? import org.apache.lucene.index.Term; ? import org.apache.lucene.search.Hits; ? import org.apache.lucene.search.IndexSearcher; ? import org.apache.lucene.search.TermQuery; ? import org.apache.lucene.store.FSDirectory; ? /** ? * This class is used to demonstrate the ? * process of searching on an existing ? * Lucene index ? * ? */ ? public class TxtFileSearcher { ??????? ? public static void main(String[] args) throws Exception{ ??????? ??? String queryStr = "lucene"; ??????? ??? //This is the directory that hosts the Lucene index ??????? File indexDir = new File("D:\\luceneIndex"); ??????? FSDirectory directory = FSDirectory.getDirectory(indexDir,false); ??????? IndexSearcher searcher = new IndexSearcher(directory); ??????? if(!indexDir.exists()){ ??????? ?????? ? System.out.println("The Lucene index is not exist"); ??????? ?????? ? return; ??????? } ??????? Term term = new Term("contents",queryStr.toLowerCase()); ??????? TermQuery luceneQuery = new TermQuery(term); ??????? Hits hits = searcher.search(luceneQuery); ??????? for(int i = 0; i < hits.length(); i++){ ??????? ?????? ? Document document = hits.doc(i); ??????? ?????? ? System.out.println("File: " + document.get("path")); ? ?????? } ??????? ? } ? } |
?
在清單 2 中,類 IndexSearcher 的構造函數接受一個類型為 Directory 的對象, Directory 是一個抽象類,它目前有兩個子類: FSDirctory 和 RAMDirectory. 我們的程序中傳入了一個 FSDirctory 對象作為其參數,代表了一個存儲在磁盤上的索引的位置。構造函數執行完成后,代表了這個 IndexSearcher 以只讀的方式打開了一個索引。然后我們程序構造了一個 Term 對象,通過這個 Term 對象,我們指定了要在文檔的內容中搜索包含關鍵詞 ”lucene” 的文檔。接著利用這個 Term 對象構造出 TermQuery 對象并把這個 TermQuery 對象傳入到 IndexSearcher 的 search 方法中進行查詢,返回的結果保存在 Hits 對象中。最后我們用了一個循環語句把搜索到的文檔的路徑都打印了出來。 好了,我們的搜索應用程序已經開發完畢,怎么樣,利用 Lucene 開發搜索應用程序是不是很簡單。
本文首先介紹了 Lucene 的一些基本概念,然后開發了一個應用程序演示了利用 Lucene 建立索引并在該索引上進行搜索的過程。希望本文能夠為學習 Lucene 的讀者提供幫助。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
