综上所述:我将推荐ElasticSearch,但让我们分解问题并讨论如何实现它:
这有几个部分:
- 从文档中提取文本以使其可索引
- 将此文本作为全文搜索提供
- 返回突出显示的文档的 sn-ps
- 了解在文档中的哪些位置可以找到这些 sn-ps
用于寻呼
- 返回完整文档
ElasticSearch 能提供什么:
- ElasticSearch(如 Solr)使用 Tika 从各种文档 formats 中提取文本和元数据
- 很明显,它提供了强大的全文搜索功能。它可以配置
用适当的语言分析每个文档,使用词干、提高某些领域的相关性(例如,标题比内容更重要)、ngram 等,即标准的 Lucene 东西
- 可以为每个搜索结果返回highlighted snippets
- 它不知道这些 sn-ps 出现在您的文档中的什么位置
- 它可以将原始文档存储为attachment,也可以存储并返回提取的文本。但它会返回整个文档,而不是一页。
您可以将整个文档作为附件发送到 ElasticSearch,然后您将获得全文搜索。但症结在于上面的 (4) 和 (5):知道您在文档中的位置,并返回文档的部分内容。
存储单个页面可能足以满足您的 where-am-I 目的(尽管您同样可以进入段落级别),但您希望它们以一种可以在搜索结果中返回文档的方式进行分组,即使搜索关键字出现在不同的页面上。
首先是索引部分:将您的文档存储在 ElasticSearch 中:
- 使用 Tika(或任何您喜欢的工具)从每个文档中提取文本。将其保留为纯文本或 HTML 以保留某些格式。 (忘记 XML,不需要它)。
- 还提取每个文档的元数据:标题、作者、章节、语言、日期等
- 将原始文档存储在您的文件系统中,并记录路径以便您以后提供它
- 在 ElasticSearch 中,索引一个“doc”文档,其中包含所有元数据,可能还有章节列表
-
将每个页面索引为“页面”文档,其中包含:
-
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,它将根据“页面”的内容找到最匹配的“文档”。