Lucene 简介以及使用

Lucene, 一个基于 Java 的开源的全文搜索工具包,可以方便的嵌入到各种应用系统中,实现针对应用的全文索引以及检索功能。目前是 Apache  jakarta 项目组的一个子项目,它的目的是为程序员提供工具包,让程序员利用工具包里的强大接口来完成全文检索。下面我们将以 Lucene4.7 版本为例,为您详细讲解索引的创建、创建时的参数配置、Lucene4.7 版本的各种 query 查询、Lucene 神器 Luke 的使用等内容。

准备工作

本文需要的 jar 包:

lucene-analyzers-common-4.7.0.jar

lucene-core-4.7.0.jar

lucene-queryparser-4.7.0.jar

Lucene 相关问题可以参考Lucene 的官方 API

Lucene 常用包官方网站下载。

重要关键字介绍

IndexWriter:用于处理索引,如增加、更改或者删除索引。

FSDirectory:索引目录,除此之外还有一个 Ramdirectry。FSDirectory 是将索引创建到磁盘里,而 Ramdirectry 是将索引创建到内存里。

IndexWriterConfig:这里可配置版本号,分词器,打开模式等等,合理的应用该对象属性可以大大提高创建索引的性能。

Document:文档,我们将每个字段放在 document 里。

Field :域,类似数据库中的 column。

回页首

Lucene 实战

创建索引

首 先我们介绍下如何创建索引。相关步骤分为:建立索引器 IndexWriter,建立文档对象 Document,建立信息字段对象 Field,将 Field 添加到 Document,将 Document 添加到 IndexWriter 里面,最后不要忘记关闭 IndexWriter。

清单 1. 建立索引
package lucene;
……
public class IndexUtil {
private String[] idArr = {"1","2","3","4","5","6"};
private String[] emailArr = {"abc@us.ibm.com","ert@cn.ibm.com","lucy@us.ibm.com",
"rock@cn.ibm.com","test@126.com","deploy@163.com"};
private String[] contentArr = {
"welcome to Lucene,I am abc","This is ert,I am from China",
"I'm Lucy,I am english","I work in IBM",
"I am a tester","I like Lucene in action"
}; private String[] nameArr = {"abc","ert","lucy","rock","test","deploy"};
private Directory directory = null;
public void index() {
IndexWriter writer = null;
try {
directory = FSDirectory.open(new File("C:/lucene/index02"));
IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_47,
new StandardAnalyzer(Version.LUCENE_47));
conf.setOpenMode(OpenMode.CREATE_OR_APPEND);
LogMergePolicy mergePolicy = new LogDocMergePolicy();
mergePolicy.setMergeFactor(10);
mergePolicy.setMaxMergeDocs(10);
conf.setMaxBufferedDocs(10);
writer = new IndexWriter(directory, conf);
Document doc = null;
int date = 1;
for(int i=0;i<idArr.length;i++) {
doc = new Document();
doc.add(new StringField("id",idArr[i],Field.Store.YES));
doc.add(new StringField("email",emailArr[i],Field.Store.YES));
doc.add(new StringField("content",contentArr[i],Field.Store.YES));
doc.add(new StringField("name",nameArr[i],Field.Store.YES));
doc.add(new StringField("date","2014120"+date+“222222”,Field.Store.YES));
writer.addDocument(doc);
date++;
} //新的版本对 Field 进行了更改,StringField 索引但是不分词、StoreField 至存储不索引、TextField 索引并分词
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(writer!=null)writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} public static void main(String args[]){
IndexUtil indexUtil = new IndexUtil();
indexUtil.index();
}
}

参数解释:

SetMergeFactor(合并因子),是控制 segment 合并频率的,其决定了一个索引块中包括多少个文档,当硬盘上的索引块达到这个值时,将它们合并成一个较大的索引块。当 MergeFactor 值较大时,生成索引的速度较快。MergeFactor 的默认值是 10。

SetMaxMergeDocs 最大合并文档数,默认是 Integer.MAX_VALUE。设置 segment 最大合并文档 (Document) 数值较小越有利于追加索引的速度,值较大, 越适合批量建立索引和更快的搜索。

setMaxBufferedDocs 最大缓存文档数,是控制写入一个新的 segment 前内存中保存的 document 的数目,设置较大的数目可以加快建索引速度,默认为 10。

