系列文章:

Lucene系列(一)快速入门

Lucene系列(二)luke使用及索引文档的基本操作

Lucene系列(三)查询及高亮

一 准备

创建项目并添加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学习网站/博客:

官方网站:[Welcome to Apache Lucene](Welcome to Apache Lucene)

Github:Apache Lucene and Solr

Lucene专栏

搜索系统18:lucene索引文件结构

Lucene6.6的介绍和使用

Lucene7.2.1系列(三)查询及高亮的更多相关文章

  1. eventFlow 系列 <三> 查询所有

    接着上面的例子,产生2条数据.怎么把这两条数据查询出来呢? var commandBus = resolver.Resolve<ICommandBus>(); , ); var execu ...

  2. Lucene7.2.1系列(二)luke使用及索引文档的基本操作

    系列文章: Lucene系列(一)快速入门 Lucene系列(二)luke使用及索引文档的基本操作 Lucene系列(三)查询及高亮 luke入门 简介: github地址:https://githu ...

  3. Lucene7.2.1系列(一)快速入门

    系列文章: Lucene系列(一)快速入门 Lucene系列(二)luke使用及索引文档的基本操作 Lucene系列(三)查询及高亮 Lucene是什么? Lucene在维基百科的定义 Lucene是 ...

  4. Elasticsearch使用系列-基本查询和聚合查询+sql插件

    Elasticsearch使用系列-ES简介和环境搭建 Elasticsearch使用系列-ES增删查改基本操作+ik分词 Elasticsearch使用系列-基本查询和聚合查询+sql插件 Elas ...

  5. MySQL并发复制系列三:MySQL和MariaDB实现对比

    http://blog.itpub.net/28218939/viewspace-1975856/ 并发复制(Parallel Replication) 系列三:MySQL 5.7 和MariaDB ...

  6. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  7. SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型

    原文:SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server ...

  8. mybatis入门系列三之类型转换器

    mybatis入门系列三之类型转换器 类型转换器介绍 mybatis作为一个ORM框架,要求java中的对象与数据库中的表记录应该对应 因此java类名-数据库表名,java类属性名-数据库表字段名, ...

  9. ldap配置系列三:grafana集成ldap

    ldap配置系列三:grafana集成ldap grafana的简介 grafana是一个类似kibana的东西,是对来自各种数据源的数据进行实时展示的平台,拥有这牛逼的外观.给一个官方的demo体验 ...

随机推荐

  1. virtio是啥子

    这个山头今天好像要攻占下来了 guest os中的一些特权操作会被hypervhisor给接收,这里一个很重要的认识是:hypervisor是os的os,既然要访问资源,那么就需要经过整机资源的管理者 ...

  2. SQL Inserted和deleted详解

    create trigger updateDeleteTime on user for update as begin update user set UpdateTime=(getdate()) f ...

  3. Django安装及简介

    一. Django简介 Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.许多成功的网站和APP都基于Django. Django是一个开放源代码的Web应用框架 ...

  4. linux内核分析 第一周 计算机是如何工作的 20125221银雪纯

    我使用的c语言代码是: int g(int x) { return x + 1; } int f(int x) { return g(x); } int main(void) { return f(6 ...

  5. 2017-2018-2 20165218 实验三《敏捷开发与XP实践》实验报告

    实验三 敏捷开发与XP实践 课程:java程序设计 姓名:赵冰雨 学号:20165218 指导教师:娄嘉鹏 实验日期:2018.4.30 实验内容.步骤与体会: (一)编码标准 //实验要求 //参考 ...

  6. js 多个事件的绑定及移除(包括原生写法和 jquery 写法)

    需要打开控制台查看效果: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  7. 010. C++ 传值与传引用

    1.参数传递 参数传递:pass by value vs. pass by reference(to const) 推荐:能传引用,尽量传引用(高效,尤其在需要拷贝的对象很大时) class comp ...

  8. UVA - 11181 数学

    UVA - 11181 题意: n个人去买东西,其中第i个人买东西的概率是p[i],最后只有r个人买了东西,求每个人实际买了东西的概率 代码: //在r个人买东西的概率下每个人买了东西的概率,这是条件 ...

  9. 面试的角度诠释Java工程师

    原文出处: locality 一.基础篇 1.面向对象的三大特性 继承.封装.多态 什么是继承?①继承是面向对象程序设计能够提高软件开发效率的重要原因之一.②继承是具有传递性的,就像现实中孙子不仅长得 ...

  10. C语言基本类型的字节数