Lucene入门实例

本文介绍了Lucene作为全文检索引擎的基本概念和流程,包括建立索引和搜索服务的细节,如Analyzer、Document、Field、Segment等核心概念,并通过一个简单的环境搭建和测试案例展示其工作原理。
摘要由CSDN通过智能技术生成

一、简介   

 Lucene是什么:Lucene是apache软件基金会jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。    Lucene是一个基于Java的全文搜索,不是一个完整的搜索应用,而是一个代码库和API,可以方便地为应用提供搜索功能。 实际上Lucene的功能就是将开发人员提供的若干个字符串建立索引,然后提供一个全文搜索服务,用户将搜索的关键词提供给搜索服务,搜索服务告诉用户关键词出现的各字符串。

二、 基本流程

lucene包含两部分:建立索引和搜索服务。建立索引是将源(本质是字符串)写入索引或者将源从索引中删除;进行搜索是向用户提供全文搜索服务,用户可以通过关键词定位源。

1. 建立索引的流程

  • 使用analyzer处理源字符串,包括:分词,即分成一个个单词;去除stopword(可选)。
  • 将源中的有效信息以不同Field的形式加入Document中,并把Document加入索引,从而在索引中记录有效的Field。
  • 将索引写入存储器(内存或磁盘)。
  • 用户提供搜索关键词,经过analyzer处理。
  • 对处理后的关键词搜索索引找出对应的Document。
  • 用户根据需要从找到的Document中提取需要的Field。 

2. 检索的流程

  • 用户提供搜索关键词,经过analyzer处理。
  • 对处理后的关键词搜索索引找出对应的Document。
  • 用户根据需要从找到的Document中提取需要的Field。
三、 基本概念

1. Analyzer

Analyzer的作用是分词,并去除字符串中的无效词语。

分词的目的是把字符串按某种语义规则划分为若干个词。英文中比较容易实现分词,因为英文本身就是以单词为单位,已经用空格分开;而中文则必须以某种方法将连成一片的句子划分成一个个词。 无效词语,如英文中的“of”、“the”和中文中的“的”、“地”等,这些词语在文章中大量出现。但是本身不包含关键信息,去掉后有利于缩小索引文件、提高命中率和执行效率。

2. Document

用户提供的源可以是文本文件、字符串或者数据库表中的一条记录等。一个源字符串经过索引之后,以一个Document的形式存储在索引文件中。搜索服务的结果也是以Document列表的形式返回。

3. Field

一个Document可以包含多个信息域,如一篇文章可以包含“标题”、“正文”、“最后修改时间”等信息域,这些信息域以Field的形式保存在Document中。

Field有两个属性:存储和索引。存储属性可以控制是否对这个Field进行存储;索引属性可以控制是否对该Field进行索引。这似乎多此一举,但事实上对这两个属性的正确组合很重要。 

下面举例说明:一篇文章需要对标题和正文进行全文搜索,所以把这两个Field的索引属性设置为真;同时希望能直接从搜索结果中提取文章标题,所以把标题Field的存储属性设置为真。但是正文Field太大了,为了缩小索引文件,将正文Field的存储属性设置为假,需要访问时再直接读取文件正文;希望能从搜索结果中提取最后修改时间;但是不需要对它进行搜索,所以把最后修改时间Field的存储属性设置为真,索引属性设置为假。

Field的两个属性禁止全为假的情况因为这对建立索引没有意义。

4. segment

建立索引时,并不是每个document都马上添加到同一个索引文件,它们首先被写入到不同的小文件,然后再合并成一个大索引文件,每个小文件都是一个segment。

5. term

term表示文档的一个词,是搜索的最小单位。term由两部分组成:所表示的词语和这个词语所出现的field。

6. token

token是term的一次出现,它包含trem文本和相应的起止偏移,以及一个类型字符串。一句话中可以出现多次相同的词语,它们都用同一个term表示,但是用不同的token,每个token标记该词语出现的位置。

四、 Lucene的组成结构

 Lucene core包含8个包:analysis、collation、document、index、queryParser、search、store、util。

