1. 程式人生 > >【Lucene學習筆記】基本使用和認識

【Lucene學習筆記】基本使用和認識


【Lucene學習筆記】基本使用和認識

一、Lucene是什麼

這個在之前轉載的2篇相關博文中已經介紹的很清晰了,包括一些基本概念什麼的,在這篇筆記裡我就不多贅述了,簡單來說Lucene就是一個開放原始碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文字分析引擎。目前最新的是4.10.2版本,所以我使用的是這個版本來進行學習實驗的。

二、基本用法

1.     索引的建立

索引的建立一般為以下6個固定步驟:

①     建立Directory

②     建立IndexWriter

③     建立Document物件

④     為Document物件新增Field

⑤     通過IndexWriter新增文件到索引中

⑥     關閉IndexWriter和Directory

下面直接給出程式作為例子(此例使用文字檔案進行索引建立):

package test;

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.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter; 
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
/** 
* This class demonstrate the process of creating index with Lucene 
* for text files 
*/ 
public class TxtFileIndexer { 
     public static void main(String[] args) throws Exception{ 
     //1、建立Directory
     Directory directory = FSDirectory.open(new File("F:\\luceneIndex"));
     Analyzer luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_CURRENT); 
     //2、建立IndexWriter
     IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_CURRENT,luceneAnalyzer);
     IndexWriter indexWriter = new IndexWriter(directory, config);
     long startTime = new Date().getTime();
     //dataDir is the directory that hosts the text files that to be indexed 
     File   dataDir  = new File("F:\\luceneData");
     File[] dataFiles  = dataDir.listFiles();
     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());
               //3、建立Document物件
               Document document = new Document(); 
               Reader txtReader = new FileReader(dataFiles[i]);
               //4、為Document物件新增Field
               document.add(new Field("num",""+(i+1),StringField.TYPE_STORED));
               document.add(new Field("content",txtReader,TextField.TYPE_NOT_STORED));
               document.add(new Field("path",dataFiles[i].getCanonicalPath(),StringField.TYPE_STORED));
               String title = dataFiles[i].getName();
               document.add(new Field("title",title.substring(0, title.length()-4),TextField.TYPE_STORED));
               //5、通過IndexWriter新增文件到索引中
               indexWriter.addDocument(document); 
          }
     }
     //6、關閉IndexWriter和Directory
     indexWriter.close();
     directory.close();
     long endTime = new Date().getTime(); 
     System.out.println("It takes " + (endTime - startTime) 
         + " milliseconds to create index for the files in directory "
         + dataDir.getPath());        
     } 
}


2.     查詢的使用

查詢的使用一般為以下8個固定步驟:

①     建立Directory

②     根據Directory建立IndexReader

③     根據IndexReader建立IndexSearcher

④     建立QueryParser物件

⑤     建立搜尋的Query

⑥     根據Searcher搜尋返回TopDocs然後獲取ScoreDoc

⑦     根據ScoreDoc獲取具體Document物件並得到所需值

⑧     關閉IndexReader和Directory

下面給出程式作為例子(使用索引建立程式建立的索引進行):

 package test; 
 
