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


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

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

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

Таково сходство на уровне концепции. В действительности поиск ИИ 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 ИД документов
deluxe 1, 2
standard 1

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

Ключевое слово в Rooms/Description ИД документов
courtyard 2
city 1
garden 1
большой 1
мотель 2
комната 1, 2
standard 1
suite 1
view 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 хранятся в структуре, которая называется инвертированным индексом и выглядит примерно так:

Термин ИД документов
spring 1, 2
summer 1
fall 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), может использоваться более широкий спектр выражений. В частности, в any можно использовать and и or, если базовые выражения сравнения объединяются в диапазоны сравнения с помощью and, которые затем объединяются с помощью or. Такая структура логических выражений называется нормальной дизъюнктивной формой (НДФ) (объединение И с помощью ИЛИ). И наоборот, лямбда-выражения для all этих для типов данных должны иметь нормальную конъюнктивную форму (НКФ) (объединение ИЛИ по И). Поиск по искусственному интеллекту Azure позволяет выполнять такие сравнения диапазонов, так как он может эффективно выполнять их с помощью инвертированных индексов, так же как и быстрый поиск терминов для строк.

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

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

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

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

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