`
yupengcc
  • 浏览: 132421 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

用Lucene构建实时索引的文档更新问题

阅读更多

1、Lucene删除文档的几种方式

 

  • IndexReader.deleteDocument(int docID)是用 IndexReader 按文档号删除。  
  • IndexReader.deleteDocuments(Term  term)是用 IndexReader 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term  term)是用 IndexWriter 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term[]  terms)是用 IndexWriter 删除包含这些词(Term)的文档。  
  • IndexWriter.deleteDocuments(Query  query)是用 IndexWriter 删除能满足此查询(Query)的文档。  
  • IndexWriter.deleteDocuments(Query[] queries)是用 IndexWriter 删除能满足这些查询(Query)的文档。

删除文档既可以用reader进行删除,也可以用writer进行删除,不同的是,reader进行删除后,此reader马上能够生效,而用writer删除后,会被缓存,只有写入到索引文件中,当reader再次打开的时候,才能够看到。

2、Lucene文档更新的几个问题

 

2.1、使用IndexReader还是IndexWriter进行删除

既然IndexReader和IndexWriter都能够进行文档删除,那么到底是应该用哪个来进行删除呢?

本文的建议是,用IndexWriter来进行删除。

因为用IndexReader可能存在以下的问题:

(1) 当有一个IndexWriter打开的时候,IndexReader的删除操作是不能够进行的,否则会报LockObtainFailedException

(2) 当IndexReader被多个线程使用的时候,一个线程用其进行删除,会使得另一个线程看到的索引有所改变,使得另一个线程的结果带有不确定性。

(3) 对于更新操作,在Lucene中是先删除,再添加的,然而删除的被立刻看到的,而添加却不能够立刻看到,造成了数据的不一致性。

(4) 即便以上问题可以通过锁来解决,然而背后的操作影响到了搜索的速度,是我们不想看到的。

2.2、如何在内存中缓存文档的删除

在上一节中,为了能够做到实时性,我们使用内存中的索引,而硬盘上的索引则不经常打开,即便打开也在背后线程中打开。

而要删除的文档如果在硬盘索引中,如果不重新打开则看不到新的删除,则需要将删除的文档缓存到内存中。

那如何将缓存在内存中的文档删除在不重新打开IndexReader的情况下应用于硬盘上的索引呢?

在Lucene中,有一种IndexReader为FilterIndexReader,可以对一个IndexReader进行封装,我们可以实现一个自己的FilterIndexReader来过滤掉删除的文档。

一个例子如下:

 

public class MyFilterIndexReader extends FilterIndexReader {

  OpenBitSet dels;

  public MyFilterIndexReader(IndexReader in) {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

  }

  public MyFilterIndexReader(IndexReader in, List<String> idToDelete) throws IOException {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

    for(String id : idToDelete){

      TermDocs td = in.termDocs(new Term("id", id)); //如果能在内存中Cache从Lucene的ID到应用的ID的映射,Reader的生成将快得多。

      if(td.next()){

        dels.set(td.doc());

      }

    }

  }

  @Override

  public int numDocs() {

    return in.numDocs() - (int) dels.cardinality();

  }

  @Override

  public TermDocs termDocs(Term term) throws IOException {

    return new FilterTermDocs(in.termDocs(term)) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

  @Override

  public TermDocs termDocs() throws IOException {

    return new FilterTermDocs(in.termDocs()) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

}

 

2.3、文档更新的顺序性问题

Lucene的文档更新其实是删除旧的文档,然后添加新的文档。如上所述,删除的文档是缓存在内存中的,并通过FilterIndexReader应用于硬盘上的索引,然而新的文档也是以相同的id加入到索引中去的,这就需要保证缓存的删除不会将新的文档也过滤掉,将缓存的删除合并到索引中的时候不会将新的文档也删除掉。

Lucene的两次更新一定要后一次覆盖前一次,而不能让前一次覆盖后一次。

所以内存中已经硬盘中的多个索引是要被保持一个顺序的,哪个是老的索引,哪个是新的索引,缓存的删除自然是应该应用于所有比他老的索引的,而不应该应用于他自己以及比他新的索引。

3、具有更新功能的Lucene实时索引方案

3.1、初始化

首先假设我们硬盘上已经有一个索引FileSystemIndex,被事先打开的,其中包含文档1,2,3,4,5,6。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,其中包含文档7,8。

绘图8

 

3.2、更新文档5

这时候来一个新的更新文档5, 需要首先将文档5删除,然后加入新的文档5。

需要做的事情是:

  • 首先在内存索引中删除文档5,当然没有文档5,删除无效。
  • 其次将对文档5的删除放入内存文档删除列表,并与硬盘的IndexReader组成FilterIndexReader
  • 最后,将新的文档5加入内存索引,这时候,用户可以看到的就是新的文档5了。
  • 将文档5放入删除列表以及将文档5提交到内存索引两者应该是一个原子操作,好在这两者都是比较块的。

注:此处对硬盘上的索引,也可以进行对文档5的删除,由于IndexReader没有重新打开,此删除是删不掉的,我们之所以没有这样做,是想保持此次更新要么全部在内存中,要么全部在硬盘中,而非删除部分已经应用到硬盘中,而新文档却在内存中,此时,如果系统crash,则新的文档5丢失了,而旧的文档5也已经在硬盘上被删除。我们将硬盘上对文档5的删除放到从内存索引向硬盘索引的合并过程。

更新文档5

如果再有一次对文档5的更新,则首先将内存索引中的文档5删除,添加新的文档5,然后将文档5加入删除列表,发现已经存在,则不必删除。

3.3、合并索引

然而经过一段时间,内存中的索引需要合并到硬盘上。

在合并的过程中,需要重新建立一个空的内存索引,用于合并阶段索引新的文档,而合并中的索引的IndexReader以及硬盘索引和删除列表所组成的FilterIndexReader仍然保持打开,对外提供服务,而合并阶段从后台进行。

后台的合并包括以下几步:

  • 将删除列表应用到硬盘索引中。
  • 将内存索引合并到硬盘索引中。
  • IndexWriter提交。

合并

3.4、合并的过程中更新文档5

在合并的过程中,如果还有更新那怎么办呢?

  • 首先将合并中索引的文档5删除,此删除不会影响合并,因为合并之前,合并中索引的IndexReader已经打开,索引合并中索引的文档5还是会合并到硬盘中去的。此删除影响的是此后的查询在合并中索引是看不到文档5的。
  • 然后将文档5的删除放入删除列表,并同合并中索引的删除列表,已经硬盘索引一起构成FilterIndexReader。
  • 将新的文档5添加到内存中索引。
  • 提交在合并中索引对文档5的删除,将文档5添加到删除列表,提交在内存索引中对文档5的添加三者应该是一个原子操作,好在三者也是很快的。

合并时更新

3.5、重新打开硬盘索引的IndexReader

当合并中索引合并到硬盘中的时候,是时候重新打开硬盘上的索引了,新打开的IndexReader是可以看到文档5的删除的。

如果这个时候有新的更新,也是添加到内存索引和删除列表的,比如我们更新文档6.

重新打开

3.6、替代IndexReader 

当IndexReader被重新打开后,则需要删除合并中的索引及其删除列表,将硬盘索引原来的IndexReader关闭,使用新的IndexReader。

替换IndexReader

 

分享到:
评论

相关推荐

    有关Lucene的问题(8):用Lucene构建实时索引的文档更新问题[整理].pdf

    有关Lucene的问题(8):用Lucene构建实时索引的文档更新问题[整理].pdf

    使用lucene构建一个简单的搜索引擎

    全文检索首先将要查询的目标文档中的词提取出来,组成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。这是一个使用lucene开源框架编写的一个...

    构建索引并实现对文档集合的检索

    基于java语言,用lucene实现对文档集的索引和收索。要求对不少于3篇文章进行向量的构建,并将产生的文档向量及文档倒排索引输出或保存在文件中。 要求对检索式构建检索向量,并输出文档与检索向量的相关度,以及文档...

    Lucene IndexApplication:使用lucene索引文本文档-开源

    自动和安排 Lucene 搜索索引的构建。 用于记录系统消息的通用日志框架。 您可以为严重和非严重错误和消息配置设置。 排名搜索——最好的结果首先返回许多强大的查询类型:短语查询、通配符查询、邻近查询、范围查询...

    论文研究-并行密文倒排索引研究.pdf

    实验结果表明,Crypt-Lucene与SSE-1相比,索引构建时间减少了约为60%,同时具有较好的空间性能,对于大文档集合,利用MapReduce在4结点构成的Hadoop集群上并行构建8个Crypt-Lucene索引能减少83.4%的时间。

    lucene例子

    Lucene 强大的 API 主要关注文本索引和搜索。它可以用于为各种应用程序构建搜索功能,比如电子邮件客户端、邮件列表、Web 搜索、数据库搜索等等。Wikipedia、TheServerSide、jGuru 和 LinkedIn 等网站都使用了 ...

    Lucene项目的文档资料大集合

    lucene_构建一个简单的WEB搜索程序 Heritrix简单任务的设置方法 创建索引_简单搜索 使用POI来处理Excel_Word_PowerPoint文件格式 nutch xpdf读取pdf pdfbox读取pdf 尚学堂lucene资料大集合

    【信息检索课程设计】sdu新闻网站全站爬取+索引构建+搜索引擎

    索引构建 对上一步爬取到的网页进行结构化预处理,包括基于模板的信息抽取、分字段解析、分词、构建索引等。 检索排序 对上一步构建的索引库进行查询,对于给定的查询,给出检索结果,明白排序的原理及方法。 详细...

    开放源代码的全文检索引擎Lucene

    第四节 Lucene索引构建逻辑模块分析··· 15 一、 绪论··· 15 二、 对象体系与UML图··· 16 1. 项(Term)··· 16 2. 域(Field)··· 17 3. 文档(document)··· 18 4. 段(segment)···...

    Heritrix lucene开发自己的搜索引擎(源码)1

    从产品信息文件构建索引的源代码 数据库操作类的源代码 基于Spring的业务层管理 检索的业务类源代码 检索的DAO源代码 检索的分页实现源代码 检索的AJAX实现源代码 安装:直接在Eclipse中选取“import-&gt;...

    开发自己的搜索引擎lucene and heritrix

    从产品信息文件构建索引的源代码 数据库操作类的源代码 基于Spring的业务层管理 检索的业务类源代码 检索的DAO源代码 检索的分页实现源代码 检索的AJAX实现源代码 安装:直接在Eclipse中选取“import-&gt;...

    基于Lucene的企业电子文档搜索系统的开发研究

    并将其引入到系统开发中,在主流的B/S分层架构基础上,重点对文本提取模块、中文词划分模块、索引模块和搜索模块进行了设计与实现,构建了一个基于Lucene的企业电子文档搜索系统。实践表明,本系统为企业员工提供了...

    java8看不到源码-pdfindexer:使用ApacheLucene和PDFBox索引和搜索PDF文件

    文档 Maven 依赖 Maven 依赖 &lt;!-- Java Library and Tool to Index and search PDF files using Apache Lucene and PDF Box http://www.bitplan.com/PdfIndexer --&gt; &lt; dependency &gt; &lt; groupId &gt;...

    lucene教案1

    Lucene实现全文检索的流程索引和搜索流程图1、绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:确定原始内容即要搜索的内容采集文档创

    人工智能-项目实践-搜索引擎-基于lucene的新闻搜索引擎[中科院现代信息检索项目作业]

    整体思路 在实现新闻信息检索系统时首先进行了信息采集,信息采集结束之后使用 Lucene 提供的 api 构建索引库, 前端使用 jsp 接收用户查询,在后台使用 servlet 对用户查询进 行分词处理,之后到索引库中进行文档匹配, ...

    elasticsearch概述及应用.pdf

    Elasticsearch,简称ES,是一个基于Lucene构建的开源、分布式、RESTful搜索引擎。它提供了一个分布式多租户能力的全文搜索引擎,具有HTTP Web界面和无模式JSON文档。Elasticsearch是用Java开发的,并作为Apache许可...

    Heritrix lucene开发自己的搜索引擎(源码)3

    从产品信息文件构建索引的源代码 数据库操作类的源代码 基于Spring的业务层管理 检索的业务类源代码 检索的DAO源代码 检索的分页实现源代码 检索的AJAX实现源代码 安装:直接在Eclipse中选取“import-&gt;...

    LucenePlusPlus:Lucene ++是流行的Java Lucene库(一种高性能,功能齐全的文本搜索引擎)的最新C ++端口。

    Lucene ++ 欢迎使用lucene ++版本3.0.8 。 Lucene ++是流行的Java 库的C ++端口,Java 库...lucene_tester是使用构建的。 您可以使用以下从仓库根目录运行的命令在Unix上运行测试套件: $ build/src/test/lucene++-

    自己动手写搜索引擎

    该书详细讲解了搜索引擎与信息检索基础,Lucene入门实例,Lucene索引的建立,使用Lucene进行搜索,排序,过滤和分页,Lucene的分析器,对Word、Excel和PDF格式文档的处理,Compass搜索引擎框架,Lucene分布式和...

    java(结合lucene)版的公交搜索系统.zip

    它通过对公交数据的索引构建,实现了对公交线路、站点名称、途经站点等关键信息的全文搜索。用户可以通过输入关键词,快速定位到相关的公交线路和站点信息,大大提高了查询效率。 此外,该系统还具备丰富的功能特性...

Global site tag (gtag.js) - Google Analytics