在创建创 IndexWriter 实例的时候应注意以下几个地方:

  1. 尽量保持 IndexWriter 在全局中只有一个实例,因为一个 directory 中只允许一个 IndexWriter 实例访问,如果两个或者两个以上的实例同时访问一个 directory 会出现 Lock obtain timed Out 异常, 在文件夹里会出现一个 write.lock 文件。
  2. 在配置 LogMergePolicy 的时候不要盲目的去设置,要根据物理机器的配置来进行次测试,来达到一个理想的配置

查询索引

当我们创建好索引后,就可以利用 Lucene 进行索引查询,Lucene 提供了多个查询功能,下面我们进行简单介绍。

Query :一个查询的抽象类,有多个子类实现,TermQuery, BooleanQuery, PrefixQuery ,WildcardQuery 等。

Term :是搜索的基本单位,一个 Term 是由两个 String 的 field 组成。比如,Term("name",“rock”), 此时该语句是查询 name 为 rock 的条件。

IndexSearcher:当索引建立好后,用该对象进行查询。该对象只能以只读的方式打开索引,所以多个 IndexSearcher 对象可以查询一个索引目录。我们要注意一下这个现象。

在介绍几种查询方式之前,首先要初始化 directory:

directory = FSDirectory.open(new File("C:/lucene/index02"));

其次获取 IndexSearcher:

