Lucene7.2.1系列(三)查询及高亮
系列文章:
一 准备
创建项目并添加Maven依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core --> <!-- Lucene核心库 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>7.2.1</version> </dependency> <!-- Lucene解析库 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>7.2.1</version> </dependency> <!-- Lucene附加的分析库 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>7.2.1</version> </dependency> <!-- 高亮显示 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-highlighter</artifactId> <version>7.2.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-smartcn --> <!-- 中文分词 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-smartcn</artifactId> <version>7.2.0</version> </dependency> </dependencies>
二 对特定单词查询/模糊查询和查询表达式
写索引
import java.io.File; import java.io.FileReader; import java.nio.file.Paths; 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.TextField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; public class Indexer { private IndexWriter writer; // 写索引实例 /** * 构造方法 实例化IndexWriter * @param indexDir * @throws Exception */ public Indexer(String indexDir)throws Exception{ Directory dir=FSDirectory.open(Paths.get(indexDir)); Analyzer analyzer=new StandardAnalyzer(); // 标准分词器 IndexWriterConfig iwc=new IndexWriterConfig(analyzer); writer=new IndexWriter(dir, iwc); } /** * 关闭写索引 * @throws Exception */ public void close()throws Exception{ writer.close(); } /** * 索引指定目录的所有文件 * @param dataDir * @throws Exception */ public int index(String dataDir)throws Exception{ File []files=new File(dataDir).listFiles(); for(File f:files){ indexFile(f); } return writer.numDocs(); } /** * 索引指定文件 * @param f */ private void indexFile(File f) throws Exception{ System.out.println("索引文件:"+f.getCanonicalPath()); Document doc=getDocument(f); writer.addDocument(doc); } /** * 获取文档,文档里再设置每个字段 * @param f */ private Document getDocument(File f)throws Exception { Document doc=new Document(); doc.add(new TextField("contents",new FileReader(f))); doc.add(new TextField("fileName", f.getName(),Field.Store.YES)); doc.add(new TextField("fullPath",f.getCanonicalPath(),Field.Store.YES)); return doc; } public static void main(String[] args) { String indexDir="D:\\lucene\\searchindex"; String dataDir="D:\\lucene\\data"; Indexer indexer=null; int numIndexed=0; long start=System.currentTimeMillis(); try { indexer = new Indexer(indexDir); numIndexed=indexer.index(dataDir); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { indexer.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } long end=System.currentTimeMillis(); System.out.println("索引:"+numIndexed+" 个文件 花费了"+(end-start)+" 毫秒"); } }
读取索引
import java.nio.file.Paths; import org.apache.lucene.analysis.Analyzer; 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.QueryParser; 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.store.Directory; import org.apache.lucene.store.FSDirectory; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SearchTest { private Directory dir; private IndexReader reader; private IndexSearcher is; @Before public void setUp() throws Exception { dir=FSDirectory.open(Paths.get("D:\\lucene\\searchindex")); reader=DirectoryReader.open(dir); is=new IndexSearcher(reader); } @After public void tearDown() throws Exception { reader.close(); } }
对特定单词查询和模糊查询
/** * 对特定单词查询及模糊查询 * * @throws Exception */ @Test public void testTermQuery() throws Exception { String searchField = "contents"; // 所给出的必须是单词,不然差不到 String q = "authorship"; // 一个Term表示来自文本的一个单词。 Term t = new Term(searchField, q); // 为Term构造查询。 Query query = new TermQuery(t); /** * 1.需要根据条件查询 * * 2.最大可编辑数,取值范围0,1,2 * 允许我的查询条件的值,可以错误几个字符 * */ Query query2 = new FuzzyQuery(new Term(searchField,"authorshioo"),1); TopDocs hits = is.search(query, 10); // hits.totalHits:查询的总命中次数。即在几个文档中查到给定单词 System.out.println("匹配 '" + q + "',总共查询到" + hits.totalHits + "个文档"); for (ScoreDoc scoreDoc : hits.scoreDocs) { Document doc = is.doc(scoreDoc.doc); System.out.println(doc.get("fullPath")); } TopDocs hits2 = is.search(query2, 10); // hits.totalHits:查询的总命中次数。即在几个文档中查到给定单词 System.out.println("匹配 '" + "authorshioo"+ "',总共查询到" + hits2.totalHits + "个文档"); for (ScoreDoc scoreDoc : hits2.scoreDocs) { Document doc = is.doc(scoreDoc.doc); System.out.println(doc.get("fullPath")); } }
我们上面查询了单词“authorship”以及模糊查询了单词"authorshioo",结果如下:
可以看到只在LICENSE.txt文档下找到该单词。
那么模糊查询为什么查不到单词"authorshioo"呢?
这是因为我们在这里允许可以错误几个字符为1个,但是我们单词"authorshioo"错误字符个数为2个,所以就查不到。
Query query2 = new FuzzyQuery(new Term(searchField,"authorshioo"),1);
解析表达式的使用
/** * 解析查询表达式 * * @throws Exception */ @Test public void testQueryParser() throws Exception { // 标准分词器 Analyzer analyzer = new StandardAnalyzer(); String searchField = "contents"; String q = "atomic a atomicReader"; String q2 = "AtomicReader and AtomicReaderContext"; // 建立查询解析器 //searchField:要查询的字段; //analyzer:标准分词器实例 QueryParser parser = new QueryParser(searchField, analyzer); Query query = parser.parse(q); //返回查询到的前10项(查到100个相关内容的话也只会返回10个) TopDocs hits = is.search(query, 10); System.out.println("匹配 " + q + "查询到" + hits.totalHits + "个记录"); for (ScoreDoc scoreDoc : hits.scoreDocs) { Document doc = is.doc(scoreDoc.doc); System.out.println(doc.get("fullPath")); } QueryParser parser2 = new QueryParser(searchField, analyzer); Query query2 = parser2.parse(q2); //返回查询到的前10项(查到100个相关内容的话也只会返回10个) TopDocs hits2 = is.search(query2, 10); System.out.println("匹配 " + q2 + "查询到" + hits2.totalHits + "个记录"); for (ScoreDoc scoreDoc : hits2.scoreDocs) { Document doc = is.doc(scoreDoc.doc); System.out.println(doc.get("fullPath")); } }
我们上面分别查询了:“atomic a atomicReader”和“AtomicReader and AtomicReaderContext”,通过查询结果可以看出即使稍微改变查询内容,也还是可以查询到和我们给出的表达式相关的文档。
三 中文查询及高亮
写索引
import java.nio.file.Paths; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 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.store.Directory; import org.apache.lucene.store.FSDirectory; public class Indexer { private String[] ids={"1","2","3"}; private String citys[]={"青岛","南京","上海"}; private String descs[]={ "青岛是一个漂亮的城市。", "南京是一个文化的城市。", "上海是一个繁华的城市。" }; private Directory dir; /** *实例化indexerWriter * @return * @throws Exception */ private IndexWriter getWriter()throws Exception{ //中文分词器 SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); IndexWriterConfig iwc=new IndexWriterConfig(analyzer); IndexWriter writer=new IndexWriter(dir, iwc); return writer; } /** * 获取indexDir * @param indexDir * @throws Exception */ private void index(String indexDir)throws Exception{ dir=FSDirectory.open(Paths.get(indexDir)); IndexWriter writer=getWriter(); for(int i=0;i<ids.length;i++){ Document doc=new Document(); doc.add(new StringField("id", ids[i], Field.Store.YES)); doc.add(new StringField("city",citys[i],Field.Store.YES)); doc.add(new TextField("desc", descs[i], Field.Store.YES)); writer.addDocument(doc); } writer.close(); } public static void main(String[] args) throws Exception { new Indexer().index("D:\\lucene\\dataindex2"); System.out.println("Success Indexer"); } }
中文查询及高亮显示
import java.io.StringReader; import java.nio.file.Paths; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.queryparser.classic.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.search.highlight.Fragmenter; import org.apache.lucene.search.highlight.Highlighter; 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; /** * * 通过索引字段来读取文档 * @author LXY * */ public class SearchTest { public static void search(String indexDir, String par) throws Exception{ //得到读取索引文件的路径 Directory dir = FSDirectory.open(Paths.get(indexDir)); //通过dir得到的路径下的所有的文件 IndexReader reader = DirectoryReader.open(dir); //建立索引查询器 IndexSearcher searcher = new IndexSearcher(reader); //中文分词器 SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); //建立查询解析器 /** * 第一个参数是要查询的字段; * 第二个参数是分析器Analyzer * */ QueryParser parser = new QueryParser("desc", analyzer); //根据传进来的par查找 Query query = parser.parse(par); //计算索引开始时间 long start = System.currentTimeMillis(); //开始查询 /** * 第一个参数是通过传过来的参数来查找得到的query; * 第二个参数是要出查询的行数 * */ TopDocs topDocs = searcher.search(query, 10); //索引结束时间 long end = System.currentTimeMillis(); System.out.println("匹配"+par+",总共花费了"+(end-start)+"毫秒,共查到"+topDocs.totalHits+"条记录。"); //高亮显示start //算分 QueryScorer scorer=new QueryScorer(query); //显示得分高的片段 Fragmenter fragmenter=new SimpleSpanFragmenter(scorer); //设置标签内部关键字的颜色 //第一个参数:标签的前半部分;第二个参数:标签的后半部分。 SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>"); //第一个参数是对查到的结果进行实例化;第二个是片段得分(显示得分高的片段,即摘要) Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer); //设置片段 highlighter.setTextFragmenter(fragmenter); //高亮显示end //遍历topDocs /** * ScoreDoc:是代表一个结果的相关度得分与文档编号等信息的对象。 * scoreDocs:代表文件的数组 * @throws Exception * */ for(ScoreDoc scoreDoc : topDocs.scoreDocs){ //获取文档 Document document = searcher.doc(scoreDoc.doc); //输出全路径 System.out.println(document.get("city")); System.out.println(document.get("desc")); String desc = document.get("desc"); if(desc!=null){ //把全部得分高的摘要给显示出来 //第一个参数是对哪个参数进行设置;第二个是以流的方式读入 TokenStream tokenStream=analyzer.tokenStream("desc", new StringReader(desc)); //获取最高的片段 System.out.println(highlighter.getBestFragment(tokenStream, desc)); } } reader.close(); } //开始测试 public static void main(String[] args) { //索引指定的路径 String indexDir = "D:\\lucene\\dataindex2"; //查询的字段 String par = "南京"; try { search(indexDir,par); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
结果会把我们查询的“南京”单词给高亮显示,这在我们平时搜索中很常见了。
我们平时搜索中的高亮就像下图:
欢迎关注我的微信公众号(分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):
Lucene我想暂时先更新到这里,仅仅这三篇文章想掌握Lucene是远远不够的。另外我这里三篇文章都用的最新的jar包,Lucene更新太快,5系列后的版本和之前的有些地方还是有挺大差距的,就比如为文档域设置权值的setBoost方法6.6以后已经被废除了等等。因为时间有限,所以我就草草的看了一下Lucene的官方文档,大多数内容还是看java1234网站的这个视频来学习的,然后在版本和部分代码上做了改进。截止2018/4/1,上述代码所用的jar包皆为最新。
最后推荐一下自己觉得还不错的Lucene学习网站/博客:
Github:Apache Lucene and Solr

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Lucene7.2.1系列(二)luke使用及索引文档的基本操作
系列文章: Lucene系列(一)快速入门 Lucene系列(二)luke使用及索引文档的基本操作 Lucene系列(三)查询及高亮 luke入门 简介: github地址:https://github.com/DmitryKey/luke 下载地址:https://github.com/DmitryKey/luke/releasesLuke是一个用于Lucene/Solr/Elasticsearch 搜索引擎的,方便开发和诊断的 GUI(可视化)工具。 它有以下功能: 查看文档并分析其内容(用于存储字段) 在索引中搜索 执行索引维护:索引运行状况检查;索引优化(运行前需要备份) 从hdfs读取索引 将索引或其部分导出为XML格式 测试定制的Lucene分析工具 创建自己的插件 luke适用的搜索引擎 Apache Lucene. 大多数情况下,luke可以打开由纯Lucene生成的lucene索引。 现在人们做出纯粹的Lucene索引吗? Apache Solr. Solr和Lucene共享相同的代码库,所以luke很自然可以打开Solr生成的Lucene索引。 Elasticsea...
- 下一篇
(1)剑指Offer之斐波那契数列问题和跳台阶问题
一 斐波那契数列 题目描述: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39 问题分析: 可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有一个很大的问题,那就是递归大量的重复计算会导致内存溢出。另外可以使用迭代法,用fn1和fn2保存计算过程中的结果,并复用起来。下面我会把两个方法示例代码都给出来并给出两个方法的运行时间对比。 示例代码: 采用迭代法: int Fibonacci(int number) { if (number <= 0) { return 0; } if (number == 1 || number == 2) { return 1; } int first = 1, second = 1, third = 0; for (int i = 3; i <= number; i++) { third = first + second; first = second; second = third; } return third; } 采用递归: public int Fibonacci(int n)...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS关闭SELinux安全模块