import java.io.File; 
import java.util.Scanner;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document; 
import org.apache.lucene.index.DirectoryReader; 
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc; 
import org.apache.lucene.store.FSDirectory; 
import org.apache.lucene.util.Version;
 /** 
 * 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{ 
	    //1、建立Directory
        File indexDir = new File("F:\\luceneIndex"); 
        FSDirectory directory = FSDirectory.open(indexDir);
        //2、根據Directory建立DirectoryReader
        DirectoryReader reader = DirectoryReader.open(directory);
        //3、根據IndexReader建立IndexSearcher
        IndexSearcher searcher = new IndexSearcher(reader);
        //4、建立QueryParser物件
        Analyzer luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
        QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "content", luceneAnalyzer);
        if(!indexDir.exists()){ 
        	 System.out.println("The Lucene index is not exist");
        	 return;
        }
        int n = 10;
        while(n--!=0) {
        	Scanner readScanner = new Scanner(System.in);
        	String str = readScanner.nextLine();
        	//5、建立搜尋的Query
        	Query query = parser.parse(str);
        	//6、根據Searcher搜尋返回TopDocs然後獲取ScoreDoc
        	ScoreDoc[] sd = searcher.search(query,1000).scoreDocs;
        	System.out.println("查詢共有"+sd.length+"個結果");
        	//7、根據ScoreDoc獲取具體Document物件並得到所需值
        	for (int i = 0; i < sd.length; i++) {
        		Document doc = searcher.doc(sd[i].doc);
        		System.out.println(doc.get("title")+" : "+doc.get("path"));
        	}
        }
        //8、關閉IndexReader和Directory
        reader.close();
        directory.close();
	 }
 }


三、一些高階搜尋

Lucene支援多種形式的高階搜尋,並且有相關的API實現,不過目前的Lucene版本可以利用QueryParse就能轉換這些高階搜尋功能,這裡只使用上面的程式進行一個測試,至於具體的細節在以後仔細琢磨後再做總結吧。

luceneData檔案:

test01.txt:Tom lives in Guangzhou,I live in Guangzhou too.

test02.txt:He once lived in Shanghai. Now, he lives in Guangzhou.

1.    布林操作符

大多數的搜尋引擎都會提供布林操作符讓使用者可以組合查詢,典型的布林操作符有 AND, OR, NOT。Lucene 支援 5 種布林操作符,分別是 AND, OR, NOT, 加(+), 減(-)。

  • OR: 如果你要搜尋含有字元 A 或者 B 的文件,那麼就需要使用 OR 操作符。需要記住的是,如果你只是簡單的用空格將兩個關鍵詞分割開,其實在搜尋的時候搜尋引擎會自動在兩個關鍵詞之間加上 OR 操作符。例如,“Java OR Lucene” 和 “Java Lucene” 都是搜尋含有 Java 或者含有 Lucene 的文件。
  • AND: 如果你需要搜尋包含一個以上關鍵詞的文件,那麼就需要使用 AND 操作符。例如,“Java AND Lucene” 返回所有既包含 Java 又包含 Lucene 的文件。
  • NOT: Not 操作符使得包含緊跟在 NOT 後面的關鍵詞的文件不會被返回。例如,如果你想搜尋所有含有 Java 但不含有 Lucene 的文件,你可以使用查詢語句 “Java NOT Lucene”。但是你不能只對一個搜尋詞使用這個操作符,比如,查詢語句 “NOT Java” 不會返回任何結果。
  • 加號(+): 這個操作符的作用和 AND 差不多,但它只對緊跟著它的一個搜尋詞起作用。例如,如果你想搜尋一定包含 Java,但不一定包含 Lucene 的文件,就可以使用查詢語句“+Java Lucene”。
  • 減號(-): 這個操作符的功能和 NOT 一樣,查詢語句 “Java -Lucene” 返回所有包含 Java 但不包含 Lucene 的文件。

 

2.     域搜尋

Lucene 支援域搜尋,你可以指定一次查詢是在哪些域(Field)上進行。例如,如果索引的文件包含兩個域,Title 和Content,你就可以使用查詢 “Title: Lucene AND Content: Java” 來返回所有在 Title域上包含 Lucene 並且在 Content 域上包含 Java 的文件。


3.    萬用字元搜尋

Lucene 支援兩種萬用字元:問號(?)和星號(*)。你可以使用問號(?)來進行單字元的萬用字元查詢,或者利用星號(*)進行多字元的萬用字元查詢。例如,如果你想搜尋 tiny 或者 tony,你就可以使用查詢語句 “t?ny”;如果你想查詢 Teach, Teacher 和 Teaching,你就可以使用查詢語句 “Teach*”。

 

4.    模糊查詢

Lucene 提供的模糊查詢基於編輯距離演算法(Edit distance algorithm)。你可以在搜尋詞的尾部加上字元 ~ 來進行模糊查詢。例如,查詢語句 “think~” 返回所有包含和 think 類似的關鍵詞的文件。

 

5.    測試範圍搜尋

範圍搜尋匹配某個域上的值在一定範圍的文件。例如,查詢 “age:[18 TO 35]” 返回所有 age 域上的值在 18 到 35 之間的文件。

 

四、小結

這裡對很多的細節沒有做太多的說明,這裡只是對Lucene的一些基本功能簡單實現了一下,因為自己才剛剛開始學習Lucene,很多東西也還在學習的過程中,目前使用的版本是4.10.2,到網上找的很多資料版本都不太高,所以其中有很多變動的東西需要去看說明文件等。這裡值得一說的是索引建立的new Field()方法中FieldType這一項中的各個引數,是否索引和是否分詞都比較好理解,對於是否儲存這一項的意思其實是在索引中是否儲存這一個域中的資訊,如果儲存則可以在查詢是使用document物件直接用get方法得到對應的資訊,否則需要通過其他資訊找到原始檔後再進行提取,這個是否儲存就取決於此域中資訊的性質了,像content之類一般是不會進行儲存的。