lucene介绍

1、https://blog.csdn.net/shuaicihai/article/details/65111523

2、https://www.cnblogs.com/rodge-run/p/6551152.html

3、https://www.ibm.com/developerworks/cn/java/j-lo-lucene1/

一 简介

术语

  • 文档(Document):一般搜索引擎处理的对象是互联网网页,对于搜索引擎来讲,Word、PDF、html、XML等不同格式的文件都可以称为文档,一般以文档来表示文本信息。
  • 文档集合(Document Collection):由若干文档构成的集合成为文档集合。比如海量的互联网网页等。
  • 文档编号(Document ID):在搜索引擎内部,会为文档集合每个文档赋予一个唯一的内部编号,以作为文档的唯一标识,以便于处理。
  • 单词编号(Word ID):与文档编号类似,搜索引擎内部以唯一的编号来表示某个单词,以作为某个单词的唯一表示。
  • 倒排索引(Inverted Index):倒排索引是实现单词——文档矩阵的一种具体存储形式。通过倒排索引,可以根据单次快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:单词词典和倒排文件。
  • 单词词典(Lexicon):搜索引擎通常的索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引记载单词本身的一些信息及指向倒排列表的指针。单词也就是我们在搜索时的一些关键字,也称为词条。
  • 倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文当中出现的位置信息,每条记录成为一个倒排向(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。
  • 倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即为倒排文件,倒排文件是存储倒排索引的物理文件。

搜索数据的方式:
1. 顺序扫描法
所谓顺序扫描,例如要找内容包含一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。这种方法是顺序扫描方法,数据量大就搜索慢。
2. 倒排索引
倒排索引(也称为倒排文件)是一种存储了来自文本中的映射的索引数据结构。比如单词或者数字,对应到它们在数据库、一个文件或者一组文件中的位置。它是在文档检索系统中使用的最流行的数据结构,在搜索引擎中有大规模使用案例

二  Lucene执行原理

2.1 索引和搜索原理

  全文索引和搜索流程图:

  lucene介绍

  1、绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:

确定原始内容即要搜索的内容-->采集文档-->创建文档-->分析文档-->索引文档

  2、红色表示搜索过程,从索引库中搜索内容,搜索过程包括:

用户通过搜索界面-->创建查询-->执行搜索,从索引库搜索-->渲染搜索结果

2.2 创建索引

  对文档索引的过程,将用户要搜索的文档内容进行索引,索引存储在索引库(index)中。

  这里我们要搜索的文档是磁盘上的文本文件,根据案例描述:凡是文件名或文件内容包括关键字的文件都要找出来,这里要对文件名和文件内容创建索引。

  1) 获取原始文档

  原始文档 是指要索引和搜索的内容。原始内容包括互联网上的网页(爬虫)、数据库中的数据(sql查询)、磁盘上的文件(IO流获取)等。

  从互联网上、数据库、文件系统中等获取需要搜索的原始信息,这个过程就是信息采集,信息采集的目的是为了对原始内容进行索引。

  在Internet上采集信息的软件通常称为爬虫或蜘蛛,也称为网络机器人,爬虫访问互联网上的每一个网页,将获取到的网页内容存储起来。

Lucene不提供信息采集的类库,需要自己编写一个爬虫程序实现信息采集,也可以通过一些开源软件实现信息采集,如下:

Nutch(http://lucene.apache.org/nutch), Nutch是apache的一个子项目,包括大规模爬虫工具,能够抓取和分辨web网站数据。

jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一个由 java 开发的、开源的网络爬虫,用户可以使用它来从网上抓取想要的资源。其最出色之处在于它良好的可扩展性,方便用户实现自己的抓取逻辑。

  获取磁盘上文件的内容,可以通过文件流来读取文本文件的内容,对于pdf、doc、xls等文件可通过第三方提供的解析工具读取文件内容,比如Apache POI读取doc和xls的文件内容。

  2)创建文档对象

  获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。

  这里我们可以将磁盘上的一个文件当成一个document,Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容),如下图:

lucene介绍

  注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)

  每个文档都有一个唯一的编号,就是文档id。

  3) 分析文档

  将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词。

比如下边的文档经过分析如下:

  原文档内容:

Lucene is a Java full-text search engine.  Lucene is not a complete

