Поделиться через


Узнайте, как работают фильтры коллекций OData в поиске ИИ Azure

В этой статье содержатся сведения о разработчиках, которые пишут расширенные фильтры со сложными лямбда-выражениями. В этой статье объясняется, почему существуют правила для фильтров коллекции, изучая способ выполнения этих фильтров поиска ИИ Azure.

При создании фильтра на полях коллекции в Azure AI Search, вы можете использовать any и all операторы вместе с выражениями лямбда. Лямбда-выражения — это булевы выражения, которые ссылаются на переменную диапазона. В фильтрах, использующих лямбда-выражение, операторы any и all аналогичны циклу for: переменная диапазона действует как переменная цикла, а лямбда-выражение — как тело цикла, в большинстве языков программирования. Переменная цикла принимает текущее значение коллекции во время итерации цикла.

По крайней мере, концептуально это работает именно так. В действительности поиск ИИ Azure реализует фильтры в совершенно другом способе работы for циклов. В идеале эти различия для вас никак не проявляются, за исключением некоторых ситуаций. Фактически при составлении лямбда-выражений необходимо следовать определенным правилам.

Примечание.

Дополнительные сведения о правилах для фильтров коллекций, включая примеры, см. в статье "Устранение неполадок фильтров коллекций OData" в службе "Поиск ИИ Azure".

Почему фильтры коллекции имеют ограничения

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

  1. Для определенных типов данных поддерживаются только определенные операторы. Например, не имеет смысла сравнивать логические значения true и false с помощью операторов lt, gt и т. д.
  2. Поиск по искусственному интеллекту Azure не поддерживает сопоставленный поиск по полям типа Collection(Edm.ComplexType).
  3. Поиск по искусственному интеллекту Azure использует инвертированные индексы для выполнения фильтров по всем типам данных, включая коллекции.

Первая причина — лишь следствие того, как определены язык OData и система типов EDM. Последние две причины подробнее описаны далее в этой статье.

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

    Rooms/any(room: room/Type eq 'Deluxe Room' and room/BaseRate lt 100)

Если фильтрация некоррелированная, то приведенный выше фильтр может вернуть гостиницы, где один номер относится к классу "Люкс", а другой имеет базовый тариф ниже 100. Это не имеет практического смысла, так как оба предложения лямбда-выражения применяются к одной переменной диапазона, а именно room. Именно поэтому такие фильтры коррелируются.

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

    Rooms/Type:deluxe AND Rooms/Description:"city view"

Вы можете снова получить информацию об отелях, где один номер люкс, а другой номер упоминается с "видом на город" в описании. Например, документ ниже с Id и 1 будет соответствовать запросу.

{
  "value": [
    {
      "Id": "1",
      "Rooms": [
        { "Type": "deluxe", "Description": "Large garden view suite" },
        { "Type": "standard", "Description": "Standard city view room" }
      ]
    },
    {
      "Id": "2",
      "Rooms": [
        { "Type": "deluxe", "Description": "Courtyard motel room" }
      ]
    }
  ]
}

Причина заключается в том, что Rooms/Type относится ко всем проанализированным поисковым словам поля Rooms/Type во всем документе, и аналогично для Rooms/Description, как показано в таблицах ниже.

Как Rooms/Type хранится для полнотекстового поиска:

Термин в Rooms/Type Идентификаторы документов
люкс 1, 2
стандарт 1

Как Rooms/Description хранится для полнотекстового поиска:

Термин в Rooms/Description Идентификаторы документов
двор 2
город 1
сад 1
большой 1
мотель 2
комната 1, 2
стандарт 1
suite 1
вид 1

Таким образом, в отличие от фильтра выше, который по сути ищет документы, где для номера поле Type равно "Deluxe Room" и для этого же номера значение BaseRate меньше 100, поисковый запрос пытается найти документы, где Rooms/Type содержит ключевое слово deluxe, а Rooms/Description — фразу city view. В последнем случае не существует понятия отдельных комнат, корреляция полей которых возможна.

Инвертированные индексы и коллекции