1. analysis包(文本解析库

 这个库是对文本进行解析,得到相应的Term对象,在Index与Searcher的时候都会用到,在索引过程中,它会对被索引的文件进行解析,而在搜索过程中,它会对查询条件进行解析。

Analysis提供自带的各种Analyzer,如按空白字符分词的WhitespaceAnalyzer,添加了stopword过滤的StopAnalyzer,支持中文分词的SmartChineseAnalyzer,以及最常用的StandardAnalyzer。

2. collation包

包含collationKeyFilter和collationKeyAnalyzer两个相同功能的类,将所有token转换为CollationKey,并将CollationKey与IndexableBinaryStringTools一起编码存储为一个term。

3. document包(文档的一个对象模型抽象库

document包中是Document相关的各种数据结构,如Document类、Field类等。

4. index包(索引库)

index包中是索引的读写操作类,常用的是对索引文件的segment进行写、合并和优化的IndexWriter类和对索引进行读取和删除操作的IndexReader类。IndexWriter只关心如何将索引写入一个个segment并将它们合并优化;IndexReader关注索引文件中各个文档的组织形式。它主要是用来对文档建立索引,对索引进行维护(更新索引,优化索引等),为查询提供访问接口。

5. queryParser包

queryParser包中是解析查询语句相关的类(常用的是QueryParser类)以及Token类。

6. search包(查询库)

search包中是从索引中进行搜索的各种不同的Query类(如TermQuery、BooleanQuery等)和搜索结果集Hits类。  它主要是为用户提供查询接口的,对用户的查询结果进行评估,并返回与用户最相关的查询信息。

7. store包(存储库

 store包中是索引的存储相关类,如Directory类定义了索引文件的存储结构,FSDirectory是存储在文件系统(即磁盘)中的索引存储类,RAMDirectory为存储在内存中的索引存储类,MmapDirectory为使用内存映射的索引存储类。它主要是用来存储索引内容的,这里提供了两种存储方式,一种是磁盘存储,另一个是内存存储。

8. util包(工具库)

 util包中是公共工具类,例如时间和字符串之间的转换工具。

五、 环境搭建及测试

在项目中引入lucene的两个核心包:lucene-core-3.6.2.jar和lucene-core-3.6.2-javadoc.jar;

在windows系统下的的D盘,建一个名叫lucene的文件夹,lucene文件夹下建个temp文件夹,temp下建两个文件夹,名字分别为docs(存放文本文件)和index(存放索引文件)。在docs文件夹里创建三个txt文件,如:1.txt,2.txt,3.txt;3个txt文件里随便写些内容。

1.建立索引:

package com.javaeye.lucene;

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.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

@SuppressWarnings("deprecation")
public class LuceneTest {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		 /** 指明要索引文件夹的位置,这里是D:\lucene\temp\docs文件夹下  */   
             File fileDir=new File("D:"+File.separator+"lucene"+File.separator+"temp"+File.separator+"docs");
             /**放索引文件的位置*/
             File indexDir=new File("D:"+File.separator+"lucene"+File.separator+"temp"+File.separator+"index");
             Analyzer luceneAnalyer=new StandardAnalyzer(Version.LUCENE_36);
             Directory  dir=FSDirectory.open(indexDir);
             IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_36,luceneAnalyer);
             iwc.setOpenMode(OpenMode.CREATE);
             IndexWriter indexWriter=new IndexWriter(dir,iwc);
             // 在索引库没有建立并且没有索引文件的时候首先要commit一下让他建立一个,索引库的版本信息 ,如果第一次没有commit就打开一个索引读取器的话就会报异常 
             //indexWriter.commit();
             File[] textFiles=fileDir.listFiles();
             long startTime=new Date().getTime();
             	//增加document到索引去
             for(int i=0;i<textFiles.length;i++){
            	 if(textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")){
            		 System.out.println("File"+textFiles[i].getCanonicalPath()+"正在被索引。");
            		 String temp=FileReaderAll(textFiles[i].getCanonicalPath(),"GBK");
            		 System.out.println(temp);
            		 Document document=new Document();
            		 //创建一个字段
            		 Field fieldPath=new Field("path",textFiles[i].getPath(),Field.Store.YES,Field.Index.NO);
            		 Field fieldBody=new Field("body",temp,Field.Store.YES,Field.Index.ANALYZED,TermVector.WITH_POSITIONS_OFFSETS);
            		 document.add(fieldPath);
            		 document.add(fieldBody);
            		 indexWriter.addDocument(document);
            		 
            	 }
             }
             indexWriter.close();
             long endTime=new Date().getTime();
             System.out.println("这花费了"+(endTime-startTime)+"毫秒来把文档增加到索引里面去!"+fileDir.getPath());
             
	}
	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;
	}

}

运行结果:

FileD:\lucene\temp\docs\1.txt正在被索引。
给我你的爱
FileD:\lucene\temp\docs\2.txt正在被索引。
爱情来得如此微妙-Yeah!
FileD:\lucene\temp\docs\3.txt正在被索引。
Because守护我们的爱情...
这花费了419毫秒来把文档增加到索引里面去!D:\lucene\temp\docs

2.建立了索引之后,查询

package com.javaeye.lucene;

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

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class QueryTest {

	/**
	 * @param args
	 */
	@SuppressWarnings("deprecation")
	public static void main(String[] args) throws IOException,ParseException {
         String index="D:\\lucene\\temp\\index";
         File file=new File(index);
         if(file!=null){
        	 System.out.println(file.getCanonicalPath());
         }
         IndexReader  reader=IndexReader.open(FSDirectory.open(file));
         IndexSearcher searcher=new IndexSearcher(reader);
         ScoreDoc[] hits=null;
         String queryStr="爱情";//搜索的关键词
         Query query=null;
         Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_36);;
        try{
        	 QueryParser qp=new QueryParser(Version.LUCENE_36,"body",analyzer);
        	  query=qp.parse(queryStr);
         }catch(ParseException e){
        	 e.printStackTrace();
         }
         if(searcher!=null){
        	 TopDocs results=searcher.search(query,10);
        	 hits=results.scoreDocs;
        	 if(hits.length>0){
            	 System.out.println("找到"+hits.length+"结果");
            	 for(int i=0;i<hits.length;i++){
            	 System.out.println(searcher.doc(hits[i].doc).get("body"));
            	 }
             }
        	 searcher.close();
         }
	}
}
运行结果:

D:\lucene\temp\index
找到3结果
爱情来得如此微妙-Yeah!
Because守护我们的爱情...
给我你的爱

到此测试成功完毕!!!!!

PS:

Lucene其实很简单的,它最主要就是做两件事:建立索引和进行搜索 
来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已----因为这一个世界有一种好东西,叫搜索。

IndexWriter:lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。

Analyzer:分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。

Directory:索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。

Document:文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。

Field:字段。

IndexSearcher:是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;

Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。

QueryParser:是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。

Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来表示的。





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值