КатегорииElasticsearchУроки

Elasticsearch — Урок 6.3 Поиск

Одной из самых мощных функций Elasticsearch является DSL (Domain specific Language) или язык запросов. Он очень выразителен и может использоваться для определения фильтров, запросов, сортировки, разбивки на страницы и агрегирования в одном запросе. Чтобы выполнить поисковый запрос, используется HTTP-запрос к _search Api. Индекс и тип, по которому должен выполняться запрос, указывается в URL-адресе. Индекс и тип являются необязательными. Если индекс / тип не указан, Elasticsearch выполняет запрос по всем индексам в кластере. Поисковый запрос в Elasticsearch может быть выполнен двумя разными способами:

  • Передавая запрос поиска в качестве параметров запроса.
  • Передавая запрос поиска в теле запроса.

Простой поисковый запрос с использованием параметров запроса:

GET example6/product/_search?q=product_name:курткa

Не забудьте декодировать кириллицу.

Простые запросы могут быть выполнены с использованием параметров URL-адреса. Все, кроме простого запроса, как в приведенном выше примере, должно быть передано как тело запроса. Предыдущий запрос, переданный как тело запроса, выглядит следующим образом.

POST example6/product/_search 
{
   "query": {
     "match": {
       "product_name" : "курткa"
     }
   }
 }

Предыдущий запрос выполняется по example6 индексу и типу product. Запрос также может выполняться одновременно по нескольким индексами/типами, как показано здесь:

POST example5,example6/product,product_reviews/_search 
{
   "query": {
     "match": {
       "product_name" : "куртка"
     }
   }
 }

Обратите внимание, что в двух предыдущих случаях мы использовали HTTP POST, так как большинство браузеров не поддерживает передачу тела при GET запросе.

Здесь показана базовая структура тела запроса:

{ 
   "size"    : // Количество документов в ответе. По умолчанию 10.
   "from"    : // Смещение результатов
   "timeout" : // Тайм-аут запроса. По умолчанию его нет.
   "_source" : // Поля которые попадут в ответ.
   "query"   : { // Запрос } 
   "aggs"    : { // Агрегация } 
   "sort"    : { // Сортировка результата } 
}

Мы обсудим каждый раздел запроса, например, разбивку на страницы, сортировку и т. д. чуть позже. Пока посмотрим на структуру ответа:

{
  "took"     : // Время потраченное на получение данных
  "timed_out": // Был ли превышен тайм-аут, если он был задан для запроса.
  "_shards": { 
    "total": // Количество осколков задействованных при запросе
    "successful": // Количество осколков вернувших успешный результат
    "failed": // Количество осколков с ошибкой
  },
  "hits": {
   "total": // Общее количество документов
   "max_score": // Максимальный балл всех документов
   "hits": [
      // Список документов
    ]
   }
 }

Основной запрос (поиск точного значения)

Основной запрос в Elasticsearch — это term (термин) запрос. Он очень простой и может использоваться для запроса чисел, бинарных значений, дат и текста. Данный запрос используется для поиска одного термина в инвертированном индексе. Запрос соответствия, с другой стороны, заботится о отображении и вызывает термин запрос внутри.

Запрос выглядит следующим образом:

POST example6/product/_search 
{
   "query": {
     "term": {
       "product_name" : "куртк"
     }
   }
 }

Обратите внимание, что используем основу слова без окончания куртк, а не куртка. Потому что в индексе храниться, как раз слово без окончания и данные запрос будет искать точное соответствие.

Термин-запрос отлично подходит для одного термина. Чтобы запросить более одного термина, мы должны использовать запрос терминов. Если документ соответствует любому из условий, он попадает в результат. Например, мы хотим найти все документы, которые содержат куртк или кожан в имени продукта. Запрос будет выглядеть следующим образом:

POST chapter6/_search 
{
   "query": {
     "terms": {
       "product_name" : ["куртк","кожан"]
     }
   }
 }

Ответ на запрос следующий:

{
   ....
   "hits": {
     "total": 3,
     "max_score": 1,
     "hits": [
       {
         "_index": "example6",
         "_type": "product",
         "_id": "2",
         "_score": 1,
         "_source": {
           "product_name": "Мужская водостойкая куртка",
           "description": "Обеспечивает комфорт во время езды на велосипеде",
           "unit_price": 69.99,
           "reviews": 5,
           "release_date": "2017-03-02"
         }
       },
       {
         "_index": "example6",
         "_type": "product",
         "_id": "1",
         "_score": 1,
         "_source": {
           "product_name": "Мужская качественная кожаная куртка",
           "description": "Лучший выбор. Всесезонная кожаная куртка",
           "unit_price": 79.99,
           "reviews": 250,
           "release_date": "2016-08-16"
         }
       },
       {
         "_index": "example6",
         "_type": "product",
         "_id": "3",
         "_score": 1,
         "_source": {
           "product_name": "Куртка женская шерстяная",
           "description": "Согреет вас зимой",
           "unit_price": 59.99,
           "reviews": 10,
           "release_date": "2018-01-15"
         }
       }
     ]
   }
 }

Пагинация

В предыдущем разделе мы узнали об основном запросе в Elasticsearch. В этом разделе мы обсудим, как ограничить количество результатов в ответе. Пагинация поддерживается с помощью полей from и size полей в запросе. Размер страницы по умолчанию 10. Например:

 #Пагинация
 POST example6/_search
 {
   "from" : 0,
   "size" : 2,
   "query" : {
     "match" : { 
       "product_name" : "куртка"
     }
   }
 }

В предыдущем запросе мы получаем лучшие 2 документа. Если размер страницы 10, чтобы получить третью страницу, надо from установить 20 и size 10. Разбивка страниц в распределенной системе очень дорога по сравнению с традиционной базой данных. Данные, принадлежащие индексу, распространяются по нескольким осколкам. Например, если индекс состоит из пяти осколков, чтобы получить третью страницу, все пять осколков отправляют тридцать результатов в координационный узел. Координационный узел, в свою очередь, сортирует результаты всех осколков и передает результаты от 20 до 30. Чем больше номер страницы, тем дороже запрос.

Сортировка на основе существующих полей

Elasticsearch — полнотекстовая поисковая система, и результаты упорядочиваются по релевантности каждого документа в запросе. Про релевантность мы поговорим чуть позже. В этом разделе мы обсудим только сортировку результатов на основе существующих полей в документе. Вы можете отсортировать результаты на основе одного или нескольких полей. Например, мы хотим сортировать продукт по цене. Например:

#Сортировка
POST example6/_search
 {
   "query": {
     "match": {
       "product_name": "куртка"
     }
   },
   "sort": {
     "unit_price": {
       "order": "desc"
     }
   }
 }

Так же можно сортировать по нескольким полям сразу. Напирмер:

POST example6/_search
 {
   "query": {
     "match": {
       "product_name": "куртка"
     }
   },
   "sort": [
     {
       "unit_price": {
         "order": "desc"
       }
     },
     {
       "reviews": {
         "order": "desc"
       }
     }
   ]
 }

При сортировке с использованием нескольких полей результаты сначала сортируются по первому полю, а затем по другим полям. Если значение первого поля уникальны, следующие поля не будет иметь эффекта. Только если два или более документов имеют одно и то же значение первого поля, второе поле будет использоваться для сортировки.

Выбор полей в ответе

Когда вы выполняете запрос, по умолчанию _source в ответ возвращается исходный документ JSON. Иногда мы хотим включать только определенные поля в ответ, особенно когда документ большой или имеет много полей.

Это можно сделать, указав поля в _source во время запроса. Значения полей извлекаются из документа _source. Если источник отключен с помощью сопоставления, это не сработает. Ответ следующего запроса будет иметь только product_name в ответе:

POST example6/product/_search
 {
   "_source": [
     "product_name"
   ],
   "query": {
     "term": {
       "product_name": "куртк"
     }
   }
 }

Ответ на предыдущий запрос выглядит следующим образом:

{
   ....
   "hits": {
     "total": 3,
     "max_score": 0.2876821,
     "hits": [
       {
         "_index": "example6",
         "_type": "product",
         "_id": "2",
         "_score": 0.2876821,
         "_source": {
           "product_name": "Мужская водостойкая куртка"
         }
       },
       {
         "_index": "example6",
         "_type": "product",
         "_id": "1",
         "_score": 0.2876821,
         "_source": {
           "product_name": "Мужская качественная кожаная куртка"
         }
       },
       {
         "_index": "example6",
         "_type": "product",
         "_id": "3",
         "_score": 0.2876821,
         "_source": {
           "product_name": "Куртка женская шерстяная"
         }
       }
     ]
   }
 }

_source Поле также поддерживает подстановочные знаки для имен полей. Мы можем сказать Elasticsearch включить поля, которые начинаются с pr, как показано здесь:

POST example6/product/_search
 {
   "_source": [
     "pr*"
   ],
   "query": {
     "term": {
       "product_name": "куртк"
     }
   }
 }

Например, наряду с ценой продукта мы хотим вернуть цену, включая налог. Если налог с продаж 10%, мы можем использовать поле сценария, чтобы рассчитать цену с налогом и вернуть значение поля с результатом. Запрос выглядит следующим образом:

POST example6/product/_search 
{
   "_source": [], 
   "query": {
     "match_all": {}
   },
   "script_fields": {
     "price_including_tax": {
       "script": {
          "source" : "params['_source']['unit_price'] * 1.1"
       }
     }
   }
 }

Язык сценариев по умолчанию для Elasticsearch называется Painless. Используя язык Painless, мы можем получить доступ к _source документу для извлечения unit_price и умножения на 1.1 чтобы рассчитать цену, включая налог. Ответ на предыдущий запрос выглядит следующим образом:

{
   ....
   "hits": {
     "total": 3,
     "max_score": 1,
     "hits": [
       {
         "_index": "example6",
         "_type": "product",
         "_id": "2",
         "_score": 1,
         "_source": {
           "product_name": "Мужская водостойкая куртка",
           "description": "Обеспечивает комфорт во время езды на велосипеде",
           "unit_price": 69.99,
           "reviews": 5,
           "release_date": "2017-03-02"
       },
       "fields": {
         "price_including_tax": [
           76.989
         ]
       }
     },
     {
       "_index": "example6",
       "_type": "product",
       "_id": "1",
       "_score": 1,
       "_source": {
         "product_name": "Мужская качественная кожаная куртка",
         "description": "Лучший выбор. Всесезонная кожаная куртка",
         "unit_price": 79.99,
         "reviews": 250,
         "release_date": "2016-08-16"
       },
       "fields": {
         "price_including_tax": [
           87.989
         ]
       }
     },
     {
       "_index": "example6",
       "_type": "product",
       "_id": "3",
       "_score": 1,
       "_source": {
         "product_name": "Куртка женская шерстяная",
         "description": "Согреет вас зимой",
         "unit_price": 59.99,
         "reviews": 10,
         "release_date": "2018-01-15"
       },
       "fields": {
         "price_including_tax": [
            65.989
         ]
       }
     }
   ]
}

Ответ включает первоначальную цену и цену, включая налог. Хотя сценарии очень эффективны, каждый документ должен анализироваться для извлечения значения поля, которое может иметь стоимость исполнения.

Запрос на основе диапазона

Запрос диапазона может использоваться для чисел и дат для запроса всех документов в заданном диапазоне.

Запрос диапазона поддерживает следующие операторы:

  • lt: < меньше, чем оператор
  • gt: > больше оператора
  • lte: <= меньше или равно оператору
  • gte: >= больше или равно оператору

SQL запрос всех продуктов больше 70 и меньше, чем 100:

select * from product where unitprice > 70 and unitprice <= 100

Предыдущий запрос в Elasticsearch:

POST example6/_search 
{
   "query": {
     "range": {
     "unit_price": {
         "gt": 70,
         "lte": 100
       }
     }
   }
 }

Аналогично, запрос диапазона может также использоваться в датах, как показано ниже:

POST example6/_search
{
   "query": {
     "range": {
       "release_date": {
         "gt": "2017-01-01",
         "lte": "now"
       }
     }
   }
 }

Работа с датами

Elasticsearch упрощает работу с датами. Он поддерживает часовые пояса, добавляет и удаляет часы / дни / месяцы / годы на определенную дату. Он также поддерживает, now как показано в следующем примере, который возвращает текущую метку времени. Например, чтобы найти все документы, которые были изменены в течение последнего часа, запрос показан здесь:

POST example6/_search
 {
   "query": {
     "range": {
       "release_date": {
         "gt": "now-1h"
       }
     }
   }
 }

Строку даты или now следующую за ней математику можно также указать следующим образом. || символ отделяет математическую операцию и дату:

POST example6/_search 
{
   "query": {
     "range": {
       "release_date": {
         "gt": "2017-01-11T20:00:00||+1M"
       }
     }
   }
 }

Ниже приведены примеры различных операций с датами:

  • now+1M: Добавляет один месяц. Вы также можете использовать другую единицу даты, такую ​​как час, день, месяц, год и т. д.
  • 2017-02-01||-1M: Вычитает один месяц
  • now/d: Используется для округления до ближайшего дня
  • now+1M-1d/d:  Добавляет один месяц, вычитает один день и округляется до ближайшего дня
  • 2017-02-01||+1H/d:  Добавляет один час к 2017-02-01 и округляет до ближайшего дня

Поддерживаются следующие единицы времени:

Единица времени Описание
y год
M месяц
w неделя
d день
H час
m минута
s секунда

Проанализированы или не проанализированы поля

Для хранения текстовых данных, Elasticsearch поддерживает два типа:

  • Text: Текстовые поля анализируются (разбиваются на отдельные термины) до их хранения в инвертированном индексе
  • Keyword: Поля сохраняются, как есть в инвертированном индексе

Чтобы лучше объяснить типы сопоставления Text и Keyword, давай их оба и попробуем:

PUT example6_fruit
{
  "mappings": { 
    "items": {
      "properties": {
        "product_id": {
          "type": "integer"
        },
        "product_name": {
          "type": "text",
          "analyzer": "russian",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        }
      } 
    }
  }
}

Давайте проиндексируем документ:

PUT example6_fruit/items/1 
{
   "product_id": "1",
   "product_name": "Сочное красное яблоко"
}

Поскольку product_name  имеет текстовое отображение, оно будет проанализировано до того, как оно будет сохранено в инвертированном индексе. Из предыдущего примера, «Сочное красное яблоко» анализируется с помощью анализатора, он разбивается на сочн, красн, яблок токены.

Можно это проверить используя Analyse API:

POST /_analyze
{
  "analyzer": "russian",
  "text": "Сочное красное яблоко"
}

Вы увидите ответ, подобный следующему:

{
 "tokens": [
   {
     "token": "сочн",
     "start_offset": 0,
     "end_offset": 6,
     "type": "<ALPHANUM>",
     "position": 0
   },
   {
     "token": "красн",
     "start_offset": 7,
     "end_offset": 14,
     "type": "<ALPHANUM>",
     "position": 1
   },
   {
     "token": "яблок",
     "start_offset": 15,
     "end_offset": 21,
     "type": "<ALPHANUM>",
     "position": 2
   }
  ]
}

Однако в product_name.keyword будет точное значение (Сочное красное яблоко), поскольку тип отображения является keyword. Давайте рассмотрим один и тот же запрос по product_name и product_name.keyword, чтобы увидеть, разницу:

POST example6_fruit/items/_search
 {
   "query": {
     "term": {
       "product_name": "яблок"
     }
   }
 }

В предыдущем запросе мы используем термин запрос он будет искать в инвертированном индексе product_name и возвращать документы, содержащие термин яблок. В ответе будет указан документ с названием продукта «Сочное красное яблоко».

Давайте запустим тот же запрос для product_name.keyword, который не анализируется:

POST example6_fruit/items/_search {
   "query": {
     "term": {
        "product_name.keyword": "яблок"
     }
   }
 }

Ответ будет пустой, так как в инвертированном индексе поле product_name.keyword не содержит записи яблок. Поскольку product_name.keyword не анализируется, инвертированный индекс будет иметь «Сочное красное яблоко». Повторим запрос:

POST example6_fruit/items/_search
 {
   "query": {
     "term": {
       "product_name.keyword": "Сочное красное яблоко"
     }
   }
 }

Вы увидите ответ, подобный следующему:

{
   .....
   "hits": {
     "total": 1,
     "max_score": 0.2876821,
     "hits": [
       {
         "_index": "example6_fruit",
         "_type": "items",
         "_id": "1",
         "_score": 0.2876821,
         "_source": {
           "product_id": "1",
           "product_name": "Сочное красное яблоко"
         }
       }
     ]
   }
 }

Ответ будет содержать только один документ, так как это точное совпадение. Аналогичным образом, если вы запрашиваете яблоко для product_name.keyword, вы не получите никаких результатов, поскольку в инвертированном индексе product_name.keyword содержит только «Сочное красное яблоко» целиком.

Запрос match

Термин-запрос является наиболее часто используемым запросом и может использоваться для запроса чисел, булевых, дат и текста. Запрос ищет точный поиск в инвертированном индексе. Он является низкоуровневым запросом. Вы можете использовать термин в анализируемых или неанализированных полях. Он просто ищет термин в инвертированном индексе.

Запрос соответствия, в отличие от термина, понимает сопоставление полей. Входной запрос разбивается с помощью анализатора. Возьмем пример и обсудим, как работает запрос соответствия:

POST example6/product/_search
 {
   "query": {
     "term": {
       "product_name": "Куртка женская"
     }
   }
 }

Этот запрос не найдет ни одного документа, поскольку запрос терминов ищет полностью «Куртка женская» в инвертированном индексе и не находит совпадений. Теперь, если мы запустим этот же запрос, используя match вместо слова term, как показано ниже, вы увидите три результата:

POST example6/product/_search
 {
   "query": {
     "match": {
       "product_name": "Куртка женская"
     }
   }
 }

При использовании запроса соответствия происходит следующее:

  • Сначала запрос соответствия проверяет product_name сопоставление полей и узнает, что это анализируемое поле
  • Он использует тот же анализатор, который используется при индексировании (если во время сопоставления анализатор не задан, стандартный анализатор по умолчанию), чтобы разбить термин запроса на токены
  • Анализируя «Куртка женская» термин запроса, он будет преобразован в куртк и женск  токены (термины)
  • Затем он запускает фильтр терминов для каждого токена и объединяет результаты. Если запрос соответствия выполняется в непроанализированном поле, он будет запускать фильтр терминов без анализа запроса

Ответ на запрос соответствия выглядит следующим образом:

{
   ....
   "hits": {
     "total": 3,
     "max_score": 0.5753642,
     "hits": [
      {
        "_index": "example6",
        "_type": "product",
        "_id": "3",
        "_score": 0.5753642,
        "_source": {
          "product_name": "Куртка женская шерстяная",
          "description": "Согреет вас зимой",
          "unit_price": 59.99,
          "reviews": 10,
          "release_date": "2018-01-15"
        }
      },
      {
        "_index": "example6",
        "_type": "product",
        "_id": "2",
        "_score": 0.2876821,
        "_source": {
          "product_name": "Мужская водостойкая куртка",
          "description": "Обеспечивает комфорт во время езды на велосипеде",
          "unit_price": 69.99,
          "reviews": 5,
          "release_date": "2017-03-02"
        }
      },
      {
        "_index": "example6",
        "_type": "product",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "product_name": "Мужская качественная кожаная куртка",
          "description": "Лучший выбор. Всесезонная кожаная куртка",
          "unit_price": 79.99,
          "reviews": 250,
          "release_date": "2016-08-16"
        }
      }
    ]
  }
}

Вы можете видеть из ответа, что наряду с женской шерстяной курткой два других документа также соответствовали запросу. Это потому, что по умолчанию запрос соответствия преобразуется в запрос как указанно ниже:

{
   "query": {
     "bool": {
       "should": [
         {
           "term": {
             "product_name": "куртк"
           }
         },
         {
           "term": {
             "product_name": "женск"
           }
         }
       ]
     }
   }
 }

Если вы хотите только документы, содержащие все условия, вы можете изменить запрос следующим образом:

POST example6/product/_search
 {
   "query": {
     "match": {
       "product_name": {
         "query": "Куртка женская",
         "operator": "and"
       }
     }
   }
 }

Ответ на предыдущий запрос выглядит следующим образом:

{
   ....
   "hits": {
     "total": 1,
     "max_score": 0.5753642,
     "hits": [
       {
         "_index": "example6",
         "_type": "product",
         "_id": "3",
         "_score": 0.5753642,
         "_source": {
           "product_name": "Куртка женская шерстяная",
           "description": "Согреет вас зимой",
           "unit_price": 59.99,
           "reviews": 10,
           "release_date": "2018-01-15"
         }
       }
     ]
   }
}

Запрос соответствия фразе

Этот запрос аналогичен запросу match, но используется для запроса текстовых фраз. Согласование фразы необходимо, когда порядок слов важен. Согласованы только документы, содержащие слова в том же порядке, что и в поиске.

Во время процесса индексирования наряду с токеном положение слова в тексте также сохраняется в инвертированном индексе. Затем информация о местоположении используется в запросе соответствия фразе. Например:

 #Match Phrase
 POST example6/_search
 {
   "query": {
     "match_phrase": {
       "description": "Всесезонная куртка"
     }
   }
 }

Предыдущий запрос не будет соответствовать ни одному продукту. Продукт в котором встречаются данные слова содержит в описании «Лучший выбор. Всесезонная кожаная куртка», как вы заметили слова «Всесезонная» и «куртка» разделяет слово «кожаная». То есть для поиска используется достаточно жесткий принцип, слова должны следовать за друг другом. Но можно уменьшить жесткость задав параметр slop, который задает сколько слов может вклиниваться между словами в поисковой фразе. Предыдущий запрос со значением slop 1 выглядит следующим образом:

 #Match Phrase
 POST example6/_search
 {
   "query": {
     "match_phrase": {
       "description": {
         "query": "All season jacket",
         "slop": 1
       }
     }
   }
 }

Ответ на предыдущий запрос выглядит следующим образом:

{
   ....
   "hits": {
     "total": 1,
     "max_score": 0.37229446,
     "hits": [
       {
         "_index": "example6",
         "_type": "product",
         "_id": "1",
         "_score": 0.37229446,
         "_source": {
           "product_name": "Мужская качественная кожаная куртка",
           "description": "Лучший выбор. Всесезонная кожаная куртка",
           "unit_price": 79.99,
           "reviews": 250,
           "release_date": "2016-08-16" 
         }
       }
     ]
   }
}

Префикс и префикс запроса соответствия фразе

Префиксный запрос используется для поиска всех документов с заданным префиксом. Подобно запросу термина, префиксный запрос является низкоуровневым запросом и не учитывает сопоставление полей. Условия в инвертированном индексе сортируются в алфавитном порядке, а префиксный запрос сканирует словарь терминов, чтобы найти соответствующие термины. Например, если мы хотим найти все продукты, которые начинаются с префикса ку, запрос выглядит следующим образом:

 #Prefix
 POST example6/_search
 {
   "query": {
     "prefix": {
       "product_name": "ку"
     }
   }
 }

В префиксном запросе соответствия фразе сочетаются запросы соответствие фразе и префиксный. Согласование префикса выполняется только на последнем члене запроса. Количество префиксов, которые собираются из инвертированного индекса, можно контролировать с помощью max_expansions параметра, по умолчанию 50. Запрос префикса соответствия фразе выглядит следующим образом:

 #Match Phrase Prefix
 POST example6/_search
 {
   "query": {
     "match_phrase_prefix": {
       "product_name": {
         "query": "кожаная ку",
         "max_expansions": 10  
       }
     }
   }
 }

Подстановочный знак и регулярные выражения

Подстановочный запрос используется для сопоставления подстановочных знаков на входе поиска. Подобно фразовому запросу, термин запроса не анализируется. Поддерживаются следующие подстановочные знаки:

Знак Описание
* Соответствует любому количеству символов
? Соответствует одному символу

Пример запроса выглядит следующим образом для токена куртк (помните что запрос низкоуровневый работает с токенами):

 #Wildcard
 POST example6/product/_search
 {
   "query": {
     "wildcard" : {
       "product_name" : "к*т?"
     }
   }
 }

Запрос Regex используется для поиска документов на основе соответствия регулярному выражению. Запрос регулярного выражения может быть очень дорогим в зависимости от префикса. Пример запроса выглядит следующим образом:

 #Regular Expression
 POST example6/product/_search
 {
   "query": {
     "regexp" : {
        "product_name" : "курт."
     }
   }
 }

Существующие и отсутствующие запросы

Существующий запрос используется для поиска документа, который содержит ненулевое значение для поля, указанного в запросе. Например, если вы хотите найти весь документ, содержащий обзоры продуктов. Запрос выглядит следующим образом:

 #Exists
 POST example6/product/_search
 {
   "query": {
     "exists" : {
       "field" : "reviews"
     }
   }
 }

Если вы хотите найти все документы, которые не содержат reviews, мы можем использовать must_not. Запрос выглядит следующим образом:

 #Missing
 POST example6/product/_search
 {
   "query": {
     "bool": {
       "must_not": {
         "exists": {
           "field": "reviews"
         }
       }
     }
   }
 }

Использование нескольких запросов

В мире реляционных баз данных мы можем использовать and/or  для объединения разных условий. В Elasticsearch мы будем использовать bool для достижения того же. Bool очень мощный; может иметь под запросы bool. Базовая структура запроса bool показана здесь:

{
   "query": {
     "bool": {
       "must": [],
       "must_not": [],
       "should": [],
       "filter": []
     }
   }
 }

must часть запроса содержит все запросы andmust_not раздел содержит все not запросы. should раздел содержит or запросы. filter содержит запросы на соответствие, но без учета ранжирования. Например, SQL запрос для поиска куртки по цене менее 100$:

select * from product where product_name like '%куртка%' and unit_price < 100

Примерно тоже в Elasticsearch:

POST example6/_search
{
   "query": {
     "bool": {
       "must": [
         {
           "match": {
             "product_name": "jacket"
           }
         },
         {
           "range": {
             "unit_price": {
               "lt": "100"
             }
           }
         }
       ]
     }
   }
 }

Поскольку условия завернуты в must, возвращаются только документы, которые удовлетворяют обоим условиям. Запрос bool также поддерживает вложенность других запросов bool, например, пользователь ищет водостойкую куртку, или кожаную стойкостью меньше 100. SQL-запрос:

select * from Product where 
product_name like '%водостойкая куртка%' 
or 
(product_name like '%кожаная куртка%' and unit_price < 100)

В предыдущем SQL-запросе or условие становится should, и and условие становится must. Elasticsearch:

POST example6/_search
 {
   "query": {
     "bool": {
       "should": [ # or
         {
           "match": {
             "product_name": {
               "query": "водостойкая куртка",
               "operator": "and"
             }
           }
         },
         {
           "bool": {
             "must": [ # and
               {
                 "match": {
                   "product_name": {
                     "query": "кожаная куртка",
                     "operator": "and"
                   }
                 }
               },
               {
                 "range": {
                   "unit_price": {
                     "lte": "100"
                   }
                 }
               }
             ]
           }
         }
       ]
     }
   }
 }

Ответ:

{
   ....
  "hits": {
    "total": 2,
    "max_score": 1.5753641,
    "hits": [
      {
        "_index": "example6",
        "_type": "product",
        "_id": "1",
        "_score": 1.5753641,
        "_source": {
          "product_name": "Мужская качественная кожаная куртка",
          "description": "Лучший выбор. Всесезонная кожаная куртка",
          "unit_price": 79.99,
          "reviews": 250,
          "release_date": "2016-08-16"
        }
      },
      {
        "_index": "example6",
        "_type": "product",
        "_id": "2",
        "_score": 0.5753642,
        "_source": {
          "product_name": "Мужская водостойкая куртка",
          "description": "Обеспечивает комфорт во время езды на велосипеде",
          "unit_price": 69.99,
          "reviews": 5,
          "release_date": "2017-03-02"
        }
      }
    ]
  }
}

Ранжирование — это дорогостоящая операция, и если подсчет не является обязательным, отдельные запросы могут быть обернуты в filter, или весь запрос bool также может быть обернут constant_score. Мы подробно обсудим ранжирование в следующих разделах. А пока пример constant_score:

{
   "query": {
     "constant_score": {
       "filter": {
         "bool": {
           "must": [],
           "must_not": [],
           "should": []
         }
       }
     }
   }
 }

Маршрутизация

Ранее в 3 уроке мы обсуждали, как настроить отображение использовать маршрутизацию и как индексировать документ , используя значение маршрутизации. В этом разделе мы обсудим использование значений маршрутизации при запуске поискового запроса.

Маршрутизация может быть очень удобной, если у вас есть способ группировать ваши данные. Например, вы можете проиндексировать все документы, принадлежащие одному и тому же пользователю, используя user_id как значение маршрутизации. Все документы, индексированные с использованием одного user_id, попадают в один и тот же осколок. При выполнении запроса, если вы укажете значение маршрутизации, Elasticsearch выполнит запрос поиска только по одному осколку. Это особенно имеет смысл, если в индексе много осколков. Вместо выполнения запроса на каждом осколке индекса запрос на поиск может выполняться только на одном осколке.

Потенциальные сбои в использовании маршрутизации — это горячие точки. Если некоторые пользователи в системе намного больше других, есть вероятность, что все крупные пользователи могут оказаться в одном и том же осколке. Это приводит к неравномерному распределению данных и недоиспользованию ресурсов и потенциально может привести к разрушению всего кластера.

Значение маршрутизации может быть передано как часть запроса, как показано ниже:

POST example6/order/_search?routing=user1
{
   "query": {
     "match": {
       "shipping_status": "почтой"
     }
   }
 }

Маршрутизация должна использоваться только в том случае, если есть способ логически делить ваши данные, избегая горячих точек.

Отладочный поисковый запрос

Если вы когда-либо задавались вопросом, почему документ не соответствует запросу, сначала проверьте сопоставление типа с помощью API-интерфейса GET, как показано здесь:

GET example6/product/_mapping

Если отображение правильное, убедитесь, что значение текстового поля проанализировано правильно. Вы можете использовать API анализа для проверки списка токенов, как показано ниже:

GET _analyze?analyzer=russian&text=Маша+любит+вареники

Если анализатор ведет себя так, как ожидалось, убедитесь, что вы используете правильный тип запроса. Например, используя совпадение вместо запроса термина и так далее.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *