初识Lucene 4.5全文搜索

近期想研究下lucene,但网络上的教程大多都是lucne 3.x版本的讲解。可是lucene版本的更新速度快的惊人,目前已经到了4.8版了,只好去查阅官方文档。虽然英文不大好,但稍微对比了下发现3.x版本至4.x版本的修改非常之大。接下来我就以4.5版来操作,分享下我对luence的初步认识。

先给大家看一张图(来至《Lucene  in  action》):

此图很形象的描述了lucene的基本流程,简而言之就是:1、创建索引;2、检索索引。

太深的道理与原理我目前也还是一知半解,所以就以小白的思维来阐述。

Lucene与数据库有许多相通之处,以下我们做个简单对比:


数据库

Luecene

基本概念

列/字段

Field

行/记录

Document

基本操作

查询(SELECT)

Searcher

添加(INSERT)

IndexWriter. addDocument


删除(DELETE)

IndexReader.delete

修改(UPDATE)

不支持(可删除后重新添加)

上面的表格式某位网友博文里的对比,我觉得挺好理解的。可以这么去认为吧,lucene把你数据库里的数据做了个索引,以后你要全文查找某数据时就可以从索引中查找,就好比字典的索引目录!

废话说了一大堆,还是用代码来说话,首先要往代码里导入三个包:

lucene-analyzers-common-4.5.0、lucene-core-4.5.0、lucene-queryparser-4.5.0

要有面向对象的思维嘛,先创建一个javabean:luceneBeans,用来存放你所要的数据

