Моделирование сложных типов данных в Когнитивном поиске Azure

Внешние наборы данных, используемые для заполнения индекса Когнитивного поиска Azure, могут предоставляться во многих формах. Иногда они содержат иерархические или вложенные структуры. Например, несколько адресов для одного клиента, несколько цветов и размеров для одного SKU, несколько авторов одной книги и т. д. В моделировании такие структуры называются сложными, составными или агрегатными типами данных. В Когнитивном поиске Azure для такого понятия используется термин сложный тип. В Когнитивном поиске Azure сложные типы моделируются с помощью сложных полей. Сложное поле — это поле, содержащее дочерние элементы (вложенные поля), которые могут иметь любой тип данных, в том числе другие сложные типы. Это схоже со структурированными типами данных в языке программирования.

Сложные поля представляют либо один объект в документе, либо массив объектов, в зависимости от типа данных. Поля типа Edm.ComplexType представляют отдельные объекты, а поля типа Collection(Edm.ComplexType) — массивы объектов.

Когнитивный поиск Azure изначально поддерживает сложные типы и коллекции. Эти типы позволяют моделировать почти любую структуру JSON в индексе Когнитивного поиска Azure. В предыдущих версиях API Когнитивного поиска Azure можно было импортировать только плоские наборы строк. В последней версии индекс может более точно соответствовать исходным данным. Иными словами, если у исходных данных сложный тип, индекс также может быть сложным.

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

Примечание

Поддержка сложных типов стала общедоступной, начиная с версии api-version=2019-05-06.

Если решение поиска основано работе с плоскими наборами данных в коллекции, измените индекс, включив в него сложные типы, которые поддерживаются в последней версии API. Дополнительные сведения об обновлении версий API см. в статье Обновление до последней версии REST API или Обновление до последней версии пакета SDK для .NET.

Пример сложной структуры

Следующий документ JSON состоит из простых и сложных полей. У сложных полей, таких как Address и Rooms, есть вложенные поля. У поля Address один набор значений для вложенных полей, так как это один объект в документе. В то же время у поля Rooms несколько наборов значений для своих вложенных полей, по одному для каждого объекта в коллекции.

{
  "HotelId": "1",
  "HotelName": "Secret Point Motel",
  "Description": "Ideally located on the main commercial artery of the city in the heart of New York.",
  "Tags": ["Free wifi", "on-site parking", "indoor pool", "continental breakfast"],
  "Address": {
    "StreetAddress": "677 5th Ave",
    "City": "New York",
    "StateProvince": "NY"
  },
  "Rooms": [
    {
      "Description": "Budget Room, 1 Queen Bed (Cityside)",
      "RoomNumber": 1105,
      "BaseRate": 96.99,
    },
    {
      "Description": "Deluxe Room, 2 Double Beds (City View)",
      "Type": "Deluxe Room",
      "BaseRate": 150.99,
    }
    . . .
  ]
}

Индексирование сложных типов

Во время индексирования можно обрабатывать не более 3000 элементов во всех сложных коллекциях внутри одного документа. Элемент сложной коллекции является членом этой коллекции, поэтому в случае поля Rooms (единственная сложная коллекция в примере для гостиницы) каждое вложенное поле (номер гостиницы) является элементом. В приведенном выше примере, если в гостинице Secret Point Motel 500 номеров, в документе для гостиницы будет 500 элементов. Для вложенных сложных коллекций каждый вложенный элемент также учитывается вместе с внешним (родительским) элементом.

Это ограничение применяется только к сложным коллекциям, а не к сложным типам (например, Address) или коллекциям строк (например, Tags).

Создание сложных полей

Как и в случае с любым определением индекса, для создания схемы, включающей сложные типы, можно использовать портал, REST API или пакет SDK для .NET.

Другие пакеты SDK Azure предоставляют примеры в Python, Java и JavaScript.

  1. Войдите на портал Azure.

  2. На странице обзора службы поиска выберите вкладку "Индексы ".

  3. Откройте существующий индекс или создайте новый индекс.

  4. Перейдите на вкладку "Поля " и нажмите кнопку "Добавить поле". Добавляется пустое поле. Если вы работаете с существующей коллекцией полей, прокрутите вниз, чтобы настроить поле.

  5. Присвойте полю имя и присвойте типу Edm.ComplexType либо.Collection(Edm.ComplexType)

  6. Щелкните многоточие справа, а затем выберите " Добавить поле " или "Добавить подполе", а затем назначьте атрибуты.

Обновление сложных полей

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

Структурные обновления определения

Новые вложенные поля можно добавить в сложное поле в любое время без необходимости перестроения индекса. Например, добавление ZipCode в Address или Amenities в Rooms разрешено, так же как и добавление поля верхнего уровня в индекс. В существующих документах для новых полей будет указано значение NULL, пока вы не заполните эти поля явным образом, обновив данные.

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

