lucene全文检索mysql教程_全文检索Lucene(一)---快速入门详解

全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程。

对于搜索,按被搜索的资源类型,分为两种:可以分为文本类型和多媒体类型。

全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。

关于全文检索,我们要知道:

1,只处理文本。

2,不处理语义。

3,搜索时英文不区分大小写。

4,结果列表有相关度排序。

在信息检索工具中,全文检索是最具通用性和实用性的。

全文检索与数据库搜索

全文检索不同于数据库的SQL查询。(他们所解决的问题不一样,解决的方案也不一样,所以不应进行对比)。在数据库中的搜索就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。

这样会有如下问题:

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

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

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

Lucene简介

全文检索就如同ORM,是一个概念。ORM的框架有很多种:Hibernate、TopLink、iBatis等,我们之前学习的是Hibernate。同样的,全文检索领域中也有多种框架,Lucene就是其中的一个用开源的全文检索框架。

Lucene的主页为:http://lucene.apache.org/

如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。所以要先把要检索的资源集合放到本地,并使用某种特定的结构存储,称为索引,这个索引的集合称为索引库。由于索引库的结构是按照专门为快速查询设计的,所以查询的速度非常快。我们每次搜索都是在本地的索引库中进行。对于全文检索功能的开发,我们要做的有两个方面:索引库管理(维护索引库中的数据)、在索引库中进行搜索。而Lucene就是操作索引库的工具。

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

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

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

我们需要对文档进行预处理,建立一种便于检索的数据结构,以此来提高信息检索的速度,这种数据结构就是索引。目前广泛使用的一种索引方式是倒排序索引 。

倒排序索引的原理就如同查字典。要先查目录,得到数据对应的页码,在直接翻到指定的页码。不是在文章中找词,而是从目录中找词所在的文章。这需要在索引库中生成一个词汇表(目录),在词汇表中的每一个条记录都是类似于“词所在文档的编号列表”的结构,记录了每一个出现过的单词,和单词出现的地方(哪些文档)。查询时先查词汇表,得到文档的编号,再直接取出相应的文档。

把数据转成指定格式放到索引库中的操作叫做建立索引。建立索引时,在把数据存到索引库后,再更新词汇表。进行搜索时,先从检索词汇表开始,然后找到相对应的文档。如果查询中仅包含一个关键词,则在词汇表中找到该单词,并取出他对应的文档就可以了。如果查询中包含多个关键词,则需要将各个单词检索出的记录进行合并再取出相应的文档记录。

如果词汇表中有一个词“德玛西亚”对应的文档编号列表为“1”。现在又有添加了一个包含“德玛西亚”的文档,则词汇表中的“德玛西亚”词后对应的编号列表变成了“1,2”。因为关键词的数量受实际语言的限制,所以不用担心词汇表会变的很大。

维护倒排索引有三个操作:添加、删除和更新文档。但是更新操作需要较高的代价。因为文档修改后(即使是很小的修改),就可能会造成文档中的很多的关键词的位置都发生了变化,这就需要频繁的读取和修改记录,这种代价是相当高的。因此,一般不进行真正的更新操作,而是使用“先删除,再创建”的方式代替更新操作。

Lucene的程序开发

1,jar包下载

登陆http://lucene.apache.org/找到你所需要的包进行下载。

目前最新的版本到6.x本,个人之前有个3.x版本就没有从新下载。

下载后解压的目录图

e8a810adfbf73cce9f464d793b202c70.png

搭建Lucene的开发环境需要的最少Jar包:

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

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

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

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

建立索引的执行过程

1,我们做的操作:把数据对象转成相应的Document,其中的属性转为Field。

2,我们做的操作:调用工具IndexWriter的addDocument(doc),把Document添加到索引库中。

3,Lucene做的操作:把文档存到索引库中,并自动指定一个内部编号,用来唯一标识这条数据。内部编号类似于这条数据的地址,在索引库内部的数据进行调整后,这个编号就可能会改变,同时词汇表中引用的编号也会做相应改变,以保证正确。但我们如果在外面引用了这个编号,前后两次去取,得到的可能不是同一个文档!所以内部编号最好只在内部用。

4,Lucene做的操作:更新词汇表。把文本中的词找出并放到词汇表中,建立与文档的对应关系。要把哪些词放到词汇表中呢,也就是文本中包含哪些词呢?这就用到了一个叫做Analyzer(分词器)的工具。他的作用是把一段文本中的词按规则取出所包含的所有词。对应的是Analyzer类,这是一个抽象类,切分词的具体规则是由子类实现的,所以对于不同的语言(规则),要用不同的分词器。

