Lucene7.2.1系列(一)快速入门
系列文章:
Lucene是什么?
Lucene在维基百科的定义
Lucene是一套用于全文检索和搜索的开放源代码程序库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程序接口,能够做全文索引和搜索,在Java开发环境里Lucene是一个成熟的免费开放源代码工具;就其本身而论,Lucene是现在并且是这几年,最受欢迎的免费Java信息检索程序库。
另外,Lucene不提供爬虫功能,如果需要获取内容需要自己建立爬虫应用。
Lucene只做索引和搜索工作。
Lucene官网
打开Luncene官网你会发现Lucene版本更新的太快了,现在最新的版本已经是7.2.1。不过这也变相说明了Luncene这个开源库的火爆。
Lucene和solr
我想提到Lucene,不得不提solr了。
很多刚接触Lucene和Solr的人都会问这个明显的问题:我应该使用Lucene还是Solr?
答案很简单:如果你问自己这个问题,在99%的情况下,你想使用的是Solr. 形象的来说Solr和Lucene之间关系的方式是汽车及其引擎。 你不能驾驶一台发动机,但可以开一辆汽车。 同样,Lucene是一个程序化库,您不能按原样使用,而Solr是一个完整的应用程序,您可以立即使用它。(参考:Lucene vs Solr)
全文检索是什么?
全文检索在百度百科的定义
全文数据库是全文检索系统的主要构成部分。所谓全文数据库是将一个完整的信息源的全部内容转化为计算机可以识别、处理的信息单元而形成的数据集合。全文数据库不仅存储了信息,而且还有对全文数据进行词、字、段落等更深层次的编辑、加工的功能,而且所有全文数据库无一不是海量信息数据库。
全文检索首先将要查询的目标文档中的词提取出来,组成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。
全面、准确和快速是衡量全文检索系统的关键指标。
关于全文检索,我们要知道:
- 只处理文本。
- 不处理语义。
- 搜索时英文不区分大小写。
- 结果列表有相关度排序。(查出的结果如果没有相关度排序,那么系统不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果。)
在信息检索工具中,全文检索是最具通用性和实用性的。
参考:https://zhuanlan.zhihu.com/p/25558228
全文检索和数据库搜索的区别
>
简单来说,这两者解决的问题是不一样。数据库搜索在匹配效果、速度、效率等方面都逊色于全文检索。下面我们的一个例子就能很清楚说明这一点。
Lucene实现全文检索流程是什么?
全文检索的流程分为两大部分:索引流程、搜索流程。
索引流程:即采集数据构建文档对象分析文档(分词)创建索引。
搜索流程:即用户通过搜索界面创建查询执行搜索,搜索器从索引库搜索渲染搜索结果
我们在下面的一个程序中,对这个全文检索的流程会有进一步的了解。
Lucene实现向文档写索引并读取文档
截止2018/3/30,用到的jar包结为最新。
程序用到的数据下载地址:
链接:https://pan.baidu.com/s/1ccgrCCRBBGOL-fmmOLrxlQ
密码:vyof
1. 创建Maven项目,并添加相关jar包依赖
<!-- 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>
- 向文档里写索引
package lucene_demo1;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
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;
/**
*
*TODO 索引文件
* @author Snaiclimb
* @date 2018年3月30日
* @version 1.8
*/
public class Indexer {
// 写索引实例
private IndexWriter writer;
/**
* 构造方法 实例化IndexWriter
*
* @param indexDir
* @throws IOException
*/
public Indexer(String indexDir) throws IOException {
//得到索引所在目录的路径
Directory directory = FSDirectory.open(Paths.get(indexDir));
// 标准分词器
Analyzer analyzer = new StandardAnalyzer();
//保存用于创建IndexWriter的所有配置。
IndexWriterConfig iwConfig = new IndexWriterConfig(analyzer);
//实例化IndexWriter
writer = new IndexWriter(directory, iwConfig);
}
/**
* 关闭写索引
*
* @throws Exception
* @return 索引了多少个文件
*/
public void close() throws IOException {
writer.close();
}
public int index(String dataDir) throws Exception {
File[] files = new File(dataDir).listFiles();
for (File file : files) {
//索引指定文件
indexFile(file);
}
//返回索引了多少个文件
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
* @return document
*/
private Document getDocument(File f) throws Exception {
Document doc = new Document();
//把设置好的索引加到Document里,以便在确定被索引文档
doc.add(new TextField("contents", new FileReader(f)));
//Field.Store.YES:把文件名存索引文件里,为NO就说明不需要加到索引文件里去
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\\dataindex";
////被索引数据的路径
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) + " 毫秒");
}
}
运行效果:
我们查看D:\lucene\dataindex文件夹。我们发现多了一些东西,这些东西就是我们马上用来全文搜索的索引。
//索引指定的文档路径
String indexDir = "D:\\lucene\\dataindex";
- 全文检索测试
package lucene_demo1;
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.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.store.Directory;
import org.apache.lucene.store.FSDirectory;
/**
* 根据索引搜索
*TODO
* @author Snaiclimb
* @date 2018年3月25日
* @version 1.8
*/
public class Searcher {
public static void search(String indexDir, String q) throws Exception {
// 得到读取索引文件的路径
Directory dir = FSDirectory.open(Paths.get(indexDir));
// 通过dir得到的路径下的所有的文件
IndexReader reader = DirectoryReader.open(dir);
// 建立索引查询器
IndexSearcher is = new IndexSearcher(reader);
// 实例化分析器
Analyzer analyzer = new StandardAnalyzer();
// 建立查询解析器
/**
* 第一个参数是要查询的字段; 第二个参数是分析器Analyzer
*/
QueryParser parser = new QueryParser("contents", analyzer);
// 根据传进来的p查找
Query query = parser.parse(q);
// 计算索引开始时间
long start = System.currentTimeMillis();
// 开始查询
/**
* 第一个参数是通过传过来的参数来查找得到的query; 第二个参数是要出查询的行数
*/
TopDocs hits = is.search(query, 10);
// 计算索引结束时间
long end = System.currentTimeMillis();
System.out.println("匹配 " + q + " ,总共花费" + (end - start) + "毫秒" + "查询到" + hits.totalHits + "个记录");
// 遍历hits.scoreDocs,得到scoreDoc
/**
* ScoreDoc:得分文档,即得到文档 scoreDocs:代表的是topDocs这个文档数组
*
* @throws Exception
*/
for (ScoreDoc scoreDoc : hits.scoreDocs) {
Document doc = is.doc(scoreDoc.doc);
System.out.println(doc.get("fullPath"));
}
// 关闭reader
reader.close();
}
public static void main(String[] args) {
String indexDir = "D:\\lucene\\dataindex";
//我们要搜索的内容
String q = "Jean-Philippe sdsds Barrette-LaPierre";
try {
search(indexDir, q);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
上面我们搜索的是:”Jean-Philippe Barrette-LaPierre”;即使你:”Jean-Philippe ssss Barrette-LaPierre”这样搜索也还是搜索到,以为Lucene对其进行了分词,对中文无效。
Lucene实现全文检索流程是什么?
我们刚刚实现的程序已经清楚地向我们展示了Lucene实现全文检索流程,我们再来回顾一下。
- 在Lucene中,采集数据(从网站爬取或连接数据库)就是为了创建索引,创建索引需要先将采集的原始数据加工为文档,再由文档分词产生索引。文档(Document) 中包含若干个Field域。
- IndexWriter是索引过程的核心组件,通过IndexWriter可以创建新索引、更新索引、删除索引操作。IndexWriter需要通过Directory对索引进行存储操作。
- Directory描述了索引的存储位置,底层封装了I/O操作,负责对索引进行存储。它是一个抽象类,它的子类常用的包括FSDirectory(在文件系统存储索引)、RAMDirectory(在内存存储索引)。
- 在对Docuemnt中的内容索引之前需要使用分词器进行分词 ,分词的主要过程就是分词、过滤两步。 分词就是将采集到的文档内容切分成一个一个的词,具体应该说是将Document中Field的value值切分成一个一个的词。
过滤包括去除标点符号、去除停用词(的、是、a、an、the等)、大写转小写、词的形还原(复数形式转成单数形参、过去式转成现在式等)。 - 停用词是为节省存储空间和提高搜索效率,搜索引擎在索引页面或处理搜索请求时会自动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。比如语气助词、副词、介词、连接词等,通常自身并无明确的意义,只有将其放入一个完整的句子中才有一定作用,如常见的“的”、“在”、“是”、“啊”等。
Lucene中自带了StandardAnalyzer,它可以对英文进行分词。
参照:https://zhuanlan.zhihu.com/p/25558228
欢迎关注我的微信公众号(分享各种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
Lucene7.2.1系列(一)快速入门的更多相关文章
- SpringBoot系列: RestTemplate 快速入门
====================================相关的文章====================================SpringBoot系列: 与Spring R ...
- Flask开发系列之快速入门
Flask开发系列之快速入门 文档 一个最小的应用 调试模式 路由 变量规则 构造 URL HTTP 方法 静态文件 模板渲染 访问请求数据 环境局部变量 请求对象 文件上传 Cookies 重定向和 ...
- BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序
BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...
- BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 连接数据库执行SQL语句
BIML 101 - BIML 快速入门教程 第一节 连接数据库执行SQL语句 本小节将用BIML建一个简单的可以执行的包. 新建一个biml文件,贴入下面的代码 1 <Biml xmlns=& ...
- Maven系列之快速入门
文章结构 唯快不破---Maven快速入门 稳打稳扎---Maven核心知识 实用为先---Maven如何建立Web项目 1 唯快不破---Maven快速入门 1.1 Maven项目 ...
- Quartz.NET开源作业调度框架系列(一):快速入门step by step
Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...
- SpringBoot系列: JdbcTemplate 快速入门
对于一些小的项目, 我们没有必要使用MyBatis/JPA/Hibernate等重量级技术, 直接使用Spring JDBC 即可, Spring JDBC 是对 jdbc的简单封装, 很容易掌握. ...
- Zookeeper系列1 快速入门
Zookeeper的简介这里我就不说了,在接下来的几篇文章会涉及zookeeper环境搭建,watcher以及相关配置说明, 三种操作zookeeper的方式(原生API方式,zkclient,Cur ...
- Quartz.NET开源作业调度框架系列(一):快速入门step by step-转
Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...
- BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 将文本文件(csv)数据导进数据库
第二节 将文本文件数据导进数据库 该小节介绍如何用BIML生成ssis包,将货币文本导入到数据库currency的表中. SSIS组件: Connection Manager组建管理connectio ...
随机推荐
- 【.NET】- async await 异步编程
为什么需要异步,异步对可能起阻止作用的活动(例如,应用程序访问 Web 时)至关重要. 对 Web 资源的访问有时很慢或会延迟. 如果此类活动在同步过程中受阻,则整个应用程序必须等待. 在异步过程中, ...
- Flink中的数据传输与背压
一图道尽心酸: 大的原理,上游的task产生数据后,会写在本地的缓存中,然后通知JM自己的数据已经好了,JM通知下游的Task去拉取数据,下游的Task然后去上游的Task拉取数据,形成链条. 但是在 ...
- 用svmpredict输出的结果为空
源程序:
- array to object
array to object native js & ES6 https://stackoverflow.com/questions/4215737/convert-array-to-obj ...
- 如何取得dbgrid中未保存(post)的值(50分)
比如说处在编辑状态时,想取得当前记录值 Dataset.fields[0].Value 就是当前值:Dataset.fields[0].OldValue 就是原始值. 呵呵,我指得是在编辑时,就是按键 ...
- Gevent-socket
1. 通过Gevent实现单线程下的多socket并发. server 端: #server side import sys import socket import time import geve ...
- CentOS ACL
ACL:访问控制列表(Access Control List). 一般来说权限是针对某一类用户设置的.例如:一个文件只有拥有者.组.其他用户三种设置方式,如果希望对某个指定的用户进行单独的权限控制,就 ...
- [CF1037H] Security
题目链接 codeforces. 洛谷. Solution 按照套路,可以\(SAM\)上线段树合并求出\(endpos\)集合,然后随便贪心一下就好了. #include<bits/stdc+ ...
- Jquery常用正则验证
常用校验的正则表达式var rulesConfig = { /** * str.replace(/^\s+|\s+$/g, '') 解析: str:要替换的字符串 \s : 表示 space ,空格 ...
- C++之内部类与外部类(嵌套类)及友元
转载于:http://www.cnblogs.com/qzhforthelife/p/3226885.html 先上代码: class Outer { public: Outer(){m_outerI ...