数千个文档的可search存档的最佳实践(pdf和/或xml)

重新审视一个停滞不前的项目,寻求将现有数千个“旧”文档进行现代化build议,并通过networking提供这些文档的build议。

文档以各种格式存在,有些已经过时:(. docPageMaker ,硬拷贝(OCR), PDF等)。 资金可用于将文档迁移到“现代”格式,许多硬拷贝已经被OCR化为PDF格式 – 我们原本以为PDF是最终的格式,但我们愿意接受build议(XML?) 。

一旦所有文档都采用通用格式,我们希望通过网页界面提供其内容并进行search 。 我们希望灵活性只返回发现search“hit”的整个文档的部分(页面?)(我相信Lucene / elasticsearch使这成为可能?!?)如果内容是全部XML,它可能会更加灵活吗? 如果是的话,如何/在哪里存储的XML? 直接在数据库中,或作为文件系统中的离散文件? 那么在文档中embedded的图像/图表呢?

好奇别人怎么会这样做。 没有“错误的”答案我只是寻找尽可能多的投入来帮助我们继续。

感谢您的任何build议。

总之:我将会推荐ElasticSearch ,但我们先来解决这个问题,并讨论如何实现它:

这有几个部分:

  1. 从文档中提取文本以使其可以索引
  2. 以全文searchforms提供此文本
  3. 返回文档的重点片段
  4. 知道在文档中的哪些片段被发现允许分页
  5. 返回完整的文档

ElasticSearch可以提供什么:

  1. ElasticSearch(如Solr)使用Tika从各种文档格式中提取文本和元数据
  2. 很明显,它提供了强大的全文searchfunction。 它可以被configuration为以适当的语言分析每个文档,提高某些领域的相关性(例如,标题比内容更重要),ngram等,即标准Lucene的东西
  3. 它可以为每个search结果返回突出显示的片段
  4. 它不知道你的文档中哪些代码段出现在哪里
  5. 它可以将原始文档存储为附件 ,也可以存储并返回提取的文本。 但它会返回整个文档,而不是一个页面。

您可以将整个文档作为附件发送到ElasticSearch,并且可以进行全文search。 但是关键点在于(4)和(5):知道你在文档中的位置,并返回文档的某些部分。

存储单个页面可能足以满足您的目的(尽pipe您可以同样下载到段落级别),但是您希望将它们分组,以便在search结果中返回文档,即使search关键字出现在不同的页面上。

首先是索引部分:将文档存储在ElasticSearch中:

  1. 使用Tika(或任何您感觉舒服的)从每个文档中提取文本。 将其保留为纯文本格式,或以HTML格式保留一些格式。 (忘了XML,不需要它)。
  2. 还提取每个文档的元数据:标题,作者,章节,语言,date等
  3. 将原始文档存储在您的文件系统中,并loggingpath,以便以后可以使用
  4. 在ElasticSearch中,索引包含所有元数据的“doc”文档,并可能包含章节列表
  5. 将每个页面索引为“页面”文档,其中包含:

    • 包含“doc”doc的ID的父字段 (请参阅下面的“亲子关系”)
    • 文本
    • 页码
    • 也许章节标题或编号
    • 您想要search的任何元数据

现在进行search。 你如何做到这一点取决于你想如何显示你的结果 – 按页面,或按文档分组。

通过页面的结果很容易。 此查询返回匹配页面的列表(每个页面全部返回)以及页面中突出显示的片段列表:

curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d ' { "query" : { "text" : { "text" : "interesting keywords" } }, "highlight" : { "fields" : { "text" : {} } } } ' 

显示由“doc”分组的结果与来自文本的突出显示有点棘手。 这不能用一个单一的查询来完成,但是一个小小的客户端分组会让你在那里。 一种方法可能是:

步骤1:执行一个顶级子查询来查找其子(“页面”)与查询最匹配的父级(“doc”):

 curl -XGET 'http://127.0.0.1:9200/my_index/doc/_search?pretty=1' -d ' { "query" : { "top_children" : { "query" : { "text" : { "text" : "interesting keywords" } }, "score" : "sum", "type" : "page", "factor" : "5" } } } 

步骤2:从上述查询中收集“doc”ID,并发出一个新的查询,以从匹配的“页面”文档中获取片段:

 curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d ' { "query" : { "filtered" : { "query" : { "text" : { "text" : "interesting keywords" } }, "filter" : { "terms" : { "doc_id" : [ 1,2,3], } } } }, "highlight" : { "fields" : { "text" : {} } } } ' 

步骤3:在您的应用程序中,将来自上述查询的结果按doc分组并显示出来。

使用第二个查询的search结果,您已经拥有了可以显示的页面的全文。 要移到下一个页面,您可以search它:

 curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d ' { "query" : { "constant_score" : { "filter" : { "and" : [ { "term" : { "doc_id" : 1 } }, { "term" : { "page" : 2 } } ] } } }, "size" : 1 } ' 

或者,给“页面”文档一个由$doc_id _ $page_num (例如123_2)组成的ID,那么你可以检索该页面:

 curl -XGET 'http://127.0.0.1:9200/my_index/page/123_2 

亲子关系:

通常,在ES(和大多数NoSQL解决scheme)中,每个文档/对象是独立的 – 没有真正的关系。 通过build立“doc”和“page”之间的父子关系,ElasticSearch确保子文档(即“页面”)与父文档(“doc”)存储在同一个分片上。

这使您可以运行顶级子查询 ,它将根据“页面”的内容find最匹配的“doc”。

我已经构build并维护了一个索引和search70k + PDF文档的应用程序。 我发现它必须从PDF中提取纯文本,将内容存储在SQL中,并使用Lucene索引SQL表。 否则,performance是可怕的。

使用太阳黑子或RSolr或类似的,它处理大多数主要的文件格式。 他们使用Solr / Lucene。