application, but rather a code library and API that can easily be used

to add search capabilities to applications.

  分析后得到的语汇单元:

lucene、java、full、search、engine。。。。

  每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的term。term中包含两部分一部分是文档的域名,另一部分是单词的内容。

  例如:文件名中包含apache和文件内容中包含的apache是不同的term。

  4) 创建索引

  对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。

lucene介绍

  注意:创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构

  传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。

  倒排索引结构是根据内容(词语)找文档,如下图:

lucene介绍

  倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。

2.3 查询索引

  查询索引也是搜索的过程。搜索就是用户输入关键字,从索引(index)中进行搜索的过程。根据关键字搜索索引,根据索引找到对应的文档,从而找到要搜索的内容(这里指磁盘上的文件)。

  1) 用户查询接口

  全文检索系统提供用户搜索的界面供用户提交搜索的关键字,搜索完成展示搜索结果。

  Lucene不提供制作用户搜索界面的功能,需要根据自己的需求开发搜索界面。

  2) 创建查询

  用户输入查询关键字执行搜索之前需要先构建一个查询对象,查询对象中可以指定查询要搜索的Field文档域、查询关键字等,查询对象会生成具体的查询语法,

  例如:  语法 “fileName:lucene”表示要搜索Field域的内容为“lucene”的文档

  3) 执行查询

  搜索索引过程:

  根据查询语法在倒排索引词典表中分别找出对应搜索词的索引,从而找到索引所链接的文档链表。

  比如搜索语法为“fileName:lucene”表示搜索出fileName域中包含Lucene的文档。

  搜索过程就是在索引上查找域为fileName,并且关键字为Lucene的term,并根据term找到文档id列表。

  4) 渲染结果

  以一个友好的界面将查询结果展示给用户,用户根据搜索结果找自己想要的信息,为了帮助用户很快找到自己的结果,提供了很多展示的效果,比如搜索结果中将关键字高亮显示,百度提供的快照等。

三 一个简单的搜索应用程序

假设我们的电脑的目录中含有很多文本文档,我们需要查找哪些文档含有某个关键词。为了实现这种功能,我们首先利用 Lucene 对这个目录中的文档建立索引,然后在建立好的索引中搜索我们所要查找的文档。通过这个例子读者会对如何利用 Lucene 构建自己的搜索应用程序有个比较清楚的认识。

建立索引

为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介绍一下这五个类的用途:

Document

Document 是用来描述文档的,这里的文档可以指一个 HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。

Field

Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。

Analyzer

在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给 IndexWriter 来建立索引。

IndexWriter

IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。

Directory

这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。

熟悉了建立索引所需要的这些类后,我们就开始对某个目录下面的文本文件建立索引了,清单 1 给出了对某个目录下的文本文件建立索引的源代码。

清单 1. 对文本文件建立索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package TestLucene;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.Date;
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.index.IndexWriter;
/**
* This class demonstrate the process of creating index with Lucene
* for text files
*/
public class TxtFileIndexer {
     public static void main(String[] args) throws Exception{
     //indexDir is the directory that hosts Lucene's index files
     File   indexDir = new File("D:\\luceneIndex");
     //dataDir is the directory that hosts the text files that to be indexed
     File   dataDir  = new File("D:\\luceneData");
     Analyzer luceneAnalyzer = new StandardAnalyzer();
     File[] dataFiles  = dataDir.listFiles();
     IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
     long startTime = new Date().getTime();
     for(int i = 0; i < dataFiles.length; i++){
          if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){
               System.out.println("Indexing file " + dataFiles[i].getCanonicalPath());
               Document document = new Document();
               Reader txtReader = new FileReader(dataFiles[i]);
               document.add(Field.Text("path",dataFiles[i].getCanonicalPath()));
               document.add(Field.Text("contents",txtReader));
               indexWriter.addDocument(document);
          }
     }
     indexWriter.optimize();
     indexWriter.close();
     long endTime = new Date().getTime();
         
     System.out.println("It takes " + (endTime - startTime)
         + " milliseconds to create index for the files in directory "
         + dataDir.getPath());       
     }
}

