【问题标题】:Best practices for searchable archive of thousands of documents (pdf and/or xml)数千个文档(pdf 和/或 xml)的可搜索存档的最佳实践
【发布时间】:2012-06-06 23:09:44
【问题描述】:

重新审视一个停滞不前的项目,并寻求有关对数千个“旧”文档进行现代化改造并通过网络提供它们的建议。

文档以各种格式存在,有些已过时:(.docPageMaker、硬拷贝 (OCR)、PDF 等)。资金可用于将文档迁移为“现代”格式,并且许多硬拷贝已经被 OCR 转换为 PDF - 我们最初认为 PDF 将是最终格式,但我们愿意接受建议(XML?) .

一旦所有文档都采用了通用格式,我们希望它们的内容可用并且可通过网络界面搜索。我们希望能够灵活地只返回整个文档中找到搜索“命中”的部分(页面?)(我相信 Lucene/elasticsearch 使这成为可能?!?)如果内容都是 XML,会不会更灵活?如果是这样,如何/在哪里存储 XML?直接在数据库中,还是作为文件系统中的离散文件?文档中嵌入的图像/图表怎么样?

很好奇其他人会如何处理这个问题。没有“错误”的答案,我只是在寻找尽可能多的输入来帮助我们继续。

感谢您的建议。

【问题讨论】:

    标签: xml pdf lucene full-text-search elasticsearch


    【解决方案1】:

    综上所述:我将推荐ElasticSearch,但让我们分解问题并讨论如何实现它:

    这有几个部分:

    1. 从文档中提取文本以使其可索引
    2. 将此文本作为全文搜索提供
    3. 返回突出显示的文档的 sn-ps
    4. 了解在文档中的哪些位置可以找到这些 sn-ps 用于寻呼
    5. 返回完整文档

    ElasticSearch 能提供什么:

    1. ElasticSearch(如 Solr)使用 Tika 从各种文档 formats 中提取文本和元数据
    2. 很明显,它提供了强大的全文搜索功能。它可以配置 用适当的语言分析每个文档,使用词干、提高某些领域的相关性(例如,标题比内容更重要)、ngram 等,即标准的 Lucene 东西
    3. 可以为每个搜索结果返回highlighted snippets
    4. 它不知道这些 sn-ps 出现在您的文档中的什么位置
    5. 它可以将原始文档存储为attachment,也可以存储并返回提取的文本。但它会返回整个文档,而不是一页。

    您可以将整个文档作为附件发送到 ElasticSearch,然后您将获得全文搜索。但症结在于上面的 (4) 和 (5):知道您在文档中的位置,并返回文档的部分内容。

    存储单个页面可能足以满足您的 where-am-I 目的(尽管您同样可以进入段落级别),但您希望它们以一种可以在搜索结果中返回文档的方式进行分组,即使搜索关键字出现在不同的页面上。

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

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

      • parent field,其中包含“doc”文档的 ID(请参阅下面的“父子关系”)
      • 正文
      • 页码
      • 可能是章节标题或编号
      • 您希望可搜索的任何元数据

    现在开始搜索。您如何执行此操作取决于您希望如何呈现结果 - 按页面或按文档分组。

    按页面的结果很容易。此查询返回匹配页面的列表(每个页面都返回完整)以及页面中突出显示的 sn-ps 列表:

    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 步:执行 top-children-query 以查找其子级(“page”)与查询最匹配的父级(“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 并发出新查询以从匹配的“page”文档中获取 sn-ps:

    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 分组并显示。

    使用第二个查询的搜索结果,您已经拥有可以显示的页面全文。要移至下一页,您只需搜索即可:

    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 组成的 ID(例如 123_2),然后您就可以检索该页面:

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

    亲子关系:

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

    这使您可以运行top-children-query,它将根据“页面”的内容找到最匹配的“文档”。

    【讨论】:

    • 好的,我会说:“DrTech for President!” ;-) 很棒的答案!希望我能投票更多。谢谢!
    • :) 有趣的是,毕竟我的名字是克林顿 :)
    • 您不知道如何为 PDF 的每个“页面”编制索引?
    • Poppler 工具 poppler.freedesktop.org 在大多数 Linux 发行版上默认可用,速度非常快且非常好。
    • 如果按页面拆分,那么您也可能无法找到跨多个页面拆分的短语,不是吗?
    【解决方案2】:

    我已经构建并维护了一个应用程序,它可以索引和搜索 70k+ PDF 文档。我发现必须从 PDF 中提取纯文本,将内容存储在 SQL 中并使用 Lucene 索引 SQL 表。否则,性能很糟糕。

    【讨论】:

    • 将内容存储在数据库中有什么好处?提取内容(假设您不只是使用 Solr 并跳过手动处理)、索引它并丢弃纯文本内容不是更容易吗?
    • 好吧...我不得不回去看看代码。这就是我正在做的事情。首先,我必须说,我们有一个单独的索引服务器来处理这个功能。过程如下: 1) 从内容服务器上的 PDF 中提取文本 2) 使用类似的目录/文件名将文本存储在 .txt 文件中。 3) 索引文本文件。搜索后,我们能够根据文件路径/命名将结果与原始 PDF 相关联
    • 我看不出在这里使用关系数据库有什么好处。 @Dave,一个更正,您不会丢弃原始文本内容,而是使用搜索引擎(Solr,ES,...)来索引和存储它。然后,在搜索结果中,您只需显示指向原始文件的链接。
    • 我们这样做有两个原因。首先,整体索引时间更快。其次,每个文档对应的数据库中都有相关数据,这样构建全索引比较简单。
    【解决方案3】:

    使用SunspotRSolr 或类似的,它处理大多数主要的文档格式。他们使用 Solr/Lucene。

    【讨论】:

    • 在这种情况下,PDF over XML 的优缺点?在现阶段,我们可以选择采取任何一种方式。我认为 PDF 一开始可能更容易创建,但可能更难维护和“服务”?!?不知道。寻求建议。
    • @Meltemi 我看不出 PDF 会更难提供;文件就是文件。 XML 文件需要格式化,并且您需要在所有格式之间转换为 xml。
    • 文件就是文件,但我们希望一次只“提供”整个文档的一部分。所以我想我们可以将每个 PDF 分解成数百个较小的 PDF,它开始变得笨拙。想知道 XML 是否可以长期使这更容易?!?也许不是。
    • @Meltemi 完全取决于;在不知道确切要求的情况下很难说。 XML DB 有点失宠。内容仍然需要进行格式化/转换,可以根据您的喜好简单或复杂。从原始源到 XML 的转换,同样取决于您的需要,可能是微不足道的,或者基本上是不可能的。使用大数据解决方案并在应用程序级别完全删除文件可能会更好——一个 hBase 行可以有数百万列,每列包含一个段落或其他内容,每一行都是一个文档。大量的解决方案。
    • @D.Newton - “大量解决方案”。好吧,这就是我问这些问题的原因。我正在寻找想法。不试图挑边。至于“要求”,它们与可能性、复杂性和成本有关。基本上我所知道的是,我们希望用户能够查询所有这些报告,并且如果存在“命中”,则包含“命中”的文档的“某些”部分。而且,从那里开始,我相信我们希望用户能够继续翻阅文档。但不要下载整个东西。希望这是有道理的?!?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 2012-10-27
    • 2020-03-16
    • 2012-05-19
    • 2021-07-26
    相关资源
    最近更新 更多