Обновление данных

Обновление существующих документов в индексе с помощью действия upload выполняется одинаково для сложных и простых полей — все поля заменяются. Однако действие merge (или mergeOrUpload в отношении существующего документа) выполняется по-разному. В частности, действие merge не поддерживает объединение элементов в коллекции. Это ограничение относится к коллекциям примитивных типов и сложным коллекциям. Чтобы обновить коллекцию, необходимо получить все значения коллекции, внести изменения, а затем включить новую коллекцию в запрос API индекса.

Поиск сложных полей

Выражения поиска в свободной форме можно применять и к сложными типам. Если поле или вложенное поле, поддерживающее поиск, в любом месте документа соответствует условию, то документ является совпадением.

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

search=Address/City:Portland AND Address/State:OR

Такие запросы не коррелируются в рамках полнотекстового поиска, в отличие от фильтров. В фильтрах запросы в отношении вложенных полей сложной коллекции коррелируются с помощью переменных диапазона в операторах any или all. Указанный выше запрос Lucene возвращает документы, содержащие значения "Portland, Maine" и "Portland, Oregon", а также другие города в штате Орегон (Oregon). Это происходит потому, что каждое предложение применяется ко всем значениям поля во всем документе, поэтому понятия "текущий вложенный документ" не существует. Дополнительные сведения см. в статье Основные сведения о фильтрах коллекции OData в Когнитивном поиске Azure.

Выбор сложных полей

С помощью параметра $select можно указать поля, которые будут возвращаться в результатах поиска. Чтобы использовать этот параметр для выбора конкретных вложенных полей сложного поля, включите в запрос родительское поле и вложенное поле, разделенные косой чертой (/).

$select=HotelName, Address/City, Rooms/BaseRate

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

Фильтрация, фасетизация и сортировка сложных полей

Синтаксис пути OData, который используется для фильтрации и поиска в полях, можно также использовать для фасетизации, сортировки и выбора полей в запросе поиска. Для сложных типов применяются правила, которые определяют, какие вложенные поля могут быть помечены как доступные для сортировки или фасетизации. Дополнительные сведения об этих правилах см. в статье о создании ссылки на API индекса.

Фасетизация вложенных полей

Любое вложенное поле, не относящееся к типу Edm.GeographyPoint или Collection(Edm.GeographyPoint), может быть помечено как доступное для фасетизации.

Количество документов, возвращаемое в результатах фасетизации, вычисляется для родительского документа (гостиницы), а не для вложенных документов в сложной коллекции (номеров). Например, предположим, что в гостинице 20 номеров типа "люкс". Для параметра фасетизации facet=Rooms/Type число аспектов будет равно одному для гостиницы, а не 20 для номеров.

Сортировка сложных полей

Операции сортировки применяются к документам (гостиницам), а не к вложенным документам (номерам). При наличии коллекции сложных типов, например номеров, важно понимать, что вы не сможете сортировать номера. На самом деле вы не можете выполнить сортировку ни одной коллекции.

Операции сортировки рассчитаны на случаи, когда поля содержат одно значение для каждого документа независимо от того, является ли поле простым полем или вложенным полем сложного типа. Например, поле Address/City доступно для сортировки, так как у гостиницы только один адрес, и запрос $orderby=Address/City будет сортировать гостиницы по городу.

Фильтрация по сложным полям

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

$filter=Address/Country eq 'Canada'

Для фильтрации по сложному полю коллекции можно использовать лямбда-выражение с операторами any и all. В этом случае переменная диапазона лямбда-выражения является объектом с вложенными полями. Вы можете ссылаться на эти вложенные поля с помощью стандартного синтаксиса пути OData. Например, следующий фильтр возвратит все гостиницы, где есть хотя бы один номер "люкс", и все номера, где запрещено курить:

$filter=Rooms/any(room: room/Type eq 'Deluxe Room') and Rooms/all(room: not room/SmokingAllowed)

Как и в случае с простыми полями верхнего уровня, простые вложенные поля сложных полей можно включать в фильтры, только если в определении индекса атрибуту Фильтруемый присвоено значение true. Дополнительные сведения см. в статье о создании ссылки на API индекса.

Дальнейшие действия

Используйте набор данных гостиниц в мастере импорта данных. Для доступа к данным потребуется информация о подключении к Cosmos DB, указанная в файле сведений.

Изучив ее, с помощью мастера создайте источник данных Azure Cosmos DB. В мастере перейдите на целевую страницу индексов и вы увидите индекс со сложными типами. Создайте и загрузите этот индекс, а затем выполните запросы, чтобы изучить новую структуру.