Объединенный не-вложенный и вложенный запрос в Elasticsearch
Я хочу использовать ES для поиска книг. Поэтому я решил поместить имя автора и заголовок (как вложенный документ) в индекс следующим образом:
curl -XPUT localhost:9200/library/search_books/1 -d'{
"author": "one",
"books": [
{
"title": "two",
},
{
"title": "three",
}
]
}'
То, что я не понимаю, это:
Как мне нужно структурировать поисковый запрос, чтобы найти только две книги при поиске "одного двух" и ничего не найти при поиске "двух трех" и всех книг при поиске "одного"?
Ответы
Ответ 1
Я нашел ответ в этом сообщении: Fun With Elasticsearch Children и Nested Documents. Вложенным документом является ключ. Отображение:
{
"book":{
"properties": {
"tags": { "type": "multi_field",
"fields": {
"tags": { "type": "string", "store":"yes", "index": "analyzed" },
"facet": { "type": "string", "store":"yes", "index": "not_analyzed" }
}
},
"editions": { "type": "nested",
"properties": {
"title_author": { "type": "string", "store": "yes", "index": "analyzed" },
"title": { "type": "string", "store": "yes", "index": "analyzed" }
}
}
}
}
}
Документ:
"tags": ["novel", "crime"],
"editions": [
{
"title": "two",
"title_author": "two one"
},
{
"title": "three",
"title_author": "three one"
}
]
Теперь я могу выполнить поиск как:
{
"query": {
"bool": {
"should": [
{
"nested": {
"path": "editions",
"query": {
"match": {
"editions.title_author": {
"query": "one two",
"operator": "and"
}
}
}
}
}
]
}
}
}
И если бы вы искали "два три", я бы не получил матч. Я бы получил один с "один два" или "один три". В версии 1.1.0 будет еще один вариант с запросом multi_match и параметром cross_fields, который позволит не повторять заголовок и добавлять только имя автора для каждого вложенного документа. Это уменьшит индекс.
Ответ 2
Возможно, что-то вроде этого?
{
"query":{
"bool":{
"must":[
{
"term":{
"author":"one"
}
},
{
"nested":{
"path":"books",
"query":{
"term":{
"books.title":"two"
}
}
}
}
]
}
}
}
Этот запрос в основном говорит, что документ должен иметь author: one
и books.title: two
. Вы можете легко перенастроить этот запрос. Например, если вы просто хотите найти авторов, удалите вложенную часть. Если вы хотите другую книгу, измените вложенный и т.д. И т.д.
Предполагается, что вы используете фактические вложенные документы, а не внутренние объекты. Для внутренних объектов вы можете просто использовать полностью определенные пути без специального вложенного запроса.
Edit1. Вы могли бы выполнить это с умным повышением в течение времени индекса, хотя это будет только приблизительное решение. Если "автор" сильно усилен, он будет сортировать более высокий, чем совпадение, только заголовок, даже если заголовок соответствует обеим частям запроса. Затем вы можете использовать обрезание min_score, чтобы предотвратить их отображение.
Его единственное свободное приближение, так как некоторые могут проскользнуть. Это может также делать странные вещи для общей сортировки между "правильными" совпадениями.
Edit2: Обновлено с помощью query_string для предоставления опции "одного входа":
{
"query":{
"query_string" : {
"query" : "+author:one +books.title:two"
}
}
}
Предположим, что вы используете стандартные "внутренние объекты" по умолчанию. Если у вас есть реальные типы Nested, query_string становится намного сложнее:
{
"query":{
"query_string" : {
"query" : "+author:one +BlockJoinQuery (filtered(books.title:two)->cache(_type:__books))"
}
}
}
Огромное ограничение Я не тестировал ни одну из этих двух query_strings, поэтому они могут быть не совсем точными. Но они показывают, что синтаксис Lucene не слишком дружелюбен.
Edit3 - Это моя лучшая идея:
Подумав об этом, лучшим решением может быть индексирование специального поля, которое объединяет автора и название книги. Что-то вроде этого:
{
"author": "one",
"books": [
{
"title": "two",
},
{
"title": "three",
}
],
"author_book": [ "one two", "one three" ]
}
Затем во время поиска вы можете выполнить точные совпадения Term на author_book
:
{
"query" : {
"term" : {
"author_book" : "one two"
}
}
}