`
forfuture1978
  • 浏览: 413468 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

有关Lucene的问题(8):用Lucene构建实时索引的文档更新问题

阅读更多

在有关Lucene的问题(7),讨论了使用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。

 

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加入删除列表,发现已经存在,则不必删除。

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。

  • 大小: 58.2 KB
  • 大小: 52 KB
  • 大小: 35.9 KB
  • 大小: 57.4 KB
  • 大小: 27.2 KB
  • 大小: 59.2 KB
11
3
分享到:
评论
10 楼 YDY20082008 2011-05-17  
看得有些头大
9 楼 honglove 2011-04-07  
看了您写的lucene的方面的文章,首先在这里感谢您的付出,请教您一个问题,为啥lucene3.0.3的源码里面有大量的assert这个关键字,我在网上查询很多资料,都建议不在使用assert关键,请教
8 楼 wady 2010-10-10  
非常不错,真的学到东西。
7 楼 luckaway 2010-08-09  
问下,lucene能支持只更新某一个字段吗?

我的需求是相似帖子归类,只保持最新的帖子是可搜索的
流程:
1.索引帖子之前判断是否存在相似的帖子(相似帖子关联信息是用根据内容的md5存放memcachedb实现的)
2.如果存在相似帖子,就把之前的那个设置一个重复的标记,默认搜索时隐藏掉。但是还是有入口可搜索的到,所以重复的索引数据是不能删除的。

如果先把数据删除掉,再重新获取数据有点麻烦。

但是只是为了更新一个标示字段,把索引所有的内容都存放起来,觉的也不太值

想问下,这个有什么的解决方案吗?
6 楼 herry 2010-07-14  
在3.4合并的过程中更新文档5中的第一步没必要删除文档5吧。
因为合并中的索引也有删除列表,而这个列表中是有文档5的,后面的查询在合并中的索引是差不到文档5的
5 楼 shguan2010 2010-07-12  
能详细的解释一下 MyFilterIndexReader 吗? 不是太明白,谢谢
4 楼 A_Bing 2010-06-28  
   非常不错..
3 楼 yangfuchao418 2010-06-27  
  
2 楼 wwty 2010-06-27  
1 楼 sishuien 2010-06-27  
不错,用心了,有用的知识

相关推荐

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

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

    lucene-solr:Apache Lucene和Solr开源搜索软件

    有关更全面的文档,请访问: Lucene: : Solr: : 用Gradle构建 建筑Lucene 参见 。 建筑太阳能 首先,您需要设置开发环境(OpenJDK 11或更高版本)。 我们假设您知道如何获取和设置JDK-如果您不了解,那么...

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

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

    lucene例子

    计算每个文档匹配给定查询的分数,并根据分数返回最相关的文档。 支持许多强大的查询类型,比如 PhraseQuery、WildcardQuery、RangeQuery、FuzzyQuery、BooleanQuery 等。 支持解析人们输入的丰富查询表达式。 允许...

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

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

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

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

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

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

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

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

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

    java8 看不到源码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; ...

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

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

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

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

    Lucene项目的文档资料大集合

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

    elasticsearch-tutorial:构建 Elasticsearch 索引和添加几个文档的简短教程

    构建 Elasticsearch 索引和添加几个文档的简短教程 入门 并解压到本地目录。 启动elasticsearch运行 &gt;&gt; ./bin/elasticsearch 这将在启动一个本地 Elasticsearch 集群。 为确保集群正在运行,请访问该 url 并确保...

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

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

    lucene教案1

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

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

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

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

    官方与Lucene和lucene ++相关的有用链接和文档。 Otis Gospodnetic和Erik Hatcher的 。 运行单元测试套件 lucene_tester是使用构建的。 您可以使用以下从仓库根目录运行的命令在Unix上运行测试套件: $ build/src/...

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

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

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

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

    Lucene全文检索(一)

    Lucene实现全文检索的流程 创建索引 获得文档 原始文档:要基于那些数据来进行搜索,那么这些数据就是原始文档。 搜索引擎:使用爬虫获得原始文档 站内搜索:数据库中的数据。 本地搜索:直接使用io流读取磁盘上的...

Global site tag (gtag.js) - Google Analytics