现在的位置: 首页 > 综合 > 正文

Lucene 索引和搜索过程

2018年02月20日 ⁄ 综合 ⁄ 共 6818字 ⁄ 字号 评论关闭

1 What can Lucene do in Search Application?

下图是一个通用的信息检索应用系统架构图,包括数据获取(acquire content,信息可以来自网页抓取,数据库,或者本地目录文件等)、文档处理(build docemten)、文档分析(如用分析器analyzer将文档分为tokens词汇单元,即分词),建立索引(index), 搜索(search user interface)。其中图中阴影部分是Lucene能完成的功能,因此Lucene是一个信息检索库,不是一个搜索引擎(Lucene
is a search library, not a full application.)

Lucene是否是否你的应用系统,《Lucene in Action》有这么一段讲的很好:

2 Lucene Arichtectrure

下图是Lucene的架构图,从图中看书Lucene主要包括:索引建立和搜索。



3 Index Process

 Lucene索引过程主要包括如下图的3个过程:从文档中抽取纯文本(plain text),分词,建立索引


为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。

(1) Document

Document 是用来描述文档的,这里的文档可以指一个HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。

(2)Field

Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。

(3)Analyzer

在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给 IndexWriter 来建立索引。

(4)IndexWriter

IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。

(5)Directory

这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。

Code Demo:

<span style="font-family:SimSun;font-size:14px;">package zyang.luceneTest;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

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.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

/**
 * @author 
 * @version 1.0
 * @date 2012-11-18 下午2:42:57
 * @fuction 建立索引
 */

public class Indextor {

	private IndexWriter indexWrite = null;

	/**
	 * 对给定文件目录中的文件建立索引
	 * @param dataDir
	 *            要建索引的文件所在目录
	 * @param indexDir
	 *            索引文件存放目录
	 * @return 建立索引的文件数目
	 * @throws Exception
	 */
	public int index(String dataDir, String indexDir) throws Exception {	
		//1 要建索引的文件所在目录
		File fIndexDir=new File(indexDir);
		Directory dir = FSDirectory.open(fIndexDir);
		
		//2 分词
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
		// 设置索引配置IndexWriterConfig
		IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_40,
				analyzer);
		// Add new documents to an existing index:		
		iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
		
		//3 建立索引
		indexWrite = new IndexWriter(dir, iwc);
        //只对txt文件建立索引
	    File fDataDir=new File(dataDir);
		File[] dataFiles = fDataDir.listFiles();
		for (File f : dataFiles) {
			if (!f.isDirectory() && !f.isHidden() && f.exists() && f.canRead()
					&& f.getName().toLowerCase().endsWith(".txt")) {
				System.out.println("Indexing " + f.getCanonicalPath());
				Document doc = new Document();
				doc.add(new StringField("fullpath", f.getPath(), Field.Store.YES));
				doc.add(new TextField("contents", new BufferedReader(new FileReader(f))));

				if (indexWrite.getConfig().getOpenMode() == OpenMode.CREATE) {
					// New index, so we just add the document (no old document can be there):
					System.out.println("adding " + f);
					indexWrite.addDocument(doc);
				} else {
					// Existing index (an old copy of this document may have been indexed) so
					// we use updateDocument instead to replace the old one
					// matching the exact path, if present:
					System.out.println("updating " + f);
					indexWrite.updateDocument(new Term("fullpath", f.getPath()), doc);
				}		
			}// end if
		}// end for
		
		int numIndexed = indexWrite.numDocs();
		return numIndexed;
	}

	//close indexWriter
	public void close() throws IOException {
		indexWrite.close();
	}

	public static void main(String[] args) throws Exception {
		// 要建索引的文件所在目录
		String dataDir = "D:/java/luceneTest/data";
		// 索引文件存放目录
		String indexDir = "D:/java/luceneTest/index";

		long start = System.currentTimeMillis();
		Indextor indexer = new Indextor();
		int numIndexed;
		try {
			numIndexed = indexer.index(dataDir, indexDir);
			
		} finally {
			indexer.close();	
		}
		long end = System.currentTimeMillis();

		System.out.println("Indexing " + numIndexed + " files took "
				+ (end - start) + " milliseconds");
	}
}</span>

4 Search Process

Lucene搜索过程包括查询语句、词法分析和语言处理,搜索索引,相关性排序,查询结果。Lucene搜索的核心类包括IndexReader,IndexSearcher,Term,Query,Term,TopDocs。

(1)IndexReader

将磁盘上的索引信息读到内存。IndexReader封装了底层的API操作,其open操作非常耗费资源,因此IndexReader应该重用。但是IndexReader打开后便不能获悉之后更新的Index,因此可reopen,reopen将尝试尽量重用
,如果无法重用将创建新的IndexReader,因此需要判断。

<span style="font-family:SimSun;font-size:14px;">IndexReader newReader =reader.reopen();
if (reader != newReader) {
reader.close();
reader = newReader;
searcher = new IndexSearcher(reader);
}</span>

(2)IndexSearcher

IndexSearcher 是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个 IndexSearcher 的实例在一个索引上进行操作。

(3)Query

用于查询的查询条件对象。这是一个抽象类,它有多个实现,比如TermQuery, BooleanQuery, PrefixQuery. 这个类的目的是把用户输入的查询字符串封装成Query类型Lucene有多种搜索方式,可以根据需要选择不同的方式。

(4)QueryParser

对查询语句进行语法分析,将查询字符串转为查询条件对象Query.

(5)Term

Term 是搜索的基本单位,一个 Term 对象有两个 String 类型的域组成。生成一个 Term 对象可以有如下一条语句来完成:Term term = new Term(“fieldName”,”queryWord”); 其中第一个参数代表了要在文档的哪一个 Field 上进行查找,第二个参数代表了要查询的关键词。

(6)TopDocs

获取结果集的最靠前的若干项。

(7)ScoreDoc

获取结果集中的结果。

Code Demo:

<span style="font-family:SimSun;font-size:14px;">package zyang.luceneTest;

import java.io.File;
import java.io.IOException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.util.Version;

/**
 * @author
 * @version 1.0
 * @date 2012-11-19 下午8:21:55
 * @fuction lucene搜索类使用
 */

public class Searcher {

	/**
	 * @param args
	 * @throws IOException
	 * @throws ParseException
	 */
	public static void main(String[] args) throws IOException, ParseException {

		// 1 查询语句
		String field = "contents"; // 要查询的字段
		String strQuery = "TechWeb"; // 要查询字段值的关键词

		// 2 搜索索引
		File indexDir = new File("D:/java/luceneTest/index"); // //索引文件目录
		if (!indexDir.exists() && !indexDir.canRead()
				&& !indexDir.isDirectory()) {
			System.out.println("索引文件目录不存在");
			return;
		}

		Directory dir = FSDirectory.open(indexDir);

		IndexReader reader = DirectoryReader.open(dir);

		try {
			IndexSearcher searcher = new IndexSearcher(reader);
			Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
			QueryParser parser = new QueryParser(Version.LUCENE_40, field,
					analyzer);
			Query query = parser.parse(strQuery);

			// 3 查询结果
			TopDocs results = searcher.search(query, null, 100);
			if (results == null || results.totalHits == 0) {
				System.out.println("没有查询到数据");
			} else {
				ScoreDoc[] hits = results.scoreDocs;
				for (ScoreDoc hit : hits) {
					Document document = searcher.doc(hit.doc);
					System.out.println("File: " + document.get("fullpath"));
				} // end for
			} // end else
		} finally {
			reader.close();
		}

	}

}
</span>


抱歉!评论已关闭.