45

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

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

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

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

感谢您的任何建议。

4

3 回答 3

123

总结:我将推荐ElasticSearch,但让我们分解问题并讨论如何实现它:

这有几个部分:

  1. 从您的文档中提取文本以使其可索引
  2. 将此文本作为全文搜索提供
  3. 返回文档的突出显示片段
  4. 知道在文档中的哪里可以找到这些片段以允许分页
  5. 返回完整的文档

ElasticSearch 可以提供什么:

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

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

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

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

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

    • 包含“doc”文档 ID的父字段(请参阅下面的“父子关系”)
    • 文本
    • 页码
    • 也许是章节标题或编号
    • 您希望可搜索的任何元数据

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

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

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”文档中获取片段:

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

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

亲子关系:

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

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

于 2012-06-02T09:45:35.780 回答
2

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

于 2012-06-01T18:07:13.830 回答
2

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

于 2012-06-01T18:09:40.577 回答