Узнайте, как работают фильтры коллекций OData в поиске ИИ Azure
В этой статье содержатся сведения о разработчиках, которые пишут расширенные фильтры со сложными лямбда-выражениями. В этой статье объясняется, почему существуют правила для фильтров коллекции, изучая способ выполнения этих фильтров поиска ИИ Azure.
При создании фильтра для полей коллекции в службе "Поиск ИИ Azure" можно использовать any
операторы и all
операторы вместе с лямбда-выражениями. Лямбда-выражения — это логические выражения, которые ссылаются на переменную диапазона. В фильтрах, использующих лямбда-выражение, any
операторы all
аналогичны for
циклу на большинстве языков программирования, при этом переменная диапазона принимает роль переменной цикла и лямбда-выражение в качестве тела цикла. Переменная диапазона принимает значение current (текущее) в коллекции во время итерации цикла.
Таково сходство на уровне концепции. В действительности поиск ИИ Azure реализует фильтры в совершенно другом способе работы for
циклов. В идеале эти различия для вас никак не проявляются, за исключением некоторых ситуаций. Фактически при составлении лямбда-выражений необходимо следовать определенным правилам.
Примечание.
Дополнительные сведения о правилах для фильтров коллекций, включая примеры, см. в статье "Устранение неполадок фильтров коллекций OData" в службе "Поиск ИИ Azure".
Почему фильтры коллекции поддерживаются с ограничениями
Существует три основные причины, по которым функции фильтрации не полностью поддерживаются для всех типов коллекций:
- Для определенных типов данных поддерживаются только определенные операторы. Например, не имеет смысла сравнивать логические значения
true
иfalse
с помощью операторовlt
,gt
и т. д. - Поиск по искусственному интеллекту Azure не поддерживает сопоставленный поиск по полям типа
Collection(Edm.ComplexType)
. - Поиск по искусственному интеллекту 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).
На практике именно эти типы фильтров обычно и используются. При этом важно понимать границы возможного.
Конкретные примеры того, какие виды фильтров разрешены, а какие нет, см. в статье Составление допустимых фильтров коллекции.