`

APACHE Lucene 的使用

 
阅读更多

用了下Lucene的全文搜索,这次的使用没多大复杂度,只是针对数据库的检索,先看下理论的东西,加固下自己的理论思想(这写理论也是看别人文档来的,自己领悟不了这么专业的词汇.....)。

1 什么是全文搜索。

全文搜索(full-text-Retrieval)是以文本为检索对象,找出含有指定词汇的文本。全面、准确、和快速是衡量全文搜索系统的关键指标。在信息检索工具中,全文搜索最通用性和实用性的。

2 全文搜索不同以数据库搜索

全文搜索不同与数据库查询。数据库的查询like 模糊查询是不一样的,如果使用like查询会先如下问题:

1.0 匹配效果:如搜索ant会搜索出planting。这样会搜索出很多无关的信息。

2.0相关度排序:查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果。

3.0 查询速度:全文检索的速度比sql like查询快。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页。

3.Lucene 的工作原理:

 

看着Lucene的API,看得出来索引库是个核心区域。

 

索引库:

索引库是一个目录,里面是一些二进制文件,就如同数据库,所有的数据也是以文件的形式存在文件系统中的。我们不能直接操作这些二进制文件,而是使用Lucene提供的API完成相应的操作,就像操作数据库应使用SQL语句一样。

对索引库的操作可以分为两种:管理与查询。管理索引库使用IndexWriter,从索引库中查询使用IndexSearcherLucene的数据结构为DocumentFieldDocument代表一条数据,Field代表数据中的一个属性。一个Document中有多个FieldField的值为String型,因为Lucene只处理文本。

我们只需要把在我们的程序中的对象转成Document,就可以交给Lucene管理了,搜索的结果中的数据列表也是Document

 

配置Lucene的工作环境:我用的3.0.x版本

 

要加入的jar包有:

llucene-core-3.0.1.jar(核心包)

lcontrib\analyzers\common\lucene-analyzers-3.0.1.jar(分词器)

lcontrib\highlighter\lucene-highlighter-3.0.1.jar(高亮)

lcontrib\memory\lucene-memory-3.0.1.jar(高亮)

lIKAnalyzer3.2.3.jar(中文分解词,这jar不在Lucene里面,需要去额外下载)

4.开始Hello world.

首先,我需要把Lucene所需要的基本功能给抽象出来,我们看Lucene的API,流程图可以看出我们都是针对索引库进行操作,需要用到IndexWrite的CRUD 写入、更新、删除索引库和IndexSearcher来搜索索引库的内容。

我们需要一个接口来描述这些基本方法:

/***
 * Lucene 索引基本操作接口
 * @author share
 *
 * @param <E>
 */
public interface IndexService<E> {
 void save(E entity);//添加
 void delete(Long id);//删除
 void update(E entity);//更新,针对打数据的操作,可以能不进行更新操作,直接delete在save记录。
 Page<E> search(Page<E> page,String queryString);//按条件分页查询搜索
 
}

 

贴Page对象的基本方法:

public class Page<T> {
 //-- 公共变量 --//
 public static final String ASC = "asc";
 public static final String DESC = "desc";
 
//-- 分页参数 --//
 protected int pageNo = 1;
 protected int pageSize = 20;
 protected boolean autoCount = true;
 
//-- 返回结果 --//
 protected List<T> result = new ArrayList<T>();
 protected long totalCount = 0;
 protected int totalPages = 0;
 protected int searchFlag=0;

 
//省略getter 和setter方法
 
}

 

设计一个LuceneUtils类

 

/** 
* 使用 IndexWriter 进行保存或更新操作时, 
* 若不手动调用 IndexWriter 的 close 方法,数据并不会持久化到索引库中。 
* IndexWriter 一般只需要在程序退出的时候再关闭。 
* 因此,需要调用它的 commit 方法手动提交。需要特别注意。 
*/ 
public class LuceneUtils { 
private static IndexWriter indexWriter; 
public static String path = ServletActionContext.getServletContext().getRealPath("/")+"Index";
 static { 
try { 
Directory directory = FSDirectory.open(new File(path));
 //使用IKAnalyzer 词典分词器
Analyzer analyzer = new IKAnalyzer();
 indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED); 
} catch (Exception e) { 
throw new RuntimeException(e); 
}
 } 

/** 
* 获取 IndexWriter 
*/ 
public static IndexWriter getIndexWriter() { 
return indexWriter; 
} 

/** 
* 关闭 IndexWriter 
*/ 
public static void closeIndexWriter() { 
try { 
indexWriter.close(); 
} catch (Exception e) { 
throw new RuntimeException(e); 
} 
}

} 


 

然后我们需要去实现那个LuceneService泛型的基本方法的实现类:(这里使用的不是Lucene分解词,用的是一个中文分词器IKAnalyzer)

 

@Service("luceneService")
@Transactional
public class LuceneServiceImpl implements IndexService<NewsContent> {
 
/***
 * 为每一条新闻记录建立一个索引
* @param col
 */
 @Transactional(readOnly=true)
 public Page<NewsContent> search(Page<NewsContent> page, String queryString){
 if(queryString == null || queryString.equals("")){
 return page;
 }
 String path = ServletActionContext.getServletContext().getRealPath("/")+"Index";
 List<NewsContent> newsList = new ArrayList<NewsContent>();
 Directory dirPath = null;
 IndexReader reader = null;
 IndexSearcher searcher = null;
 int firstResult = (page.getPageNo()-1)*page.getPageSize();
 int maxResult = page.getPageSize();
 //查询字段
try {
 dirPath = FSDirectory.open(new File(path));
 reader = IndexReader.open(dirPath);
 searcher = new IndexSearcher(reader);
 //在索引中使用IKSimilarity相似度评估器
searcher.setSimilarity(new IKSimilarity());
 //使用:search(Query query , Filter filter , int n , Sort sort)
 //搜索的字段
String[] fields = { "title", "content" };
 IKQueryParser parser = new IKQueryParser(); 
Query query = parser.parseMultiField(fields,queryString);
 //sort 
// 按降序排列
SortField sf = new SortField("id", SortField.LONG,true); 
Sort sort = new Sort(sf); 
TopDocs tds = searcher.search(query, null, firstResult + maxResult , sort);
 int totalCount = tds.totalHits; 
ScoreDoc[] sd = tds.scoreDocs;
 /* 保证循环的次数不超过 scoreDocs 的长度*/ 
int length = Math.min(firstResult+maxResult, sd.length); 
// 一、创建并配置高亮器 
Formatter formater = new SimpleHTMLFormatter("<font color='red'>", "</font>"); // 高亮效果,默认为<B>与</B> 
Scorer scorer = new QueryScorer(query); // 查询条件 
Highlighter highlighter = new Highlighter(formater, scorer); 
highlighter.setTextFragmenter(new SimpleFragmenter(20)); // 摘要的大小,默认为100个字符 

//遍历查询出来的文档转换成 新闻记录集合
for (int i = firstResult; i < length; i++) {
 NewsContent newsContent = new NewsContent();
 Document docId = reader.document(sd[i].doc);
 newsContent.setId(Long.valueOf(docId.get("id")));
 // 返回高亮后的(关键词出现次数最多的地方的)一段文本,如果当前高亮的属性中没有出搜索的关键词,则返回null 
String text = highlighter.getBestFragment(getAnalyzer(), "title", docId.get("title")); 
if (text != null) { 
docId.getField("title").setValue(text); // 使用高亮后的文本替换原始内容 
}
newsContent.setTitle(docId.get("title"));

 String dateStr = docId.get("addTime");
 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
 Date date = sdf.parse(dateStr);
 newsContent.setAddTime(date);
 newsContent.setClickNum(Long.valueOf(docId.get("clickNum")));

 NewsType newsType = new NewsType();
 newsType.setId(Long.valueOf(docId.get("newsTypeId")));
 newsContent.setNewsType(newsType);
 newsList.add(newsContent);
 }
 page.setResult(newsList);
 //计算总页数
page.setTotalCount(totalCount);
 } catch (Exception e) {
 // TODO: handle exception
 e.printStackTrace();
 }finally{
 try {
 if(dirPath!=null)
 dirPath.close();
 if(reader!=null)
 reader.close();
 if(searcher!=null)
 searcher.close();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 return page;
 }
 }
 
/***
 * 为一条新闻记录建立一个索引
* @param news
 */
 @Transactional(readOnly=true)
 public void save(NewsContent news) {
 IndexWriter writer = LuceneUtils.getIndexWriter();
 try {
 Document doc = createDocument(news);
 writer.addDocument(doc);
 } catch (Exception e) {
 // TODO: handle exception
 System.out.println("创建索引库失败!");
e.printStackTrace();
 }finally{
 try {
 writer.optimize();
 writer.commit();
 //LuceneUtils.closeIndexWriter();
 } catch (CorruptIndexException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 
/***
 * 删除包含此词的文档
* @param col
 */
 @Transactional(readOnly=true)
 public void delete(Long newsId){
 IndexWriter writer = LuceneUtils.getIndexWriter();
 try {
 Term term = new Term("id",newsId.toString()+ ""); 
writer.deleteDocuments(term);
 } catch (Exception e) {
 // TODO: handle exception
 e.printStackTrace();
 }finally{
 try {
 writer.commit();
 //LuceneUtils.closeIndexWriter();
 } catch (CorruptIndexException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 
/***
 * 删除包含此词的文档
* @param col
 */
 @Transactional(readOnly=true)
 public void update(NewsContent news){
 IndexWriter writer = LuceneUtils.getIndexWriter();
 try {
 Document doc = createDocument(news);
 Term term = new Term("id",news.getId().toString()+ "");
 writer.updateDocument(term, doc);
 } catch (Exception e) {
 // TODO: handle exception
 e.printStackTrace();
 }finally{
 try {
 writer.optimize();
 writer.commit();
 //LuceneUtils.closeIndexWriter();
 } catch (CorruptIndexException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 
/**
 * 产生文档文件
* @param news
 * @return
 */
 @Transactional(readOnly=true)
 private Document createDocument(NewsContent news) {
 Document doc = new Document();
 doc.add(new Field("id", news.getId().toString(), Field.Store.YES, Index.ANALYZED));
 doc.add(new Field("title", news.getTitle(), Field.Store.YES, Index.ANALYZED));
 doc.add(new Field("content", news.getContent(), Field.Store.YES, Index.ANALYZED));
 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
 sdf.format(news.getAddTime());
 doc.add(new Field("addTime", sdf.format(news.getAddTime()), Field.Store.YES, Index.ANALYZED));
 doc.add(new Field("newsTypeId", news.getNewsType().getId().toString(), Field.Store.YES, Index.ANALYZED));
 doc.add(new Field("clickNum", news.getClickNum().toString(), Field.Store.YES, Index.ANALYZED));
 return doc;
 }
 

/****
 * 获取文档分析器
* @return
 */
 @Transactional(readOnly=true)
 public static Analyzer getAnalyzer(){
 return new IKAnalyzer();
 }
}

 

这些基本的查找都写好,就差需要的时候调用了。

 

5.说下Lucene另一个核心分解词:

中文分词器

中文的分词比较复杂,因为不是一个字就是一个词,而且一个词在另外一个地方就可能不是一个词,如在帽子和服装中,和服就不是一个词。对于中文分词,通常有三种方式:单字分词、二分法分词、词典分词。

l单字分词:就是按照中文一个字一个字地进行分词。如:我们是中国人
效果:。(StandardAnalyzer就是这样)。

l二分法分词:按两个字进行切分。如:我们是中国人,效果:我们们是是中中国国人。(CJKAnalyzer就是这样)。

l词库分词:按某种算法构造词,然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语。通常词库分词被认为是最理想的中文分词算法。如:我们是中国人,效果为:我们中国人。(使用极易分词的MMAnalyzer。可以使用极易分词,或者是庖丁分词分词器、IKAnalyzer)。

 

 

其他的中文分词器有:

1,极易分词:MMAnalyzer,最后版本是1.5.3,更新时间是2007-12-05,不支持Lucene3.0

2,庖丁分词:PaodingAnalzyer,最后版本是2.0.4,更新时间是2008-06-03,不支持Lucene3.0

 

 

中文分词器使用IKAnalyzer,主页:http://www.oschina.net/p/ikanalyzer

实现了以词典为基础的正反向全切分,以及正反向最大匹配切分两种方法。IKAnalyzer是第三方实现的分词器,继承自LuceneAnalyzer类,针对中文文本进行处理。具体的使用方式参见其文档。

注意:扩展的词库与停止词文件要是UTF-8的编码,并且在要文件头部加一空行。

分享到:
评论

相关推荐

    apache Lucene4.7最全最新的jar包

    7. **内存管理**:Lucene 4.7对内存使用进行了优化,减少了在索引构建和搜索过程中的内存消耗,降低了对硬件的要求。 8. **分布式搜索**:通过Solr等扩展,Lucene支持分布式搜索,可以处理大规模的数据集并提供高...

    apache下的lucene教程

    ### Apache Lucene教程知识点概述 #### 一、Apache Lucene简介 - **Lucene定义**:Lucene是一款高性能、全功能的文本搜索引擎库,由Java编写而成,是Apache基金会下的一个开源项目。 - **发展历程**:自1999年由...

    Apache Lucene.Net 2.4.0 API Documentation.chm

    Apache Lucene.Net 2.4.0 API

    Apache Lucene全文检索和IKAnalyzer分词工具类

    import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org...

    Apache Lucene-4.10.3源码

    Apache Lucene是一个开源的全文搜索引擎库,被广泛用于构建高效、可扩展的搜索功能。它在Java编程语言中实现,但提供了多种语言的接口,包括Python、PHP、C#等,使得开发人员能够轻松地在各种项目中集成高级搜索功能...

    apache lucene(JAVA检索引擎工具包) v8.8.2 最新版

    apache lucene中文版是一款非常好用的检索工具包,使用方便,功能强大,有需要的朋友不要错过了,而且是完全开放的,你可以自由进行使用,可以支持多种检索功能。兼容多个操作系统,下载后可以直接使用,没有任何...

    apache-lucene-snowball.jar

    jar包,亲测可用

    Lucene 使用正则表达式

    ### Lucene 使用正则表达式 #### 知识点概览 1. **Lucene简介** 2. **正则表达式(regex)在Lucene中的应用** 3. **regexQuery详解** 4. **示例代码解析** 5. **索引创建与查询流程** 6. **正则表达式的语法** #### ...

    cpp-Rucene是ApacheLucene项目的一个Rust移植

    Apache Lucene是一个广泛使用的开源Java库,专门用于构建高级文本检索功能,而Rucene则将这些功能带入了Rust的世界,一个以安全和性能著称的系统级编程语言。 首先,我们来深入了解一下Rucene的核心特性。Rucene的...

    lucene org.apache

    org.apache.lucene.analysis.cjk.CJKAnalyzer .......

    lucene-core-7.7.0-API文档-中文版.zip

    Maven坐标:org.apache.lucene:lucene-core:7.7.0; 标签:apache、lucene、core、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档...

    Apache Lucene3.0 入门实例介绍

    Lucene使用TF-IDF算法计算文档与查询的相关性,生成一个分数,用于排序搜索结果。 9. **高级特性**: - **Faceted Search**:允许用户按类别过滤和浏览结果。 - **Highlighting**:高亮显示搜索结果中的匹配部分...

    Lucene.Net

    Apache Lucene.Net is a C# full-text search engine. Apache Lucene.Net is not a complete application, but rather a code library and API that can easily be used to add search capabilities to applications...

    apache lucene solr 官网历史版本 免费下载地址

    http://archive.apache.org/dist/lucene/java/ 这个是lucene的历史版本 http://archive.apache.org/dist/lucene/solr/ 这个是solr的历史版本

    apache-lucene-analyzers.jar

    使用"apache-lucene-analyzers.jar"时,开发者需要根据实际应用的需要选择合适的分析器,以确保文本能够被正确地索引和搜索。在Java程序中,可以通过导入相关的类并实例化分析器来实现这一功能。 总的来说,"apache...

    lucenenet:Apache Lucene.NET

    Apache Lucene.NET.NET的全文本搜索Apache Lucene.NET是.NET全文搜索引擎框架,是流行的Apache Lucene项目的C#端口。 Apache Lucene.NET并不是一个完整的应用程序,而是可以轻松用于向应用程序添加搜索功能的代码库...

    apache lucene 4.10.0入门单元测试代码demo

    总结了一些实用的demo 包括: 1.建立索引 2.通过IKAnalyzer搜索中文关键词 3.复杂的多字段搜索 4.多线程并发搜索,通过contiperf测试,详见:...lucene支持多线程并发搜索和建索引,只要IndexWriter是单例模式即可

    Lucene使用

    在使用2012版时异常:ClassNotFoundException: org.apache.lucene.analysis.tokenattributes.CharTermAttribute 庖丁分词 使用 paoding-analysis-2.0.4-beta.zip 版时异常 Exception in thread "main" java.lang....

    java开源系统源码-apache-lucene-beginning:“ApacheLucene开始:Java/开源/全文搜索系统的构建”的代

    java源系统apache-lucene开头 “ Apache Lucene的开始:Java /开源/全文搜索系统的构建”的代码kata。 这是关口浩二(Koji Sekiguchi)的“ Apache Lucene简介:Java,开放源代码和全文本搜索系统的构建”(2006年...

    lucene:Apache Lucene开源搜索软件

    阿帕奇·卢森(Apache Lucene) Apache Lucene是用Java编写的高性能,功能齐全的文本搜索... Lucene使用进行构建控制。 注意:从9.0版开始,Lucene从Ant更改为Gradle。 以前的版本仍使用Ant。 步骤1)结帐/下载Lucene

Global site tag (gtag.js) - Google Analytics