从索引库中搜索的执行过程

1, 把要查询字符串转为Query对象。这就像在Hibernate中使用HQL查询时,也要先调用Session.createQuery(hql)转成Hibernate的Query对象一样。把查询字符串转换成Query是使用QueryParser,或使用MultiFieldQueryParser。查询字符串也要先经过Analyzer(分词器)。要求搜索时使用的Analyzer要与建立索引时使用的Analzyer要一致,否则可能搜不出正确的结果。

2, 调用IndexSearcher.search(),进行查询,得到结果。此方法返回值为TopDocs,是包含结果的多个信息的一个对象。其中有totalHits 代表决记录数,ScoreDoc的数组。ScoreDoc是代表一个结果的相关度得分与文档编号等信息的对象。

3, 取出要用到的数据列表。调用IndexSearcher.doc(scoreDoc.doc)以取出指定编号对应的Document数据。在分页时要用到:一次只取一页的数据。

代码示例:

Article.java

package com.my.bean;

public class Article {

private Integer id;

private String title;

private String content;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

}

HelloWord.java

package com.my.lucene;

import java.io.File;

import java.util.ArrayList;

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.Field.Index;

import org.apache.lucene.document.Field.Store;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriter.MaxFieldLength;

import org.apache.lucene.queryParser.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 org.junit.Test;

import com.my.bean.Article;

public class HelloWord {

// 建立索引(模拟在贴吧中发表了一个文章,会保存到数据库中,并且应该建立索引,以便能搜索到)

@Test

public void createIndex() throws Exception {

// 模拟一条刚保存到数据库中的数据

Article article = new Article();

article.setId(1);

article.setTitle("Lucene是全文检索的框架");

article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。");

// 建立索引 ?

// 1,把Article转成Document

Document doc = new Document();

doc.add(new Field("id", article.getId().toString(), Store.YES,Index.ANALYZED));

doc.add(new Field("title", article.getTitle(), Store.YES,Index.ANALYZED));

doc.add(new Field("content", article.getContent(), Store.YES,Index.ANALYZED));

// 2,建立索引

Directory directory = FSDirectory.open(new File("./indexDir/")); // 索引库文件所在的目录

Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);

IndexWriter indexWriter = new IndexWriter(directory, analyzer,new MaxFieldLength(10000)); // MaxFieldLength.LIMITED

indexWriter.addDocument(doc);

indexWriter.close();

}

// 搜索

@Test

public void search() throws Exception {

// 搜索条件

String queryString = "lucene";

// String queryString = "compass";

Directory directory = FSDirectory.open(new File("./indexDir/")); // 索引库文件所在的目录

Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);

// 1,把查询字符串转为Query对象

QueryParser queryParser = new QueryParser(Version.LUCENE_30, "title",analyzer); // 只在title中查询

Query query = queryParser.parse(queryString);

// 2,查询,得到中间结果

IndexSearcher indexSearcher = new IndexSearcher(directory);

TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束

int count = topDocs.totalHits; // 总结果数

ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息

// 3,处理结果

List list = new ArrayList();

for (int i = 0; i < scoreDocs.length; i++) {

ScoreDoc scoreDoc = scoreDocs[i];

float score = scoreDoc.score; // 相关度得分

int docId = scoreDoc.doc; // Document数据库的内部编号(是唯一的,由Lucene自动生成的)

// 根据编号取出真正的Document数据

Document doc = indexSearcher.doc(docId);

// 把Document转成Article

Article article = new Article();

article.setId(Integer.parseInt(doc.get("id"))); // 需要转Integer型

article.setTitle(doc.get("title")); // doc.getField("title").stringValue()

article.setContent(doc.get("content"));

list.add(article);

}

indexSearcher.close();

// 显示结果

System.out.println("总结果数量为:" + list.size());

for (Article article : list) {

System.out.println("--------> id = " + article.getId());

System.out.println("title = " + article.getTitle());

System.out.println("content= " + article.getContent());

}

}

}

Lucene核心类

建立索引的核心类

1,IndexWriter

2,Directory

3,Analyzer

4,Document

5,Field

搜索的核心类

1,IndexSearcher

2,Term

3,Query

4,QueryParser与MultiFieldQueryParser

5,TopDocs

6,ScoreDoc

Lucene下载目录的docs目录有以上类的介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值