阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Lucene实现索引和查询

138次阅读
没有评论

共计 10168 个字符,预计需要花费 26 分钟才能阅读完成。

0 引言

随着万维网的发展和大数据时代的到来,每天都有大量的数字化信息在生产、存储、传递和转化,如何从大量的信息中以一定的方式找到满足自己需求的信息,使之有序化并加以利用成为一大难题。全文检索技术是现如今最普遍的信息查询应用,生活中利用搜索引擎,在博客论坛中查找信息,这些搜索的核心原理就是本文要实现的全文检索技术。随着文档信息数字化的实现,将信息有效存储并及时准确的提取是每一个公司、企业和单位要做好的基础。针对英文的全文检索已经有很多成熟的理论和方法,开放源代码的全文检索引擎 Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目,它的目的是为软件开发人员提供一个简单易用的工具包,方便在目标系统中实现全文检索的功能。Lucene 不支持中文,但是目前已有很多开源的中文分词器可以对中文内容进行索引,本文在研究 Lucene 核心原理的基础上,分别实现了对中英文网页的爬取和检索。

1 Lucene 介绍

1.1 lucene 简介

Lucene 是一个用 Java 写的全文检索引擎工具包,实现构造了索引和搜索两大核心功能,并且两者相互独立,这使得开发人员可以方便扩展,Lucene 提供了丰富的 API , 可以与存储在索引中的信息方便的交互。需要说明的是它并不是一个完整的全文检索应用, 而是为应用程序提供索引和搜索功能。即若想让 Lucene 真正起作用, 还需在其基础上做一些必要的二次开发。

Lucene 的结构设计与数据库的设计较为相似,但 Lucene 的索引与数据库有着极大的不同。数据库和 Lucene 建立索引都是为了查找方便,但是数据库仅仅针对部分字段进行建立,且需要把数据转化为格式化信息,并予以保存。而全文检索是将全部信息按照一定方式进行索引。两种检索的不同和相似如表 1 - 1 所示。

表 1 -1:数据库检索与 Lucene 检索对比

比较项

Lucene 检索

数据库检索

数据检索

从 Lucene 的索引文件中检出

由数据库索引检索记录

索引结构

Document(文档)

Record(记录)

查询结果

Hit:满足关系的文档组成

查询结果集:包含关键字的记录组成

全文检索

支持

不支持

模糊查询

支持

不支持

结果排序

设置权重,进行相关性排序

不能排序

1.2 lucene 总体结构

  Lucene 软件包的发布形式是一个 JAR 文件,版本更新较快且版本差距较大,本文使用的是 5.3.1 的版本,主要使用的子包如表 1 - 2 所示。

表 1 -2:子包和功能

包名

功能

Org .apache.lucene .analysis

分词

Org .apache.lucene .document

对索引管理的文档

Org .apache.lucene .index

索引操作,包括增加、删除等

Org .apache.lucene .queryParser

查询器,构造检索表达式

Org .apache.lucene .search

检索管理

Org .apache.lucene .store

数据存储管理

Org .apache.lucene .util

公共类

 

1.3 lucene 架构设计

Lucene 功能非常强大,但从根本上来说,主要包括两块:一是从文本内容切分词后索引入库;二是根据查询条件返回结果,即建立索引和进行查询两部分。

如图 1 - 1 所示,本文抛出外部接口以及信息来源,重点对网页爬取的文本内容进行索引和查询。Lucene 实现索引和查询 

图 1 -1:Lucene 的架构设计

 

 

2 JDK 的安装和环境变量的配置

1.jdk 的下载:

在 Oracle 官网下载符合系统版本的压缩包,网址如下。点击安装,根据提示进行安装,在安装过程中会提示是否安装 jre,点击是。http://www.oracle.com/technetwork/java/javase/downloads/index.html

2. 设置环境变量:

(1)右键计算机 =》属性 =》高级系统设置 =》环境变量 =》系统变量 =》新建 =》JAVA_HOME: 安装路径

(2)Path 中新增 =》%JAVA_HOME%\bin

  3. 测试是否成功:

    开始 =》运行 =》CMD  回车 在弹出的 DOS 窗口内

输入:java -version 会出现版本信息,

输入:javac 出现 javac 的用法信息

出现如图 2 - 1 所示为成功。

Lucene 实现索引和查询

图 2 -1:cmd 命令框测试 java 配置

编写 Java 代码实现对网页内容的获取

因为 Lucene 针对不同语言要使用不同的分词器,英文使用标准分词器,中文选择使用 smartcn 分词器。在获取网页的时候,先获取网页存为 html 文件,在 html 中由于标签           的干扰,会对检索效果产生影响,因此需要对 html 标签进行剔除,并将文本内容转为 txt 文件进行保存。中英文除了分词器不同,其他基本一致,因此之后的代码和实验结果演            示会选择任一。本文选取五十篇中文故事和英文故事的网页为例。

              具体代码设计如下图:Url2Html.java 将输入网址的网页转存为 html 文件,Html2Txt.java 文件实现 html 文档标签的去除,转存为 txt 文档。具体代码如图 3 - 1 和 3 -2。

 
public void way(String filePath,String url) throws Exception{File dest = new File(filePath);// 建立文件
        InputStream is;// 接收字节输入流
        FileOutputStream fos = new FileOutputStream(dest);// 字节输出流
        URL wangzhi = new URL(url);// 设定网址 URL
        is = wangzhi.openStream();
        BufferedInputStream bis = new BufferedInputStream(is);// 为字节输入流加缓冲
        BufferedOutputStream bos = new BufferedOutputStream(fos);// 为字节输出流加缓冲
        /*
         * 对字节进行读取
         */
        int length;
        byte[] bytes = new byte[1024*20];
        while((length = bis.read(bytes, 0, bytes.length)) != -1){fos.write(bytes, 0, length);
        }
        /*
         * 关闭缓冲流和输入输出流
         */
        bos.close();        
        fos.close();
        bis.close();
        is.close();}
 
 

public String getBody(String val){
String zyf = val.replaceAll(“</?[^>]+>”, “”); // 剔出 <html> 的标签
return zyf;
}

public void writeTxt(String Str,String writePath) {
File writename = new File(writePath);
try {
writename.createNewFile();
BufferedWriter out = new BufferedWriter(new FileWriter(writename));
�� out.write(Str);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}

以童话故事《笨狼上学》的网页为例,文档路径设为”E:\work \lucene \test \data \html”和”E:\work\lucene\test\data\txt”,在每一次读取网页的时候需要设定的两个参数为文件命名 filename 和获取目标网址 url。新建一个 main 函数,实现对两个方法的调用。具体实现如图 3 - 3 所示:

 

public static void main(String[] args) {
String filename = “jingdizhi”;// 文件名字
String url = “http://www.51test.net/show/8072125.html”;// 需要爬取的网页 url
String filePath = “E:\\work\\lucene\\test\\data\\html\\”+filename+”.html”;// 写出 html 的文件路径 + 文件名
String writePath = “E:\\work\\lucene\\test\\data\\txt\\”+filename+”.txt”;// 写出 txt 的文件路径 + 文件名

Url2Html url2html = new Url2Html();
try {
url2html.way(filePath,url);
} catch (Exception e) {
e.printStackTrace();
}

Html2Txt html2txt = new Html2Txt();
String read=html2txt.readfile(filePath);// 读取 html 文件
String txt = html2txt.getBody(read);// 去除 html 标签
System.out.println(txt);
try {
html2txt.writeTxt(txt,writePath);
} catch (Exception e) {
e.printStackTrace();
}
}

执行程序后,分别在两个文件夹中建立”笨狼上学.html”和”笨狼上学.txt”。

建立索引

索引和查询的基本原理如下:

建立索引:搜索引擎的索引其实就是实现“单词 - 文档矩阵”的具体数据结构。也是进行全文检索的第一步,lucene 提供 IndexWriter 类进行索引的管理,主要包括 add()、delete()、update()。还有对权值的设定,通过不同索引权值的设定,可以在搜索的时候根据相关性大小进行返回。

进行搜索:原本的直接搜索是针对文档进行顺序检索,在建立索引之后,可以通过对索引的查找以找到索引词在文档中出现的位置,然后返回索引项所对的文档中的位置和词。Lucene 提供 IndexSearcher 类进行对文档的检索,检索形式主要分为两类,第一类是 Term,针对单个词项的检索;第二类是 Parser,可以自定义构造检索表达式,有较多的检索形式,具体的方法会在之后进行实现的演示。

4.1 实验环境

本 PC 机采用 windows 10×64 系统,8G 内存,256G 固态硬盘。开发环境为 Myeclipse 10,jdk 版本为 1.8。在实验过程中,因为部分语法的转变,若干 Class 采用 1.6 版本实现。

4.2 建立索引

建立索引库就是往索引库添加一条条索引记录,Lucene 为添加一条索引记录提供了接口,添加索引。

主要用到了“写索引器”、“文档”、“域”这 3 个类。要建立索引,首先要构造一个 Document 文档对象,确定 Document 的各个域,这类似于关系型数据库中表结构的建立,Document 相当于表中的一个记录行,域相当于一行中的列,在 Lucene 中针对不同域的属性和数据输出的需求,对域还可以选择不同的索引 / 存储字段规则,在本实验中,文件名 fileName、文件路径 fullPath 和文本内容 content 作为 Document 的域。

IndexWriter 负责接收新加入的文档,并写入索引库中。在创建“写索引器”IndexWriter 时需要指定所使用的语言分析器。建立索引分为两个类别,第一:不加权索引;第二:加权索引。

 
public Indexer(String indexDir)throws Exception{Directory dir=FSDirectory.open(Paths.get(indexDir));
        Analyzer analyzer=new StandardAnalyzer(); // 标准分词器
        //SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();
        IndexWriterConfig iwc=new IndexWriterConfig(analyzer);
        writer=new IndexWriter(dir, iwc);
    }

设置索引字段,Store 表示是否对索引内容存储:fileName 和 fullPath 占用内存较少可以进行存储,以方便查询返回。

 
private Document getDocument(File f)throws Exception {Document doc=new Document();
        doc.add(new TextField("contents", new FileReader(f)));
        doc.add(new TextField("fileName", f.getName(),Store.YES));
        doc.add(new TextField("fullPath",f.getCanonicalPath(),Store.YES));// 路径索引 
        return doc;
    }

执行主代码后结果如图:设计在索引某个文件的时候返回文件“索引文件:+ 文件路径”,且计算输出索引全部文件花费的时间。

Lucene 实现索引和查询

4.3 对索引的删除和修改

一般对数据库的操作包括 CRUD(增加、删除、更改、查询),增加就是对索引项的选择和建立,查询作为较为核心的功能会在之后展开论述,这里主要记录一下在删除、更新索引时用到的方法。

删除分为两种类型,包括普通的删除和彻底删除,因为索引的删除影响到整个数据库,而且对于大型的系统而言,删除索引意味着对系统的底层进行更改,耗时耗力而且无法返回,前面索引的时候看到建立索引后生成若干小文件,当进行查找的时候会将各个文件进行合并然后查找。普通删除仅仅是对之前建立的索引做个简单的标记,致使无法进行查找返回。彻底删除则是对索引进行销毁,无法撤销。以删除索引项“id”为 1 的索引为例:

普通的删除 (在合并前删除):

writer.deleteDocuments(new Term("id","1"));

writer.commit();

彻底的删除(在合并后删除):

writer.deleteDocuments(new Term("id","1"));

   writer.forceMergeDeletes(); // 强制删除 

   writer.commit();

对索引的修改原理比较简单,就是在原有索引的基础上实现覆盖,实现代码跟上文的增加索引一样,在此不多做阐述。

4.4 对索引的加权

Lucene 默认按照相关度排序,Lucene 对 Field 提供了一个可以设置的 Boosting 参数,这个参数用来表示记录的重要性,在满足搜索条件是,会优先考虑重要性高的记录,返回结果靠前,如果记录较多,权值低的记录会排到首页之后,因此,对索引的加权操作是影响返回结果满意度的重要因素,在实际设计信息系统的时候,应该有严格的权值计算公式,方便对 Field 权值的更改,更好的满足用户的需求。

例如搜索引擎将点击率高,链入链出的网页给定较高的权重,在返回的时候排到第一页。实现代码如图 4 - 1 所示,不加权和加权结果对比如图 4 - 2 所示。

     TextField field = new TextField("fullPath", f.getCanonicalPath(), Store.YES);
        if("A GREAT GRIEF.txt".equals(f.getName())){field.setBoost(2.0f);// 对文件名为 secondry story.txt 的 fullPath 路径加权;
        }                        // 默认权重为 1.0,改为 1.2 即增加权重。
        doc.add(field);

图 4 -1:索引加权

Lucene 实现索引和查询

图 4 -2:加权之前

Lucene 实现索引和查询

图 4 -2:加权之后

由图 4 - 2 结果可以看出,不加权时,按照字典顺序排列返回,因此 first 在 secondry 之前,在对 secondry 命名的文件路径加权后,返回的时候顺序发生变化,实现对权重的测试。

进行查询

Lucene 的检索接口主要由 QueryParser、IndexSearcher、Hits 这 3 个类构成,QueryParser 是查询解析器,负责解析用户提交的查询关键字,在新建一个解析器时需要指定要解析的域和使用什么语言分析器,这里使用的语言分析器必须与索引库建立时使用的解析器相同,否则查询结果不正确。IndexSearcher 是索引搜索器,在实例化 IndexSearcher 时需要指定索引库所在的目录,IndexSearcher 有一个 search 方法执行索引的检索,这个方法接受 Query 作为参数,返回 Hits,Hists 是一系列排好序的查询结果的集合,集合的元素是 Document。通过 Document 的 get 方法可以得到与这个文档对应文件的信息,比如:文件名、文件路径、文件内容等。

5.1 基本查询

如图查询主要有两种方式,但是推荐使用第一种构造 QueryParser 表达式,它可以有灵活的组合方式,包括布尔逻辑表达、模糊匹配等,但是第二种 Term 只能针对词汇查询。

1. 构造 QueryParser 查询式:

    QueryParser parser=new QueryParser("fullPath", analyzer);

       Query query=parser.parse(q);

2. 对特定项的查询:

    Term t = new Term("fileName", q);

       Query query = new TermQuery(t);

查询结果如图 5 - 1 所示:以查询文件名 fileName 包含“大”为例。

Lucene 实现索引和查询

图 5 -1:“大”查询结果

5.2 模糊查询

在构造 QueryParser 时,通过对词项 q 的修改可以实现精确匹配和模糊匹配。模糊匹配通过在“q”之后加“~”进行修改。如图 5 - 2 所示:

Lucene 实现索引和查询

Lucene 实现索引和查询

图 5 -2:模糊匹配

5.3 限定条件查询

布尔逻辑查询和模糊查询只需要对查询词 q 进行更改,而限定条件查询需要对 query 表达式进行设定,主要分为以下几类:

分别为指定项范围搜索、指定数字范围、指定字符串开头和多条件查询,分别列出应用的查询,true 参数指的:是否包含上限和下限在内。

指定项范围:

TermRangeQuery query=new TermRangeQuery("desc", new BytesRef("b".getBytes()), new BytesRef("c".getBytes()), true, true);

指定数字范围:

NumericRangeQuery<Integer> query=NumericRangeQuery.newIntRange("id", 1, 2, true, true);

指定字符串开头:

PrefixQuery query=new PrefixQuery(new Term("city","a"));

多条件查询:

 
NumericRangeQuery<Integer>query1=NumericRangeQuery.newIntRange("id", 1, 2, true, true);

PrefixQuery query2=new PrefixQuery(new Term("city","a"));

BooleanQuery.Builder booleanQuery=new BooleanQuery.Builder();

booleanQuery.add(query1,BooleanClause.Occur.MUST);

booleanQuery.add(query2,BooleanClause.Occur.MUST);

5.4 高亮查询

在百度、谷歌等搜索引擎中,进行查询时,返回的网页包含查询关键字的时候会显示为红色,且进行摘要显示,即对包含关键字的部分内容进行截取并返回。高亮查询即为实现对关键字的样式更改,本实验在 myeclipse 中进行,返回结果并���会有样式的改变,只会对返回内容的关键字添加 html 标签,如果显示到网页即产生样式的变化。

高亮的设置代码如图 5 - 3 所示,结果如图 5 - 4 所示,会对南京匹配词添加 <b> 和 <font> 标签,显示到网页上为加粗和变红。

QueryScorer scorer=new QueryScorer(query);
        Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
        SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");
        Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
        highlighter.setTextFragmenter(fragmenter);

图 5 -3:高亮设置

Lucene 实现索引和查询

图 5 -4:高亮显示结果

实验过程中遇到的问题和不足

Lucene 版本更新较快,在 jdk 版本、eclipse 版本和 lucene 版本之间需要一个良好的衔接,否则会造成很多的不兼容,在调试版本以及 jdk1.6 和 jdk1.8 的选择上出现很多困难,比如网页抓取中的 append 方法在 1.8 版本已经删除,不能使用。但是对文档路劲的读取 FSDirectory.open() 则需要 jdk1.8 才支持。

本实验的不足之处主要表现在:

代码的灵活性较低,在爬取网页的时候需要手工进行,且需要对中文和英文分别进行,应该完善代码使得对网页的语言有个判定,然后自动选择执行不同的分词器。

代码的复用性较低,没有较为合理的分类和方法的构建,为了简便,基本在几个核心代码中进行注释和标记而实现效果,有待改进。

代码的可移植性较低,对网页的爬取使用的是 jdk1.6 的版本,Lucene 的实现使用的是 jdk1.8 的版本,在导出到其他机器上,需要对环境稍加修改和配置,无法实现一键式操作。

总结

本文从 Lucene 的原理出发,了解了全文检索的思路和方法,并对常用的功能进行了实验和测试。在实验的过程中,了解了搜索引擎的原理,基于信息检索课程的内容上,有了一个更好的实操体验。Lucene 是一个优秀的开源全文本搜索技术框架,通过对它的深入研究,对其实现机制更加熟悉,在研究它的过程中学习了很多面向对象的编程方法和思想,它良好的系统框架和扩展性值得学习借鉴。

————————————– 分割线 ————————————–

基于 Lucene 多索引进行索引和搜索 http://www.linuxidc.com/Linux/2012-05/59757.htm

Lucene 实战 (第 2 版) 中文版 配套源代码 http://www.linuxidc.com/Linux/2013-10/91055.htm

Lucene 实战 (第 2 版) PDF 高清中文版 http://www.linuxidc.com/Linux/2013-10/91052.htm

使用 Lucene-Spatial 实现集成地理位置的全文检索 http://www.linuxidc.com/Linux/2012-02/53117.htm

Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a9 http://www.linuxidc.com/Linux/2012-02/53113.htm

Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a8 http://www.linuxidc.com/Linux/2012-02/53111.htm

Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a7 http://www.linuxidc.com/Linux/2012-02/53110.htm

Lucene 实践心得笔记  http://www.linuxidc.com/Linux/2017-03/141902.htm

Project 2-1: 配置 Lucene, 建立 WEB 查询系统 [Ubuntu 10.10] http://www.linuxidc.com/Linux/2010-11/30103.htm

Lucene 的配置及创建索引全文检索  http://www.linuxidc.com/Linux/2017-09/146773.htm

————————————– 分割线 ————————————–

Lucene 的详细介绍 :请点这里
Lucene 的下载地址 :请点这里 

本文永久更新链接地址 :http://www.linuxidc.com/Linux/2017-12/149793.htm

正文完
星哥说事-微信公众号
post-qrcode
 
星锅
版权声明:本站原创文章,由 星锅 2022-01-21发表,共计10168字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中