public IndexSearcher getSearcher() {
IndexReader reader = null;
try {
reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
return searcher;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
清单 2. 使用 TermQuery 搜索
	public void searchByTerm(String field, String name, int num) {
try {
IndexSearcher searcher = getSearcher();
Query query = new TermQuery(new Term(field, name));
TopDocs tds = searcher.search(query, num);
System.out.println("count:" + tds.totalHits);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

结果:

count:1

docId:4

name:rock

email:rock@cn.ibm.com

date:2014-12-4

说明:

TermQuery 是 Lucene 查询中最基本的一种查询,它只能针对一个字段进行查询。

清单 3. 范围查询 RangeQuery (搜索指定范围的数据)
public void searchByTermRange(String field,String start,String end,int num) {
try {
IndexSearcher searcher = getSearcher();
BytesRef lowerTerm = new BytesRef(start);
BytesRef upperTerm = new BytesRef(end);
Query query = new TermRangeQuery(field,lowerTerm,upperTerm,true, true);
TopDocs tds = searcher.search(query, num);
System.out.println("count:"+tds.totalHits);
for(ScoreDoc sd:tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

结果:

count:3

docId:1

name:abc

email:abc@us.ibm.com

date:20141201222222

docId:2

name:ert

email:ert@cn.ibm.com

date:20141202222222

docId:3

name:lucy

email:lucy@us.ibm.com

date:20141203222222

说明:

TermRangeQuery query=new TermRangeQuery(字段名, 起始值, 终止值, 起始值是否包含边界, 终止值是否包含边界)。

清单 4.PrefixQuery 前缀查询
//查询以 ro 开头的 name
public void searchByPrefix(String field, String value, int num) {
try {
IndexSearcher searcher = getSearcher();
Query query = new PrefixQuery(new Term(field, value));
TopDocs tds = searcher.search(query, num);
System.out.println("count:" + tds.totalHits);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

结果:

count:1

docId:4

name:rock

email:rock@cn.ibm.com

date:20141204222222

说明:

前缀查询, 搜索匹配开始位置的数据类似百度的输入框。

清单 5.WildcardQuery 通配符查询
//查询 email 是 test 的
public void searchByWildcard(String field, String value, int num) {
try {
IndexSearcher searcher = getSearcher();
Query query = new WildcardQuery(new Term(field, value));
TopDocs tds = searcher.search(query, num);
System.out.println("count" + tds.totalHits);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

结果:

count1

docId:5

name:test

email:test@126.com

date:20141205222222

说明:

通配符分为两种,“*”和“?”,“*”表示任何字符,“?”表示任意一个字符。

Term term=new Term(字段名, 搜索关键字+通配符)。

清单 6.FuzzyQuery 模糊搜索
public void searchByFuzzy(int num) {
try {
IndexSearcher searcher = getSearcher();
FuzzyQuery query = new FuzzyQuery(new Term("name","acc"),1,1);
//System.out.println(query.getPrefixLength());
TopDocs tds = searcher.search(query, num);
System.out.println("count:"+tds.totalHits);
for(ScoreDoc sd:tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

结果:

count:1

docId:1

name:abc

email:abc@us.ibm.com

date:20141201222222

说明

FuzzyQuery(new Term("name","acc"),1,1),需要 3 个参数,第一个参数是词条对象,第二个参数是 levenshtein 算法的最小相似度,第三个参数是指与多少个前缀字符匹配。

清单 7.BooleanQuery 查询
public void searchByBoolean(int num) {
try {
IndexSearcher searcher = getSearcher();
BooleanQuery query = new BooleanQuery();
query.add(new TermQuery(new Term("name", "abc")),BooleanClause.Occur.SHOULD);
query.add(new TermQuery(new Term("email","lucy@us.ibm.com")), BooleanClause.Occur.SHOULD);
TopDocs tds = searcher.search(query, num);
System.out.println("count" + tds.totalHits);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id"));
System.out.println("name:"+doc.get("name"));
System.out.println("email:"+doc.get("email"));
System.out.println("date:"+doc.get("date"));
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
结果:

count2

docId:1

name:abc

email:abc@us.ibm.com

date:20141201222222

docId:3

name:lucy

email:lucy@us.ibm.com

date:20141203222222

说明:

BooleanQuery,也就是组合查询,允许进行逻辑 AND、OR 或 NOT 的组合,通过 BooleanQuery 的 add 方法将一个查询子句增加到某个 BooleanQuery 对象中。

BooleanClause.Occur.MUST:必须包含,相当于逻辑运算的与

BooleanClause.Occur.MUST_NOT:必须不包含,相当于逻辑运算的非

BooleanClause.Occur.SHOULD:可以包含,相当于逻辑运算的或

清单 8. 分页查询
	private static void testPageSearch1(int currentPage) {
int PAGE_SIZE = 10;
IndexReader reader = null;
try {
reader = DirectoryReader.open(FSDirectory.open(new File("")));
IndexSearcher searcher = new IndexSearcher(reader);
Query query = new TermQuery(new Term("name", "rock"));
TopDocs topDocs = searcher.search(query, currentPage * PAGE_SIZE);
ScoreDoc[] hits = topDocs.scoreDocs;
int endNuM = Math.min(topDocs.totalHits, currentPage * PAGE_SIZE); for (int i = (currentPage - 1) * PAGE_SIZE; i < endNuM; i++) {
Document doc = searcher.doc(hits[i].doc);
System.out.print(doc.get("USERNAME"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
//在 Lucene 的 3.5 以后的版本,Lucene 的 API 里提供了一个分页方法 searchafter。
private ScoreDoc getLastScoreDoc(int pageIndex, int pageSize, Query query,
IndexSearcher searcher) throws IOException {
if (pageIndex == 1)
return null;
int num = pageSize * (pageIndex - 1);
TopDocs tds = searcher.search(query, num);
return tds.scoreDocs[num - 1];
} public void searchPageByAfter(String query, int pageIndex, int pageSize) {
try {
IndexSearcher searcher = getSearcher();
QueryParser parser = new QueryParser(Version.LUCENE_47,
"content",new StandardAnalyzer(Version.LUCENE_47));
Query q = parser.parse(query);
ScoreDoc lastSd = getLastScoreDoc(pageIndex, pageSize, q, searcher);
TopDocs tds = searcher.searchAfter(lastSd, q, pageSize);
for (ScoreDoc sd : tds.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println("docId:"+doc.get("id") + ",name:" +
doc.get("name")+",email:"+ doc.get("email") );
}
} catch (IOException e) {
e.printStackTrace();
} catch (org.apache.lucene.queryparser.classic.ParseException e) {
e.printStackTrace();
}
}

排序

Lucene 除了提供大量的查询功能外,还提供了一个可改变查询结果顺序的类 Sort,用户可根据自己的需求进行 Sort 排序设置。

	Sort sort = new Sort();
ortField sf=new SortField("name",Type.STRING_VAL, false);

以 上语句表示根据 name 进行排序,false 代表升序,如果是 true 代表降序,可以有多个 SortField,利用 Sort 的 sort.setSort(sf,sf1...) 将每个 SortField 添加到 sort 中,最后返回按 sort 进行排序的搜索结果。

TopDocs topDocs = searcher.search(query, currentPage * PAGE_SIZE,sort);

Lucene4.X 高级应用的更多相关文章

  1. lucene中文学习地址推荐

    Lucene原理与代码分析http://www.cnblogs.com/forfuture1978/category/300665.html Lucene5.5学习(1)-初尝Lucene全文检索引擎 ...

  2. 【课程分享】基于Lucene4.6+Solr4.6+Heritrix1.14+S2SH实战开发从无到有垂直搜索引擎

    对这个课程有兴趣的朋友,能够加我的QQ2059055336和我联系,能够和您分享.  课程介绍:最有前途的软件开发技术--搜索引擎技术  搜索引擎作为互联网发展中至关重要的一种应用,已经成为互联网各个 ...

  3. 【转】lucene4.3.0 配置与调试

    lucene4.3.0 配置与调试 demo lucene的最新版本是4.3.0, http://www.apache.org/dyn/closer.cgi/lucene/java/4.3.0 luc ...

  4. MySQL高级知识- MySQL的架构介绍

    [TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...

  5. PayPal高级工程总监:读完这100篇论文 就能成大数据高手(附论文下载)

    100 open source Big Data architecture papers for data professionals. 读完这100篇论文 就能成大数据高手 作者 白宁超 2016年 ...

  6. 马哥linux运维初级+中级+高级 视频教程 教学视频 全套下载(近50G)

    马哥linux运维初级+中级+高级 视频教程 教学视频 全套下载(近50G)目录详情:18_02_ssl协议.openssl及创建私有CA18_03_OpenSSH服务及其相关应用09_01_磁盘及文 ...

  7. JS高级前端开发群加群说明及如何晋级

    JS高级前端开发群加群说明 一.文章背景: 二. 高级群: 三. 加入方式: 四. 说明:   一.文章背景: 去年年初建了几个群,在不经意间火了,一直排在“前端开发”关键字搜索结果第一名.当然取得这 ...

  8. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

  9. Visual Studio 宏的高级用法

    因为自 Visual Studio 2012 开始,微软已经取消了对宏的支持,所以本篇文章所述内容只适用于 Visual Studio 2010 或更早期版本的 VS. 在上一篇中,我已经介绍了如何编 ...

随机推荐

  1. Openjudge-计算概论(A)-晶晶赴约会

    描述 晶晶的朋友贝贝约晶晶下周一起去看展览,但晶晶每周的1.3.5有课必须上课,请帮晶晶判断她能否接受贝贝的邀请,如果能输出YES:如果不能则输出NO. 输入输入有一行,贝贝邀请晶晶去看展览的日期,用 ...

  2. 【开发笔记】java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

    在进行"spring的声明式事务管理配置"的时候,抛出该异常. 错误原因: 缺少aopalliance.jar包. 事务管理配置如下: <!-- #######5.sprin ...

  3. windows service and process 的关系

    1.查询windows 对应的服务 sc query > 1.txt notepad 1.txt 2.查询对应的进程信息 tasklist /m > tasks.txt notepad t ...

  4. js预编译

    先来做三个测试 eg1: var a; a = 1; function a() {}; console.log(a); eg2: var a; function a() {}; console.log ...

  5. WPF类层次结构

    WPF类层次结构 System.Threading.DispatcherObject类 WPF应用程序使用STA(Single Thread Affinity)模型,整个用户界面由一个单独的线程拥有, ...

  6. 关于perl闭包(个人理解)

    我个人理解,就是当一个变量超出作用域时,应是消失了,不见了的,但你还能访问它,这就是闭包. # #看下面的例子. #!/usr/bin/env perl -w use strict; { my $va ...

  7. touchesBegan: withEvent: <--- with UIScrollView / UIImageView

    touchesBegan: withEvent: / touchesMoved: withEvent: / touchesEnded: withEvent: 等只能被UIView捕获(如有问题请指出对 ...

  8. windows禅道环境搭建

    zentao官网的几个网址 http://www.zentao.net/ http://www.zentao.net/article-view-79863.html   搭建环境需要下载两个文件 1) ...

  9. 第一次点击button, view视图出现;第二次点击button,view视图消失

    主要思想:点击一下按钮选中Yes,View出现,再点击一下选中为No  view消失

  10. SCU 1069 POJ 2955 Brackets

    区间DP #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> ...