[lucene]08.获取最佳摘要以及高亮配置
首先明白一下什么是最佳摘要?
比如,现在有个文本存放的是一段“围城”,因为故事的主人公是“方鸿渐”,这段文本里面可能有10个地方都出现了方鸿渐,当我使用“方鸿渐”作为关键词去查询时,我想要展示的100个字,然后这100个字是文本里面连续的一段话,这段话里面含有的“方鸿渐”这个关键字最密集,那么这段话就是最佳摘要。
理解了什么是最佳摘要,那么我们来看一下如何在lucene中获取最佳摘要,我们还是使用之前的例子,只是加上了跟获取最佳摘要以及高亮配置有关的代码。
package cn.zhao.cms.column; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.nio.charset.Charset; import java.nio.file.Paths; import org.apache.commons.io.FileUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 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.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.MultiFields; import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.queryparser.classic.QueryParser.Operator; 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.search.highlight.Fragmenter; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; import org.apache.lucene.search.highlight.QueryScorer; import org.apache.lucene.search.highlight.SimpleHTMLFormatter; import org.apache.lucene.search.highlight.SimpleSpanFragmenter; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.BytesRef; public class TestLucene { public static void main(String[] args) throws Exception{ createIndex(); searchIndex(); } //检索 private static void searchIndex() throws IOException, ParseException, InvalidTokenOffsetsException { Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index")); IndexReader ir=DirectoryReader.open(directory); //获取某个域分词后的terms Terms terms = MultiFields.getTerms(ir, "content"); TermsEnum iterator = terms.iterator(); BytesRef bRef = null; int rank=1; while ((bRef=iterator.next())!=null) { String oneTerm=new String(bRef.bytes, bRef.offset, bRef.length,Charset.forName("utf-8")); //docFreq:当前这个term在几个document里面出现了 System.out.println("第"+rank+"个term是:"+oneTerm+",共在:"+iterator.docFreq() +"个文档出现,该term在所有文档中共出现:" +ir.totalTermFreq(new Term("content", bRef))+"次"); rank++; } //可以看做是jdbc里面的connection IndexSearcher isearcher = new IndexSearcher(ir); //Analyzer analyzer=new StandardAnalyzer(); SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); //可以看成是sql里面的PreparedStatement,下面就像是 content=?的意思一样 QueryParser parser = new QueryParser("content", analyzer); parser.setDefaultOperator(Operator.OR);//默认空格就是OR parser.setAllowLeadingWildcard(true);//设置通配符能在第一位 //query对象就像一个最终生成的sql语句,下面就像是ps.setString("xxx")一样 //能不能搜索到,要看分词器是否把content里面的内容切出了"窃窃私语"这个term单元,英文分词器 //可能会切出"窃","私","语",而中文分词器可能就会切出"窃窃私语"来 Query query = parser.parse("href"); //可能有很多个文档含有上边的搜索词,这里10代表只取前10条 TopDocs tds = isearcher.search(query,10); ScoreDoc[] sds = tds.scoreDocs; int length = sds.length; System.out.println("共搜索到"+length+"个文档含有设置的关键词"); //高亮配置 QueryScorer queryScorer=new QueryScorer(query); Fragmenter fragmenter=new SimpleSpanFragmenter(queryScorer); //SimpleHTMLFormatter("","");//不加红也不加粗了 SimpleHTMLFormatter formatter=new SimpleHTMLFormatter("<b><font color='red'>", "</font></b>"); Highlighter highlighter=new Highlighter(formatter, queryScorer); highlighter.setTextFragmenter(fragmenter); for (int i = 0; i < length; i++) { Document hitDoc = isearcher.doc(sds[i].doc); System.out.println("得到设置的id域:"+hitDoc.get("docurl")); System.out.println("得到设置的docurl域"+hitDoc.get("docurl")); String content = hitDoc.get("content"); System.out.println("内容:"+content); //根据高亮配置获取最佳摘要 TokenStream tStream=analyzer.tokenStream("content", new StringReader(content)); String bestFragment = highlighter.getBestFragment(tStream, content); System.out.println("最佳摘要:"+bestFragment); } ir.close(); directory.close(); } //创建索引 private static void createIndex() throws IOException { //指定存放lucene生成的索引的目录,可以把这个目录看做一张表,lucene的所有索引都存放在这个表中 Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index")); //使用默认的英文分词器 // Analyzer analyzer=new StandardAnalyzer(); //使用对中文支持更好的分词器 SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); IndexWriterConfig conf=new IndexWriterConfig(analyzer); IndexWriter iwriter =new IndexWriter(directory, conf); //每次创建先删除原来的已经生成的索引 iwriter.deleteAll(); //deleteAll只是放进了lucene索引回收站,这里还要从回收站也删除掉 iwriter.forceMergeDeletes(); //这里我索引了D:\\lucene\\data目录下的所有文件 File dirFile=new File("D:\\lucene\\data"); File[] listFiles =dirFile.listFiles(); for(int i=0;i<listFiles.length;i++){ File file=listFiles[i]; //Document可以看做表的一条记录,记录自然有多个field,可以看做表里的字段 //field在lucene里叫域,一个域有n(>=1)个terms,这个terms没法类比关系型数据库里面的概念了, //你可以理解每个字段的内容按一定的切分规则(分词器)切分成了若干单元,每个单元就叫做term Document document=new Document(); document.add(new StringField("id", 0+"", Field.Store.YES)); document.add(new StringField("docurl", file.getAbsolutePath(), Field.Store.YES)); TextField textField = new TextField("content", FileUtils.readFileToString(file), Field.Store.YES); if (file.getName().contains("tomcat")) {//设置加权权重信息norm textField.setBoost(2.0f);//默认为1,设置的值越大,搜索的时候越排在前面 } document.add(textField); //按指定的分词规则analyzer写入存放索引的目录D:\\lucene\\index iwriter.addDocument(document); } System.out.println("实际索引多少个文档:"+iwriter.numDocs()); System.out.println("最大索引多少个文档:"+iwriter.maxDoc()); //最后记得关闭资源 iwriter.close(); directory.close(); } }
运行结果:
代码说明:
1.最佳摘要是基于关键词的,关键词不同,你得到的最佳摘要自然也是不同的。
2.代码跟前边几篇文章差不多,只不过是加了高亮和最佳摘要的代码,虽然看着多了点,但是都是原先几篇文章的老代码
3.既然获取了最佳摘要,也对关键字加粗加红了,那么把最佳摘要显示在网页上的时候,就有了高亮的效果了,比如,我们的乐之者java网站的搜索: