Ответ 1
Вкратце: я собираюсь рекомендовать ElasticSearch, но позвольте сломать проблему и поговорить о том, как ее реализовать:
Есть несколько частей:
- Извлечение текста из ваших документов для их индексации
- Предоставление этого текста в виде полнотекстового поиска
- Возврат выделенных фрагментов документа
- Зная, где в документе обнаружены эти фрагменты для подкачки
- Вернуть полный документ
Что может предоставить ElasticSearch:
- ElasticSearch (например, Solr) использует Tika для извлечения текста и метаданных из большого количества документов formats
- Это, очевидно, обеспечивает мощный полнотекстовый поиск. Он может быть настроен анализировать каждый документ на соответствующем языке с последующим повышением релевантности определенных полей (например, название более важно, чем контент), ngrams и т.д., т.е. стандартный материал Lucene.
- Он может возвращать выделенные фрагменты для каждого результата поиска
- Он НЕ знает, где находятся эти фрагменты в вашем документе
- Он может хранить исходный документ как attachment, или он может хранить и возвращать извлеченный текст. Но он вернет весь документ, а не страницу.
Вы можете просто отправить весь документ в ElasticSearch в качестве вложения, и вы получите полный текстовый поиск. Но точками крепления являются (4) и (5) выше: зная, где вы находитесь в документе, и возвращаете части документа.
Сохранение отдельных страниц, вероятно, достаточно для ваших целей where-am-I (хотя вы можете в равной степени перейти к уровню абзаца), но вы хотите, чтобы они были сгруппированы таким образом, что документ будет возвращен в результатах поиска, даже если ключевые слова для поиска отображаются на разных страницах.
Сначала указательная часть: сохранение ваших документов в ElasticSearch:
- Используйте Tika (или все, что вам удобно), чтобы извлечь текст из каждого документа. Оставьте это как обычный текст или как HTML, чтобы сохранить некоторое форматирование. (забудьте о XML, не нужно для этого).
- Также извлекайте метаданные для каждого документа: название, авторы, главы, язык, даты и т.д.
- Сохраните исходный документ в своей файловой системе и запишите путь, чтобы впоследствии вы могли его обслуживать.
- В ElasticSearch индексируйте документ doc, содержащий все метаданные и, возможно, список глав
-
Индексируйте каждую страницу как документ "страницы", который содержит:
- A родительское поле, которое содержит идентификатор документа doc (см. ниже "Родительские отношения" )
- Текст
- Номер страницы
- Возможно, заголовок или номер главы
- Любые метаданные, которые вы хотите найти для поиска
Теперь для поиска. Как вы это делаете, зависит от того, как вы хотите представить свои результаты - по страницам или сгруппированы по документам.
Результаты по странице просты. Этот запрос возвращает список совпадающих страниц (каждая страница возвращается полностью) плюс список выделенных фрагментов со страницы:
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, чтобы найти родителя ( "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" из вышеуказанного запроса и выпустите новый запрос, чтобы получить фрагменты из соответствующих документов "страницы":
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
}
'
Или, напротив, дайте "странице" docs идентификатор, состоящий из $doc_id _ $page_num
(например, 123_2), тогда вы можете просто получить эту страницу:
curl -XGET 'http://127.0.0.1:9200/my_index/page/123_2
Родительские отношения:
Обычно в ES (и большинстве решений NoSQL) каждый doc/объект независим - реальных отношений нет. Установив отношения между родителями и дочерними элементами между "доком" и "страницей", ElasticSearch гарантирует, что дочерние документы (т.е. "страница" ) хранятся на том же осколке, что и родительский документ ( "doc" ).
Это позволяет вам запустить top-children-query, который найдет наилучшее соответствие "doc" на основе содержимого "страниц".