package pojo;
public class LuceneBeans {
private String id;
private String title;
private String introduce;
private String addtime;
private String category;
public LuceneBeans() {
super();
}
public LuceneBeans(String id, String title, String introduce,
String addtime, String category) {
super();
this.id = id;
this.title = title;
this.introduce = introduce;
this.addtime = addtime;
this.category = category;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
public String getAddtime() {
return addtime;
}
public void setAddtime(String addtime) {
this.addtime = addtime;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}

以下代码把lucene基本操作方法封装在了IndexUtil类中,其中考虑到创建indexReader开销过大,就设计了单实例模式。注释中会穿插些4.x版本与3.x版本的不同之处。

package lucene;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.document.Field.Store;
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.Term;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.ParseException;
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;
import org.apache.lucene.util.Version;
import pojo.LuceneBeans;
public class IndexUtil {
private static Directory directory = null;
private static IndexReader reader = null;
/**
* 设置存储路径
* @return
*/
public static String getIndexDir(){
//得到.class文件所在路径
String classpath = LuceneUtil.class.getResource("/").getPath();
//将class中的%20替换成为空格
classpath = classpath.replaceAll("%20", " ");
//索引存储位置:WEB-INF/classes/index
String path = classpath+"index/";
return path;
}
public IndexUtil(){
try {
//存储方式有CompoundFileDirectory, FileSwitchDirectory, FSDirectory,
//NRTCachingDirectory, RAMDirectory……等分别对应的不同方式
//用FSDirectory的好处就在于它会自动帮你分配该使用哪种方式
directory = FSDirectory.open(new File(getIndexDir()));
//3.x是reader=IndexReader.open(directory);
//但4.x后已经不建议使用,改成了DirectoryReader.open(directory)
reader = DirectoryReader.open(directory);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建IndexWriter
* @return
*/
public static IndexWriter getIndexWriter(){
//创建分词器,分词器可根据自己需求去自定义创建,此处以lucene自带的标准分词器分词
Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_45);
IndexWriter indexWriter = null;
try {
IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_45, analyzer);
iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
indexWriter = new IndexWriter(directory, iwConfig);
return indexWriter;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
//--------省略余下方法代码

嗯~创建索引前我们需要先获得IndexWriter,其实步骤就像你往数据库中插入一条数据(document),得再这条数据中加入字段(Field),得给这个字段做个说明(Store、Index)……

/**
* 批量创建索引
* @param list
* @return
*/
public boolean createIndexList(List<LuceneBeans> list){
boolean result = false;
IndexWriter indexWriter = IndexUtil.getIndexWriter();
try {
if(list!=null&&list.size()>0){
Document doc = null;
for(int i=0;i<list.size();i++){
doc = new Document();
LuceneBeans lb = list.get(i);
doc.add(new StringField("id",lb.getId(),Store.YES));
doc.add(new TextField("title",lb.getTitle(),Store.YES));
doc.add(new TextField("introduce",lb.getIntroduce(),Store.YES));
doc.add(new StringField("addtime",lb.getAddtime(),Store.YES));
doc.add(new StringField("category",lb.getCategory(),Store.YES));
indexWriter.addDocument(doc);
indexWriter.commit();
result = true;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(indexWriter!=null){
try {
indexWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}

之前3.x版本是这样写的:

doc.add(new Field("id",lb.getI(),Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS));

那我们就顺便来分析下store、index

(存储域选项)Store判断是否把域存入文件(yes存入,可还原;no不存入,不可还原但可被索引)
 (索引域选项)Index:
   //|----ANALYZED进行分词和索引,适用于标题、内容等
   //|----NO_ANALYZED进行索引但是不进行分词,身份号、姓名、ID等,适用于精确搜索
   //|----ANALYZED_NOT_NORMS进行分词但不存储norms信息,这个norms中包括了创建索引的时间和权值等信息
   //|----NOT_ANALYZED_NOT_NORMS既不进行分词也不存储norms信息

但4.x之后就改变了,它需要更为精确的StringField、TextField、IntField……

那为什么4.x版本只写了Store而没Index?还是让我们看看官方文档上的描述吧:

先看TxetField:自动分词

再看看StringField:不分词

所以,4.x有很多写法是与3.x不同的,虽然还可以向下兼容,但总是要跟着时代的脚步前进嘛!

那接下来我们可以写个测试方法看看效果,至于luceneBeans的数据我就不贴出来了,大家可以自己导入,我们不仅可以用javaBean的数据来创建索引,像txt、pdf、ini等文件的数据也是可以导入来创建索引的;

如果你创建成功了则在对应的index文件夹下会多出这些文件:

创建好索引后,当然我们要开始检索索引咯,重要的是获得indexReader,然后创建indexSearch去检索。我知识举个最常规的用法,其中还有很多东西还需个人去体会。(接上面代码写在IndexUtil类内)

/**
* 创建IndexReader
* 为保证检索时,索引的同步更新,需对reader进行判断
* @return
*/
public static IndexSearcher getIndexSearcher(){
try {
//判断reader是否为空
if(reader==null){
reader = DirectoryReader.open(directory);
}else{
//3.x版本是用IndexReader.openIfChange(reader);来判断reader是否有改变。
//reader有改变则返回改变后的;若无改变则返回null
IndexReader ir = DirectoryReader.openIfChanged((DirectoryReader)reader);
if(ir!=null){
//reader已改变则关闭原先的,再将改变后的赋值给reader
reader.close();
reader = ir;
}
}
return new IndexSearcher(reader);
} catch (IOException e) {
e.printStackTrace();
return null;
}
/**
* 从索引中检索
* @param content
* @return
*/
public List<LuceneBeans> searchIndex(String content){
List<LuceneBeans> list = new ArrayList<LuceneBeans>();
try {
System.out.println("文档数numDocs:"+reader.numDocs());
System.out.println("文档总数maxDocs:"+reader.maxDoc());
System.out.println("删除数deleteDocs:"+reader.numDeletedDocs());
IndexSearcher searcher = IndexUtil.getIndexSearcher();
//使用查询解析器创建Query(Version.LUCENE_45对应版本号,“introduce”默认索引域)
//new StandardAnalyzer(Version.LUCENE_45))标准分词器
//Query的方法还有很多种,例如TermQuery精确查询、PrefixQuery前缀查询、WildcardQuery通配符查询……等
QueryParser questParser = new QueryParser(Version.LUCENE_45,"introduce",
new StandardAnalyzer(Version.LUCENE_45));
Query query = null;
try {
query = questParser.parse(content);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("查询语句:"+query.toString());
long begin = new Date().getTime();
TopDocs topDocs = searcher.search(query, 15);//15为检索出的个数
if(topDocs!=null){
ScoreDoc[] scoreDoc = topDocs.scoreDocs;
System.out.println("共搜索到("+topDocs.totalHits+")条匹配结果");
for(ScoreDoc sd : scoreDoc){
Document doc = searcher.doc(sd.doc);
System.out.println("标题:"+doc.get("title")+"  评分score:"+sd.score);
LuceneBeans lb = new LuceneBeans();
lb.setId(doc.get("id"));
lb.setTitle(doc.get("title"));
lb.setIntroduce(doc.get("introduce"));
lb.setAddtime(doc.get("addtime"));
lb.setCategory(doc.get("category"));
list.add(lb);
}
}
long end = new Date().getTime();
System.out.println("搜索完毕... ... 共用时:" + (end - begin) +"毫秒...");
} catch (IOException e) {
e.printStackTrace();
}
return list;
}

具体的每个方法和属性,就不一一讲解,毕竟挺别人说不一定全对,还是去查看官方文档更为准确。写个测试方法看看上面那段代码:

@Test
public void search(){
IndexUtil lu = new IndexUtil();
List<LuceneBeans> list = lu.searchIndex("小王子");
if(list!=null&&list.size()>0){
System.out.println("检索到的结果数据:");
for(LuceneBeans lb:list){
System.out.println("===================================");
System.out.println("id:"+lb.getId()+"===title:"+lb.getTitle());
}
}
}

嗯~夜已深,先分享到这边。之后陆续还会再分享关于lucene的相关博文,希望对您有所帮助。初出茅如,若文中有错误之处还望包涵指正。喜欢交流、喜欢分享~

初识Lucene 4.5全文搜索,布布扣,bubuko.com

时间: 2024-11-03 21:55:47

初识Lucene 4.5全文搜索的相关文章

记一次企业级爬虫系统升级改造(五):基于JieBaNet+Lucene.Net实现全文搜索

实现效果: 上一篇文章有附全文搜索结果的设计图,下面截一张开发完成上线后的实图: 基本风格是模仿的百度搜索结果,绿色的分页略显小清新. 目前已采集并创建索引的文章约3W多篇,索引文件不算太大,查询速度非常棒. 刀不磨要生锈,人不学要落后.每天都要学一些新东西. 基本技术介绍: 还记得上一次做全文搜索是在2013年,主要核心设计与代码均是当时的架构师写的,自己只能算是全程参与. 当时使用的是经典搭配:盘古分词+Lucene.net. 前几篇文章有说到,盘古分词已经很多年不更新了,我在Support

基于JieBaNet+Lucene.Net实现全文搜索

实现效果: 上一篇文章有附全文搜索结果的设计图,下面截一张开发完成上线后的实图: 基本风格是模仿的百度搜索结果,绿色的分页略显小清新. 目前已采集并创建索引的文章约3W多篇,索引文件不算太大,查询速度非常棒. 刀不磨要生锈,人不学要落后.每天都要学一些新东西. 基本技术介绍: 还记得上一次做全文搜索是在2013年,主要核心设计与代码均是当时的架构师写的,自己只能算是全程参与. 当时使用的是经典搭配:盘古分词+Lucene.net. 前几篇文章有说到,盘古分词已经很多年不更新了,我在Support

JavaScript全文搜索之相关度评分

全文搜索,与机器学习领域其他大多数问题不同,是一个 Web程序员在日常工作中经常遇到的问题.客户可能要求你在某个地方提供一个搜索框,然后你会写一个类似 WHERE title LIKE %:query% 的 SQL 语句实现搜索功能.一开始,这是没问题,直到有一天,客户找到你跟你说,"搜索出错啦!" 当然,实际上搜索并没有"出错",只是搜索的结果并不是客户想要的.一般的用户并不清楚如何做精确匹配,所以得到的搜索结果质量很差.为了解决问题,你决定使用全文搜索.经过一阵

初识Lucene 4.5.0 全文搜索--(二)

上一篇文章初识Lucene 4.5.0--(一)已经介绍了如何创建索引与检索索引.接下来就是删除与更新啦~ 一.删除索引. 原本3.x版本时 IndexWriter 与 IndexReader 都是有删除方法的,让我们先来看看lucene 3.6 api文档中的IndexReader的描述: 从4.0开始已经被删除了,所以现在只能用IndexWriter中的方法来进行删除.有哪些方法呢?继续看文档(lucene 4.5 api): 除了上面的六个外还有一个方法tryDeleteDocument(

Lucene全文搜索原理与使用

本文中主要是对于Lucene全文搜索的基础原理进行简单的分析,以及Lucene实现全文搜索的流程,之后就是Lucene在Java中的最简单使用:创建索引,查询索引库: 本文中使用的Lucene主要是4.10.3和6.0.0,两个版本的原理相同,但是API的使用并不相同: 1.结构化数据与非结构化数据 2.非结构化数据搜索 3.全文搜索 4.搜索如何实现 5.Lucene实现全文搜索流程 6.Lucene的API使用 1.结构化数据与非结构化数据 结构化数据:指具有固定格式或有限长度的数据,如数据

Apache Solr采用Java开发、基于Lucene的全文搜索服务器

http://docs.spring.io/spring-data/solr/ 首先介绍一下solr: Apache Solr (读音: SOLer) 是一个开源.高性能.采用Java开发.基于Lucene的全文搜索服务器,文档通过Http利用XML加到一个搜索集合中,查询该集合也是通过 http收到一个XML/JSON响应来实现.Solr 中存储的资源是以 Document 为对象进行存储的.每个文档由一系列的 Field 构成,每个 Field 表示资源的一个属性.Solr 中的每个 Doc

Lucene及全文搜索实现原理

Lucene及全文搜索实现原理 全文搜索 全文搜索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式.这个过程类似于通过字典中的检索字表查字的过程.全文搜索搜索引擎数据库中的数据. ????全文搜索的过程主要分为两个部分,索引的建立以及索引的搜索. 国内外的全文搜索常用的检索模型主要有向量模型,布尔模型等. 布尔模型 布尔模型是第一个信息检索的模型,可能也是最

全文搜索存储引擎 Elasticsearch 一点点

开始请大家想一个问题,如何统计一个Web站点的有效PV? 针对用户请求的URL,统计时做模式匹配-------->即用户真正去打开一个站点的有效页面并对每个页面的入口的访问做一个统计浏览量: 简要搜索引擎 搜索引擎在互联网上特别多有专业(Startpage,Google,Yahoo,Baidu)等也有非专业开源(北大搜索.任何基于Lucene库的二次开发搜索代理引擎)等:其重点都是用来做海量数据搜索存储.分析,并且根据用户指定的filter来过滤出用户所需要的数据.而背后所需基础组件无外乎是 索

MySQL 全文搜索支持, mysql 5.6.4支持Innodb的全文检索和类memcache的nosql支持

背景:搞个个人博客的全文搜索得用like啥的,现在mysql版本号已经大于5.6.4了也就支持了innodb的全文搜索了,刚查了下目前版本号都到MySQL Community Server 5.6.19 了,所以,一些小的应用可以用它做全文搜索了,像sphinx和Lucene这样偏重的.需要配置或开发的,节省了成本. 这儿有一个原创的Mysql全文搜索的文章, mysql的全文搜索功能:http://blog.csdn.net/bravekingzhang/article/details/672