• 1. lucene 简介lucene 是什么? lucene是一套Java API,它不是一个独立的搜索引擎系统,但是你可以使用lucene开发搜索引擎系统。现在我们学习lucene主要是学习如何使用别人开源的东西,来组建自己想要的搜索引擎系统。 在这里我是和大家共同讨论学习lucene,前些日子我先简单的学习了一下,下面给大家具体的介绍一下lucene. lucene 有什么(也就是lucene的组成) (1)indexer (2)searcher 一个完整的搜索引擎有四部分组成,lucene可以完成两部分。 1.2.1 大家先来看一个小例子。
  • 2. 首先来回顾一下IO读取文件,lucene的功能主要是全文搜索,所以对文件的读写时相当多的。 package luceneindexer; import java.io.*; public class FileText{ //读取一个文件的所有内容 public static String getText(File f){ StringBuffer sb=new StringBuffer(""); try{ FileReader fr=new FileReader(f); BufferedReader br=new BufferedReader(fr); String s=br.readLine(); while(s!=null){ sb.append(s); s=br.readLine();} br.close(); } catch(Exception e){ sb.append(""); } return sb.toString(); }
  • 3. //读取一个文件的所有内容--重载 public static String getText(String s){ String t = ""; try{ File f = new File(s); t = getText(f); }catch(Exception e){ t = ""; } return t; } //主函数,测试 public static void main(String[] args) throws IOException{ String s = FileText.getText("doc/test.htm"); System.out.println(s); } }
  • 4. 上面的例子是对文件的读取,对于luncen读取文件是为创建索引获取内容的,有了内容才可以创建索引,下面再看个例子就是创建索引。 package luceneindexer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.analysis.standard.StandardAnalyzer; import java.io.*; import jeasy.analysis.MMAnalyzer; public class FileIndexer{ public static void main(String[] args) throws java.io.IOException{ String indexPath = "file"; IndexWriter writer = new IndexWriter(indexPath,new StandardAnalyzer(),true);//创建索引的构造方法 Document doc = new Document();//创建索引的主要元素
  • 5. File f = new File("doc/黑帝.htm"); String name = f.getName(); Field field = new Field("name",name ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); String content = FileText.getText(f); field = new Field("content", content ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); String path = f.getPath(); field = new Field("path", path ,Field.Store.YES, Field.Index.NO); doc.add(field); writer.addDocument(doc,new MMAnalyzer());
  • 6. doc = new Document(); f = new File("doc/China.htm"); name = f.getName(); field = new Field("name",name ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); content = FileText.getText(f); field = new Field("content", content ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); path = f.getPath(); field = new Field("path", path ,Field.Store.YES, Field.Index.NO); doc.add(field); writer.addDocument(doc,new StandardAnalyzer()); writer.close(); System.out.println("File Index Created!"); } }
  • 7. 好,现在已经把索引建成。通过这两个小例子让大家对lucene的第一部分有个感性的认识。 Lucene做搜索引擎不可能是从一个文件里来搜索,应该是从多个文件来查找想要的东西,现在就对上面的代码做一个修改,在创建索引时从一个文件夹里读取多个文件,然后创建索引。 package indexfiles; import java.io.*; public class FileList{ private static final String SEP = "/"; private static StringBuffer sb = new StringBuffer(""); //返回数组,把一个文件夹下面的所有文件都读取进来 public static String[] getFiles(File f) throws IOException{ if(f.isDirectory()){ File[] fs=f.listFiles(); for(int i=0;i
  • 8. public static String[] getFiles(String t) throws IOException File f = new File(t); if(f.isDirectory()){ File[] fs=f.listFiles(); for(int i=0;i
  • 9. 好,上面是读取一个文件夹下面的所有文件,有了这些内容我们就可以建立索引。下面就是建立索引的代码: package indexfiles; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import luceneindexer.FileText; 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; public class LuceneIndexerTool { //创建索引 -- 被索引的文件的路径,索引的路径 public static void index(String filesPath,String indexPath) throws IOException{ //建立索引器 IndexWriter writer = new IndexWriter(indexPath,new StandardAnalyzer(),true); //递归遍历文件目录来建立索引 String s[] = FileList.getFiles(filesPath); for(int i=0;i
  • 10. for(int i=0;i1024){ size = String.valueOf(Math.floor(si/1024)) + " K"; }else{ size = String.valueOf(si) + " Bytes"; } field = new Field("size", size ,Field.Store.YES, Field.Index.NO); doc.add(field); String text = FileText.getText(f); field = new Field("text", text ,Field.Store.COMPRESS, Field.Index.TOKENIZED); doc.add(field);
  • 11. String digest = ""; if(text.length()>200){ digest=text.substring(0,200); }else{ digest=text; } field = new Field("digest", digest ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); writer.addDocument(doc); } } // 关闭索引器 writer.close(); } public static String getExt(File f) { String s = f.getName(); try{ s = s.substring(s.lastIndexOf(".")+1); }catch(Exception e){ s= ""; } return s; } public static void main(String[] args) throws IOException { index("E:/text", "E:/indexer"); System.out.println("Indexer success!!"); } }
  • 12. 好,现在索引已经建好了,大家可以看到建立索引的原文件,可以大体上了解一下文件的内容。下面就进入lucece第二部分搜索器的创建: package luceneSearcher; import java.io.IOException; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.Term; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery;
  • 13. public class Searcher { //执行搜索 -- 搜索的关键词,索引的路径 public static void search(String phrase,String indexPath) throws IOException{ IndexSearcher searcher = new IndexSearcher(indexPath);//建立搜索器 Term t = new Term("text", phrase);//搜索 text 字段 Query q = new TermQuery(t);//生成Query对象 Hits hs = searcher.search(q);//获得搜索结果对象 int num = hs.length();//搜索到的结果数量 //输出搜索结果 for(int i = 0; i < num; i++){ Document doc = hs.doc(i);//获得Document if(doc == null){ continue; }
  • 14. Field field = doc.getField("filename");//获得filename String filename = field.stringValue(); field = doc.getField("uri");//uri String uri = field.stringValue(); field = doc.getField("cdate"); String cdate = field.stringValue(); field = doc.getField("digest"); String digest = field.stringValue(); StringBuffer sb=new StringBuffer(); sb.append("URI:" + uri +"\n"); sb.append("filename:" + filename + "\n"); sb.append("cdate:" + cdate +"\n"); sb.append("digest:" + digest + "\n"); sb.append("------------------------------------" + "\n"); System.out.println(sb); } //关闭搜索器 searcher.close(); } public static void main(String[] args) throws IOException { search("史记中称伏牺","E:/indexer"); } }
  • 15. 好,现在就可以看到整个lucene的运行流程。但是lucene的东西是很多的还有排序,高亮,这些在后面的学习中给大家做介绍。现在我想先带大家入门,先做简单的,大家在用的时候可以深入的学习。现在我把主要的知识点讲解一下,lucene提供了五个基础类,Document 、Field、indexWriter、Analyzer、Directory。 Document 就相当一张表里的一条记录。 Field 就相当一这个表中的字段。 Analyzer 就是在文档被索引之前首先要对文档的内容进行分词处理。Analyzer是一个抽象类,它有多个实现方法,针对不同的语言和应用需要选择适当的Analyzer.具体的实现大家要参阅API。 IndexWriter是lucene创建索引的核心类,它的作用是封装Document对象。如果把Document理解成一条记录的话IndexWriter就相当于存储这条记录的表。 IndexWriter 的构造函数有六个。分别是: IndexWriter(Directory d, Analyzer a) IndexWriter(Directory d, Analyzer a, boolean create) IndexWriter(File path, Analyzer a) IndexWriter(File path, Analyzer a, boolean create) IndexWriter(String path, Analyzer a) IndexWriter(String path, Analyzer a, boolean create)
  • 16. IndexWriter writer = new IndexWriter(indexPath,new StandardAnalyzer(),true);//常用的构造方法 writer.setUseCompoundFile(false); //非复合式索引模式 Lucene的索引形式有2种:“复合式索引”和“非复合式索引”。通过IndexWriter类的setUseCompoundFile(boolean)方法进行设置。设为“False”表示不使用复合索引(为了展示Lucene具体的索引文件)。建立后的索引文件比较多;若设为“True”或者默认情况下(不调用该方法),表示开启复合索引功能,建立后的索引文件只有三个。使用复合式索引的优点是可以减少文件的IO操作 。 这行代码是设置索引器的参数,他们的作用可以在代码演示时体现。 在实际中经常会用到的是第一个和第二个,第二个比第一个多一个boolean参数,我给大家介绍一下这几个参数。Directory是建立索引要存放的位置。Analyzer是使用什么分析器。(在这里大家要注意分析器和解析器的区别,解析器是对文件进行解析,比如html文件,用html解析器就能提取出文件里的内容,就把文件里html标签都去掉,分析器是对文件内容跟分析器的算法进行拆分);create 是建立索引是全新的还说累加的,如果create为true是要建立全新的索引,要为false就是要在原有的索引上加新的索引。 其他构建索引器的构造函数是使用方法,大家可以查阅API。 最后一个基础类就是Directory,这个类代表了Lucene的索引的存储位置,这也是一个抽象类,它目前有两个实现方法,第一个是FSDirectory它表示一个存储在文件系统中的索引的位置。第二个是RAMDirectory 它表示要存储在内存当中的索引位置,这个大家可以根据实际来选择。      
  • 17. 现在大家多Lucene创建索引器的各个元素都了解了,现在我和大家共同理一下创建索引的思路。创建索引首先的有内容,先到获取内容,如果是文件就要读取文件,如果是数据库里的内容就要查找要建立索引的内容。有了内容就要对内容进行包装,刚说了Filed就相当一字段,咱们对表的操作也是把内容放到对应的字段里,Lucene也一样把要建立索引的内容要放到Field里。在上面代码里大家看到了这样的代码 Field field = new Field("filename", filename ,Field.Store.YES, Field.Index.TOKENIZED); //创建索引的内容都必须以字符形式来存储的,大家看Field的构造方法就知道 Filename 就相当于在表里是字段名,这个大家可以随便起名, filename就是要建立索引的内容, Field.Store.YES这个位置 大家可以选择有Field.Store.YES、 Field.Store.NO、Field.Store.COMPRESS。分别代表的意思是建立索引并存储、建立索引不存储、建立索引压缩存储。 Field.Index.TOKENIZED这个位置也是有多个选择的,有Field.Index.NO、 Field.Index.TOKENIZED……;这个大家在开发是都有提示,我在这稍做解释,这些都是索引方式,就是索引和分词的组合。 Field.Index.NO是不索引, Field.Index.TOKENIZED是分词并建立索引。其他的大家参阅API。
  • 18. 索引内容封装好了就要创建Document来存储Field。Document只有一个无参构造方法,Document document = new Document();这样就可以了。 Document的方法很多,大家可以看文档,在常见索引的时候会用到add()方法,一个Document 可以封装多个Field。准备工作都做好了,最后用IndexWriter把他存储到指定的地方 执行语句writer.addDocument(doc);最后别忘了写writer.close(); 创建索引就说这么多了,以大家聪明的脑袋我估计能写出了更好的程序了!呵呵
  • 19. 现在讲Searcher,Searcher的作用是从索引文件里根据我们的条件来查找。使用Lucene进行搜索,首先要创建IndexSearcher对象,然后要通过Term和Query对象来封装用户输入的搜索条件,最后,将结果封装在Hits对象中,返回给用户。下面对Searcher的这个类的用法进行讲解: 创建IndexSearcher对象的方法 IndexSearcher is = new IndexSearcher(“索引存放的路径”);这句代码的意思就是读取索引文件,IndexSearcher有好多方法,我们常用的方法是searcher 它的构造函数也很多,大家可以查看API。 使用IndexSearcher搜索需要对查询条件进行封装,Lucene提供了Term和Query对象来封装搜索条件。首先是Term对搜索条件封装成Term对象,然后再封装成Query对象,
  • 20. Term t = new Term(“索引里的Field名字”,”搜索的关键字”); Query q = new Query(t); Searcher的搜索条件必须的封装成Query;现在可以执行搜索了, is.search(q);执行这条代码来查询最后它要返回一个结果集,这个结果集Lucene也进行 了封装,封装成Hits。Hits和List差不多,也有自己的索引,Hits里存放的是Document,取出Doc然后再取出Field,最后把Field转化成String就OK了。 Field field = doc.getField(“filename”);//获得 filenameString filename = field.stringValue();
  • 21. 好,到现在就可以做简单的搜索引擎了,下面我再讲解一下中文分词,现在主流的中文分词器有单字切分、二分法、词典法、语义法。 Lucene默认分词很多,支持中文的只有StandardAnalyzer,大家可以先看一下Lucene自带的分词器的效果。现在流行的分词方法都有开源的大家可以直接用。如果这还不满足的话,咱们自己可以写分析器。只需要继承Analyzer就可以了,具体代码在项目中可以看到。 在开发中大家用Lucene默认的分词法就够用了,下面我给大家介绍几个网上传说好的中文分词器。Lucene的各种包都分着的,大家用什么分词法就的下载相应的jar包。 二分法:CJKAnalyzer public class UseCJK{ public static void main(String[] args) throws java.io.IOException{ String s = "我爱我伟大的老爸老妈,我爱我壮丽的中华!"; StringReader sr = new StringReader(s); CJKTokenizer cjk = new CJKTokenizer(sr); org.apache.lucene.analysis.Token t = null; while( (t=cjk.next()) !=null ){ System.out.print(t.termText()+"|"); } } } 运行结果:我爱|爱我|我伟|伟大|大的|的老|老爸|爸老|老妈|我爱|爱我|我壮|壮丽|丽的|的中|中华|
  • 22. Lucene自带的中文分词法ChineseAnalyzer,他是单字分词,大家可以看一下分词效果。 public class UseCN{ public static void main(String[] args) throws java.io.IOException{ String s = "我爱我伟大的老爸老妈,我爱我壮丽的中华!"; StringReader sr = new StringReader(s); ChineseTokenizer cn = new ChineseTokenizer(sr); org.apache.lucene.analysis.Token t = null; while( (t=cn.next()) !=null ){ System.out.print(t.termText()+"|"); } } } 运行结果:我|爱|我|伟|大|的|老|爸|老|妈|我|爱|我|壮|丽|的|中|华| JE分词器: 是基于词库,可以向词库中增加新词,现在用的挺多的。 看一下分词效果: public class UseJE{ public static void main(String[] args) throws java.io.IOException{ String s = "我爱我伟大的老爸老妈,我爱我壮丽的中华!"; MMAnalyzer mm = new MMAnalyzer(); System.out.print(mm.segment(s,"|")); } } 运行结果:我|爱我|伟大|老爸|老妈|我|爱我|壮丽|中华| JE分词是单双结合的,有点按语义分词的效果。 在网上还有好多免费的分词方法,大家可以试着使用一下,看看效果怎么。
  • 23. 现在说一下搜索器中的TermQuery和QueryParser,TermQuery用于单个的字查询, QueryParser可以解析多个关键字,在我们开发中肯定是单字加短语,这样我们就使用QueryParser parser = new QueryParser("surname", new StandardAnalyzer()); Query q = parser.parse("(surname:'"+content+"' description:'"+content+"')"); 这样就解决了, StandardAnalyzer实现了分词,这样就可以把一个字也作为短语来查询。 QueryParser 把解析出来的关键字封装成一个Query对象,searcher就可以实现多个关键字的查询了。我们在开发中根据实际情况来选择分词器。 现在再说一下搜索结果的处理,在百度或其他网站上大家搜索后,关键字有所变化,有的变色,有的加粗等,用Lucene也能做到,这就是用高亮。还有对搜索结果的筛选,排序等操作。这这里我只做简单的介绍,大家可以看我写的列子,对照列子我给大家做一下讲解。 先说排序吧,排序是Lucene自带的,所以只要引入core包就不用再添加其他包了,sort就相当于一个排序器它的构造方法有: Sort()           Sort(SortField field)           Sort(SortField[] fields)           Sort(String field)           Sort(String[] fields)           Sort(String field, boolean reverse)
  • 24. public class Test2{ public static void main(String[] args) throws java.io.IOException{ RAMDirectory rd = new RAMDirectory(); //RAMDirectory是把索引建到内存中,下面就是创建索引,这没的说吧,大家都会哦 IndexWriter writer = new IndexWriter(rd,new StandardAnalyzer()); Document doc = null; Field field = null; String id = null; String text = null; String time = null; //doc 0 doc = new Document(); id = "0"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "i love you, my mother land! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); time = "12:00 2007-05-28"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc);
  • 25. //doc 1 doc = new Document(); id = "1"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "i love you, xiaoyue,honey! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); time = "10:00 2007-05-29"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc); //doc 2 doc = new Document(); id = "2"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "people all love Genius! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED); doc.add(field); time = "22:00 2007-06-01"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc); //close writer.close(); /**********************************************************/
  • 26. //这里就是搜索了,排序就是在对搜索结果进行的操作 IndexSearcher searcher = new IndexSearcher(rd); //Term & Query String searchField = "text"; String searchPhrase = "love"; Term t = new Term(searchField, searchPhrase); TermQuery q = new TermQuery(t); //搜索条件封装好了就要执行搜索了,在搜索的过程中就要加上排序了,这是咱们现在学习的一个知识点 //Hits //SortField sf = new SortField("time",SortField.LONG,false); //Sort sort = new Sort(sf); //可以和下面的进行对比,要排序的字段能按照大家想要的类型进行比较排序的,在这里要注意core包的版本,在1.4以前的类型转化是很少的,现在我用的是2.9.0所有基本去有了 //先用这个对排序条件不加任何处理的字段来做为排序字段,就像怎么写的SQL语句中的order by Sort sort = new Sort("time",true); //按照时间排序,这没有把时间字符串转化成long型进行比较,而是把它作为字符串来比较的,大家可以参阅Lucene-API,在建立索引时都以字符串来存储的 //如果把true换做false就是从小到大了 Hits hs = searcher.search(q,sort);
  • 27. int num = hs.length(); StringBuffer sb = new StringBuffer(""); for(int i=0;i
  • 28. 排序说完了,就对高亮做一个介绍吧,HighLight。 大家先看例子,然后对照例子进行讲解 public class Test2 { public static void main(String[] args) throws java.io.IOException { //create index RAMDirectory rd = new RAMDirectory(); IndexWriter writer = new IndexWriter(rd,new StandardAnalyzer()); Document doc = null; Field field = null; String id = null; String text = null; String time = null; //doc 0 doc = new Document(); id = "0"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land!i love you, my mother land! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(field); time = "2007-05-28"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc);
  • 29. //doc 1 doc = new Document(); id = "1"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "i love you, xiaoyue,honey!i love you, xiaoyue,honey!i love you, xiaoyue,honey! i love you, xiaoyue,honey! i love you, xiaoyue,honey! i love you, xiaoyue,honey! i love you, xiaoyue,honey! i love you, xiaoyue,honey! i love you, xiaoyue,honey! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(field); time = "2007-05-29"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc); //doc 2 doc = new Document(); id = "2"; field = new Field("id",id ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); text = "people all love Genius!people all love Genius!people all love Genius!people all love Genius!people all love Genius!people all love Genius!people all love Genius!people all love Genius!people all love Genius! "; field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(field); time = "2007-06-01"; field = new Field("time", time ,Field.Store.YES, Field.Index.UN_TOKENIZED); doc.add(field); writer.addDocument(doc); //close writer.close(); /**********************************************************/
  • 30. IndexSearcher searcher = new IndexSearcher(rd); //Term & Query String searchField = "text"; String searchPhrase = "love"; Term t = new Term(searchField, searchPhrase); TermQuery q = new TermQuery(t); Hits hs = searcher.search(q,Sort.RELEVANCE); SimpleHTMLFormatter shf = new SimpleHTMLFormatter("",""); Highlighter highlighter =new Highlighter(shf,new QueryScorer(q)); SimpleFragmenter sf = new SimpleFragmenter(60); highlighter.setTextFragmenter( sf ); for (int i = 0; i < hs.length(); i++){ text = hs.doc(i).get("text"); int maxNumFragmentsRequired = 3; String fragmentSeparator = "!!!"; TermPositionVector tpv = (TermPositionVector)searcher.getIndexReader().getTermFreqVector(hs.id(i),"text"); TokenStream tokenStream=TokenSources.getTokenStream(tpv); String result = highlighter.getBestFragments( tokenStream, text, maxNumFragmentsRequired, fragmentSeparator); System.out.println(result); System.out.println("----------------------------------"); } searcher.close(); } }
  • 31. 通过上面的例子,大家注意到在增加高亮这一功能时创建索引是否发生变化呢,实现高亮的原理是(1)建立索引时,在文档的相关字段(需要高亮处理的字段,被搜索的字段)中记录词条(Term)的位置。 (2)搜索时,利用字段中记录的词条的位置信息。将修饰符号添加进去,从而改变了搜索关键字的显示格式,达到了突出显示的目的。 在创建索引是怎么记录词条的位置呢,这就的靠Field的构造方法的第五个参数了。看上面例子有这样的Field构造方法 field = new Field("text", text ,Field.Store.YES, Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); 最后一个参数就是来记录词条的位置的。要实现高亮先的构造Highlighter对象,我先介绍一种构造方法,Highlighter hl = new Highlighter (Scorer fragmentScorer); 其参数类型是Scorer,Scorer是一个接口。它的一个常用子类就是QueryScorer,这个类可以由QueryScorer (Query query)方法来构造。
  • 32. 下面看具体的构造Highlighter的步骤。 Term t = new Term(“id”,”1”);//这是封装查询条件 Query q = new TermQuery(t); QueryScorer qs = new QueryScorer(q); Highlighter hl = new Highlighter(qs); 如果搜索出来一个很大的文章,我们不想把整个文章的内容都返回去,那么我没就可一通过设定文本块的大小来实现返回部分文字,但命中的内容都能够返回。就需要下面的代码来实现了 SimpleFragmenter sf = new SimpleFragmenter(20);//设置文本块的大小 hl.setTextFragmenter( sf ); 现在已经对Highlighter对象的创建完成,但还没有对搜索结果进行高亮处理呢,参看代码再做讲解。
  • 33. 现在大家就可以简单的用Lucene来走搜索了,下面我再给大家介绍一下Lucene索引的管理,这里的管理也就是对索引的增加、删除和修改。 在原有索引上再继续添加索引,这就要在索引器上添加第三个参数了 ; IndexWriter iw = new IndexWriter(indexPath,new StandardAnalyzer(),false); 其他的和新建索引是一样的,第三个参数设置为false就是在原有的索引上继续添加新的索引。 对索引的删除,需要先把索引的索引都读取出来,然后再做删除,IndexReader就能够完成读取索引,大家可以参阅API,我在这就不详细介绍了,我只说一下IndexReader常用的方法,open()就是常用的一个,用法如下:IndexReader ir = IndexReader.open(indexPath);IndexPath就是存放索引文件的路径。IndexReader的其他方法在代码中做介绍.索引文件的修改,是要先删除已经存在的索引文件,然后再把新的索引文件添加上去。删除和添加都会了那更新也就会了吧。在简单的说一下索引的优化,建立索引的目的是为了搜索,搜索实际上是I/O操作,但索引数量增加,文件增大的时候,I/O操作就会变慢的,搜索速度也就会变慢的,索引对索引的优化是很有必要的。 (1)使用复合式索引文件,这样索引文件就不会太零散了。使用复合式索引文件可以通过设置IndexWriter的useCompoundFile参数,方法如下: writer.setUseCompoundFile(true);参数设置为true就是使用复合式索引文件,如果设置为false就不使用复合式索引文件,这个方法是索引优化的重要方法。
  • 34. (2)调整索引优化参数,内存I/O速度比文件I/O速度快,但内存中的信息是暂时性的,索引我们可以先在内存中建立索引,然后将索引写到文件系统中,这样比直接在文件系统中添加索引速度要快点,这是主要优化策略。 Lucene提供了3个优化参数,可以优化磁盘写入的频率和内存消耗。对这些参数因该灵活使用,从而优化索引,这3个参数可以通过IndexWriter类来设定。 1:mergeFactor 用来控制索引块的合并频率和大小,默认值是10. 2:maxMergeDocs 用来限制每个索引块的文档数量。 3: maxBufferedDocs 用来现在内存中的文档数量,默认值是10,这个值越大,在内存中存储是文档数量就越多,越消耗内存,同时磁盘I/O越少。 设置效果看代码演示效果。 下面我给大家再补充些常用的查询方法。 TermQuery(单字查询)、BooleanQuery(多条件组合查询)、PhraseQuery(短语查询)、PrefixQuery(前缀查询)、 FuzzyQuery(模糊查询,用于西文)、RangeQuery(范围搜索)大家可以查看API。 现在Lucene的基本操作也就讲得差不多了,由于我个人研究的比较粗浅,所以给大家也就是带个路,还需要大家深入的学习才能用好Lucene。
  • 35. 谢谢大家观看