Возможно, вы заметили, что существует гораздо меньше ограничений на лямбда-выражения над сложными коллекциями, чем для простых коллекций, таких как Collection(Edm.Int32), Collection(Edm.GeographyPoint)и т. д. Это связано с тем, что поиск ИИ Azure сохраняет сложные коллекции как фактические коллекции поддокументов, а простые коллекции вообще не хранятся в виде коллекций.

Например, рассмотрим фильтруемое поле коллекции строк, например seasons, в индексе для интернет-магазина. Документы, отправляемые в этот индекс, могут выглядеть следующим образом:

{
  "value": [
    {
      "id": "1",
      "name": "Hiking boots",
      "seasons": ["spring", "summer", "fall"]
    },
    {
      "id": "2",
      "name": "Rain jacket",
      "seasons": ["spring", "fall", "winter"]
    },
    {
      "id": "3",
      "name": "Parka",
      "seasons": ["winter"]
    }
  ]
}

Значения поля seasons хранятся в структуре, которая называется инвертированным индексом и выглядит примерно так:

Термин Идентификаторы документов
весна 1, 2
лето 1
падение 1, 2
winter 2, 3

Эта структура данных предназначена для очень быстрого получения ответа на один вопрос: в каком документе есть указанный термин (ключевое слово)? Ответ на этот вопрос больше похож на простую проверку равенства, чем на циклический перебор коллекции. На самом деле, именно поэтому для строковых коллекций поиск ИИ Azure разрешает использовать eq только в качестве оператора сравнения внутри лямбда-выражения для any.

Далее мы рассмотрим, как можно объединить несколько проверок равенства в одной переменной диапазона с or. Такая операция работает благодаря алгебре и дистрибутивности квантификаторов. Выражение

    seasons/any(s: s eq 'winter' or s eq 'fall')

эквивалентно

    seasons/any(s: s eq 'winter') or seasons/any(s: s eq 'fall')

и каждое из двух подвыражений any может эффективно выполняться с использованием инвертированного индекса. Кроме того, благодаря закону отрицания квантификаторов выражение

    seasons/all(s: s ne 'winter' and s ne 'fall')

эквивалентно

    not seasons/any(s: s eq 'winter' or s eq 'fall')

Именно поэтому all можно использовать с ne и and.

Примечание.

Хотя в этом документе не рассматриваются соответствующие подробности, эти же принципы применяются и к проверкам расстояния и пересечения для коллекций геопространственных точек. Именно поэтому в any:

  • к geo.intersects нельзя применить отрицание;
  • geo.distance необходимо сравнивать с помощью lt и le;
  • выражения должны объединяться с помощью or, а не and.

Для all применяются обратные правила.

При фильтрации по коллекциям типов данных, которые поддерживают операторы lt, gt, le и ge, такие как, например, Collection(Edm.Int32), может использоваться более широкий спектр выражений. В частности, в and можно использовать or и any, если базовые выражения сравнения объединяются в диапазоны сравнения с помощью and, которые затем объединяются с помощью or. Такая структура логических выражений называется нормальной дизъюнктивной формой (НДФ) (объединение И с помощью ИЛИ). И наоборот, лямбда-выражения для all этих типов данных должны быть в нормальной конъюнктивной форме (НКФ), также известной как "и из или". Поиск по искусственному интеллекту Azure позволяет выполнять такие сравнения диапазонов, так как он может эффективно выполнять их с помощью инвертированных индексов, так же как и быстрый поиск терминов для строк.

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

  • Внутри any всегда разрешены положительные проверки, такие как проверка равенства, сравнение диапазонов, geo.intersects или сравнение geo.distance с помощью lt или le (при проверке расстояния близость можно представить как аналог равенства).
  • Внутри any всегда разрешено or. and можно использовать только для типов данных, которые поддерживают проверку диапазона, и только в формате "объединение И с помощью ИЛИ" (НДФ).
  • Внутри allправила изменяются на противоположные. Разрешены только отрицательные проверки , всегда можно использовать and и использовать or только для проверок диапазона, выраженных как AND OR (CNF).

На практике именно эти типы фильтров обычно и используются. При этом важно понимать границы возможного.

Конкретные примеры того, какие виды фильтров разрешены, а какие нет, см. в статье Составление допустимых фильтров коллекции.

Следующие шаги