在清单 1 中,我们注意到类 IndexWriter 的构造函数需要三个参数,第一个参数指定了所创建的索引要存放的位置,他可以是一个 File 对象,也可以是一个 FSDirectory 对象或者 RAMDirectory 对象。第二个参数指定了 Analyzer 类的一个实现,也就是指定这个索引是用哪个分词器对文挡内容进行分词。第三个参数是一个布尔型的变量,如果为 true 的话就代表创建一个新的索引,为 false 的话就代表在原来索引的基础上进行操作。接着程序遍历了目录下面的所有文本文档,并为每一个文本文档创建了一个 Document 对象。然后把文本文档的两个属性:路径和内容加入到了两个 Field 对象中,接着在把这两个 Field 对象加入到 Document 对象中,最后把这个文档用 IndexWriter 类的 add 方法加入到索引中去。这样我们便完成了索引的创建。接下来我们进入在建立好的索引上进行搜索的部分。

搜索文档

利用 Lucene 进行搜索就像建立索引一样也是非常方便的。在上面一部分中,我们已经为一个目录下的文本文档建立好了索引,现在我们就要在这个索引上进行搜索以找到包含某个关键词或短语的文档。Lucene 提供了几个基础的类来完成这个过程,它们分别是呢 IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍这几个类的功能。

Query

这是一个抽象类,他有多个实现,比如 TermQuery, BooleanQuery, PrefixQuery. 这个类的目的是把用户输入的查询字符串封装成 Lucene 能够识别的 Query。

Term

Term 是搜索的基本单位,一个 Term 对象有两个 String 类型的域组成。生成一个 Term 对象可以有如下一条语句来完成:Term term = new Term(“fieldName”,”queryWord”); 其中第一个参数代表了要在文档的哪一个 Field 上进行查找,第二个参数代表了要查询的关键词。

TermQuery

TermQuery 是抽象类 Query 的一个子类,它同时也是 Lucene 支持的最为基本的一个查询类。生成一个 TermQuery 对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参数,那就是一个 Term 对象。

IndexSearcher

IndexSearcher 是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个 IndexSearcher 的实例在一个索引上进行操作。

Hits

Hits 是用来保存搜索的结果的。

介绍完这些搜索所必须的类之后,我们就开始在之前所建立的索引上进行搜索了,清单 2 给出了完成搜索功能所需要的代码。

清单 2 :在建立好的索引上进行搜索
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package TestLucene;
import java.io.File;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.FSDirectory;
/**
* This class is used to demonstrate the
* process of searching on an existing
* Lucene index
*
*/
public class TxtFileSearcher {
    public static void main(String[] args) throws Exception{
       String queryStr = "lucene";
       //This is the directory that hosts the Lucene index
       File indexDir = new File("D:\\luceneIndex");
       FSDirectory directory = FSDirectory.getDirectory(indexDir,false);
       IndexSearcher searcher = new IndexSearcher(directory);
       if(!indexDir.exists()){
            System.out.println("The Lucene index is not exist");
            return;
       }
       Term term = new Term("contents",queryStr.toLowerCase());
       TermQuery luceneQuery = new TermQuery(term);
       Hits hits = searcher.search(luceneQuery);
       for(int i = 0; i < hits.length(); i++){
            Document document = hits.doc(i);
            System.out.println("File: " + document.get("path"));
       }
    }
}

在清单 2 中,类 IndexSearcher 的构造函数接受一个类型为 Directory 的对象,Directory 是一个抽象类,它目前有两个子类:FSDirctory 和 RAMDirectory. 我们的程序中传入了一个 FSDirctory 对象作为其参数,代表了一个存储在磁盘上的索引的位置。构造函数执行完成后,代表了这个 IndexSearcher 以只读的方式打开了一个索引。然后我们程序构造了一个 Term 对象,通过这个 Term 对象,我们指定了要在文档的内容中搜索包含关键词”lucene”的文档。接着利用这个 Term 对象构造出 TermQuery 对象并把这个 TermQuery 对象传入到 IndexSearcher 的 search 方法中进行查询,返回的结果保存在 Hits 对象中。最后我们用了一个循环语句把搜索到的文档的路径都打印了出来。 好了,我们的搜索应用程序已经开发完毕,怎么样,利用 Lucene 开发搜索应用程序是不是很简单。

上一篇:解决vue在ios或android中用webview打开H5链接时#号后面的参数被忽略问题angular同样适用


下一篇:打开excl链接时总是出现问题