lucene 3.6.0学习总结
目前,主流的全文索引工具有:Lucene , Sphinx , Solr , ElasticSearch。其中Solr和Elastic Search都是基于Lucene的。Sphinx不是 apache的项目,如果你想把Sphinx放到某个商业性的项目中,你就得买个商业许可证。(其实我只学习了lucence,solr 只是了解,这两天项目需要,研究学习了下.此文为个人学习备忘之用)
第一章 LUCENE基础
在全文索引工具中,都是由这样的三部分组成:索引部分、分词部分和搜索部分
IndexWriter:用来创建索引并添加文档到索引中。
Directory:这个类代表了索引的存储的位置,是一个抽象类。
Analyzer:对文档内容进行分词处理,把分词后的内容交给 IndexWriter来建立索引。
Document:由多个Field组成,相当于数据库中的一条记录。
Field:相当于数据库中的一条记录中的一个字段。
分词部分的核心类
Analyzer:简单分词器(SimpleAnalyzer)、停用词分词器(StopAnalyzer)、空格分词器(WhitespaceAnalyzer)、标准分词器(StandardAnalyzer)。
TokenStream:可以通过这个类有效的获取到分词单元信息。
Tokenizer:主要负责接收字符流Reader,将Reader进行分词操作。
TokenFilter:将分词的语汇单元,进行各种各样过滤。
搜索部分的核心类
IndexSearcher:用来在建立好的索引上进行搜索。
Term:是搜索的基本单位。
Query:把用户输入的查询字符串封装成Lucene能够识别的Query。
TermQuery:是抽象类Query的一个子类,它的构造函数只接受一个参数,那就是一个Term对象
TopDocs:保存返回的搜索结果。
SocreDoc:保存具体的Document对象。
第二章 索引建立
索引的建立是将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。如下图:
示例子:
package text; 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.NumericField; 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; public class TestFileIndexer { public static void main(String[] args) throws Exception { /* 指明要索引文件夹的位置,这里是C盘的source文件夹下 */ File fileDir = new File( "c:\\source " ); /* 这里放索引文件的位置 */ File indexDir = new File( "c:\\index" ); Directory dir=FSDirectory.open(indexDir);//将索引存放在磁盘上 Analyzer lucenAnalyzer=new StandardAnalyzer(Version.LUCENE_36);//分析器 IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_36,lucenAnalyzer); iwc.setOpenMode(OpenMode.CREATE);//创建新的索引文件create 表示创建或追加到已有索引库 IndexWriter indexWriter=new IndexWriter(dir,iwc);//把文档写入到索引库 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(":;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); 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,Field.TermVector.WITH_POSITIONS_OFFSETS); NumericField modifiField=new NumericField("modified");//所以key为modified modifiField.setLongValue(fileDir.lastModified()); document.add(FieldPath); document.add(FieldBody); document.add(modifiField); 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; }
Field.Store.YES:存储。该值可以被恢复(还原)。
NO:不存储。该值不可以被恢复,但可以被索引。
Field.Index.ANALYZED:分词。
NOT_ANALYZED:不分词。
NOT_ANALYZED_NO_NORMS:不分词也不加权(即不存储NORMS信息)。
查询索引的基本信息
package text; 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.document.Document; 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 TestQuery { public static void main(String[] args) throws ParseException, IOException { String index="c:\\index";//搜索的索引路径 IndexReader reader=IndexReader.open(FSDirectory.open(new File(index))); IndexSearcher searcher=new IndexSearcher(reader);//检索工具 ScoreDoc[] hits=null; String queryString="测试"; //搜索的索引名称 Query query=null; Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_36); try { QueryParser qp=new QueryParser(Version.LUCENE_36,"body",analyzer);//用于解析用户输入的工具 query=qp.parse(queryString); } catch (Exception e) { // TODO: handle exception } if (searcher!=null) { TopDocs results=searcher.search(query, 10);//只取排名前十的搜索结果 hits=results.scoreDocs; Document document=null; for (int i = 0; i < hits.length; i++) { document=searcher.doc(hits[i].doc); String body=document.get("body"); String path=document.get("path"); String modifiedtime=document.get("modifiField"); System.out.println(body+" "); System.out.println(path); } if (hits.length>0) { System.out.println("找到"+hits.length+"条结果"); } searcher.close(); reader.close(); } } }
索引文件作用
索引建立成功后,会自动在磁盘上生成一些不同后缀的文件(如下图),这些文件缺一不可,这里简单的介绍下不同后缀名的文件都有些什么作用:
.fdt : 保存域的值(即Store.YES属性的文件)。
.fdx : 与.fdt的作用相同。
.fnm :保存了此段包含了多少个域,每个域的名称及索引方式。
.frq : 保存倒排表。数据出现次数(哪篇文章哪个词出现了多少次)。
.nrm : 保存评分和排序信息。
.prx : 偏移量信息。倒排表中每个词在包含此词的文档中的位置。
.tii : 保存了词典(Term Dictionary)。也即此段包含的所有的词按字 典顺序的排序。
.tis : 同上。存储索引信息。
备注:
①如上图,具有相同前缀文件的属同一个段,图中共两个段 "_0"和 "_1"。
②一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。
③这些索引文件可以使用使用lukeall-3.5.0.jar打开,具体使用方法在后面的章节进行详述
Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_36);
QueryParser qp=new QueryParser(Version.LUCENE_36,"body",analyzer);//用于解析用户输入的工具
Query query=qp.parse(queryString);
根据Query获取TopDocs
TopDocs tds = searcher.search(query, 10); //返回10条数据
根据TopDocs获取ScoreDoc
ScoreDoc[] hits=null;
hits=results.scoreDocs;
Document document=null;
for (int i = 0; i < hits.length; i++) {
document=searcher.doc(hits[i].doc);
String body=document.get("body");
String path=document.get("path");
String modifiedtime=document.get("modifiField");
System.out.println(body+" ");
System.out.println(path);
}