Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Azure DevOps Services | Azure DevOps Server 2022 — Azure DevOps Server 2019
Разработчики расширений могут воспользоваться рекомендациями, приведенными в этой статье, для разработки эффективных запросов OData к Аналитике для Azure DevOps. Следуя этим рекомендациям, можно убедиться, что запросы имеют хорошую производительность для времени выполнения и потребления ресурсов. Запросы, которые не соответствуют этим рекомендациям, могут привести к снижению производительности, с длительным временем ожидания отчета, запросами, превышающими допустимое потребление ресурсов или блоки служб.
Примечание.
Служба Аналитики автоматически включается и поддерживается в рабочей среде для всех служб в Azure DevOps Services. Интеграция Power BI и доступ к веб-каналу OData службы Аналитики общедоступны. Рекомендуется использовать канал данных OData для аналитики и предоставить обратную связь.
Доступные данные зависят от версий. Последняя поддерживаемая версия API OData — v2.0
, а последняя предварительная версия — v4.0-preview
. Дополнительные сведения см. в разделе "Управление версиями API OData".
Примечание.
Служба Аналитики автоматически устанавливается и поддерживается в рабочей среде для всех новых коллекций проектов для Azure DevOps Server 2020 и более поздних версий. Интеграция Power BI и доступ к веб-каналу OData службы Аналитики общедоступны. Рекомендуется использовать канал данных OData для аналитики и предоставить обратную связь. При обновлении с Azure DevOps Server 2019 можно установить службу Аналитики во время обновления.
Доступные данные зависят от версий. Последняя поддерживаемая версия API OData — v2.0
, а последняя предварительная версия — v4.0-preview
. Дополнительные сведения см. в разделе "Управление версиями API OData".
Эти руководящие принципы содержат наши рекомендации, обозначенные терминами DO, CONSIDER, AVOID и DON'T. Ограничивающие правила, применяемые аналитикой, содержат префикс [ЗАБЛОКИРОВАНО] . Вы должны понимать компромиссы между различными решениями. При определенных обстоятельствах могут потребоваться требования к данным, которые заставляют вас нарушать одно или несколько рекомендаций. Такие случаи должны быть редкими. Мы рекомендуем вам иметь четкие и убедительные причины для таких решений.
Совет
Примеры, показанные в этом документе, основаны на URL-адресе Azure DevOps Services. Используйте подстановки для локальных версий.
https://{servername}:{port}/tfs/{OrganizationName}/{ProjectName}/_odata/{version}/
Сообщения об ошибках и предупреждения
✔️ Просмотрите предупреждения в ответах OData
Каждый выполняемый запрос проверяется на соответствие набору предопределенных правил. Нарушения возвращают ответ OData следующим образом: @vsts.warnings
. Просмотрите эти предупреждения по мере предоставления текущей и контекстно-конфиденциальной информации о том, как улучшить запрос.
{
"@odata.context": "https://{OrganizationName}.tfsallin.net/_odata/v1.0/$metadata#WorkItems",
"@vsts.warnings": [
"The specified query does not include a $select or $apply clause which is recommended for all queries."
],
...
}
✔️ Обязательно проверьте сообщения об ошибках OData
Запросы, которые нарушают правило ошибки OData, приводят к сбою ответа с кодом состояния 400 (недопустимый запрос). Ассоциированные сообщения не отображаются в свойстве @vsts.warnings
. Вместо этого они создают сообщение об ошибке в свойстве message
в ответе JSON.
{
"error": {
"code": "0",
"message": "The query specified in the URI is not valid. The Snapshot tables in Analytics are intended to be used only in an aggregation."
}
}
Ограничения
Рекомендации
- ✔️ Обязательно ограничьте запрос проектами, к которым у вас есть доступ
-
✔️ Укажите фильтр проекта внутри
$expand
предложения, если расширение может включать данные в другие, потенциально недоступные проекты - ✔️ Подождите или остановите операцию, если запрос превышает ограничения использования
- ✔️ Дожидайтесь или остановите операцию, если запрос завершается сбоем с истечением времени ожидания
- ✔️ Включите столбец из
DateSK
илиDateValue
в предложениеgroupby
, когда вы выполняете агрегацию по таблицам моментальных снимков. - ✔️ Явно обращайтесь к сущностям с использованием фильтров
-
✔️ Используйте
WorkItemRevisions
набор сущностей для загрузки всех ревизий для данного элемента работы - ✔️ Обязательно используйте пакетную конечную точку для длинных запросов
- ✔️ Указывайте часовой пояс при фильтрации по столбцам дат
Рассмотрите
Заблокировано
- ❌ [ЗАБЛОКИРОВАНО] Не используйте сущности моментального снимка ни для чего, кроме агрегаций
- ❌ [ЗАБЛОКИРОВАНО] Не используйте ключи сущностей в путях ресурсов для адресации сущностей
-
❌ [ЗАБЛОКИРОВАНО] Не разворачивайте
Revisions
наWorkItem
объекте - ❌ [ЗАБЛОКИРОВАНО] Не группировать по отдельным столбцам
-
❌ [ЗАБЛОКИРОВАНО] Не используйте
countdistinct
агрегирование - ❌ [ЗАБЛОКИРОВАНО] Не используйте пакетную конечную точку для отправки нескольких запросов
- ❌ [ЗАБЛОКИРОВАНО] Не используйте запросы, которые приводят к более чем 800 столбцам
Избегайте
- ❌ Избегайте агрегирования, которые могут привести к арифметическому переполнению
- ❌ Избегайте создания длинных запросов
✔️ Ограничьте запрос проектами, к которым у вас есть доступ
Если запрос предназначен для данных из проекта, к которым у вас нет доступа, запрос возвращает сообщение "Доступ к проекту запрещен". Чтобы убедиться, что у вас есть доступ, убедитесь, что для всех проектов, которые вы запрашиваете, задано разрешение "Просмотр аналитики" на "Разрешить". Дополнительные сведения см. в разделе "Разрешения", необходимые для доступа к аналитике.
Если у вас нет доступа к проекту, отобразится следующее сообщение:
Результаты запроса включают данные в один или несколько проектов, для которых у вас нет доступа. Добавьте один или несколько фильтров проектов, чтобы указать проекты, к которые у вас есть доступ в сущности WorkItems. Если вы используете свойства $expand или навигации, фильтр проекта обязателен для этих сущностей.
Чтобы обойти эту проблему, можно либо явно добавить фильтр проекта, либо использовать конечную точку с областью проекта, как описано далее в этой статье.
Например, следующий запрос извлекает рабочие элементы, принадлежащие проектам с именем {projectSK1}
и {projectSK2}
.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK1} or ProjectSK eq {projectSK2}
&$select=WorkItemId, Title
✔️ Укажите фильтр проекта внутри $expand
предложения, если расширение может включать данные в другие, потенциально недоступные проекты
При развертывании свойств навигации есть вероятность того, что вы в конечном итоге ссылаетесь на данные из других недоступных проектов. Если вы ссылаетесь на недоступные данные, вы получите то же сообщение об ошибке, указанное ранее, "Результаты запроса включают данные в один или несколько проектов...". Аналогичным образом можно устранить эту проблему, добавив явные фильтры проектов для управления расширенными данными.
Это можно сделать в обычном $filter
предложении для простых свойств навигации. Например, следующий запрос явно запрашивает WorkItemLinks
, где ссылка и целевой объект существуют в одном проекте.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
$filter=ProjectSK eq {projectSK} and TargetWorkItem/ProjectSK eq {projectSK}
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($select=WorkItemId, Title)
Вместо этого можно переместить фильтр на $filter
параметр расширения в условии $expand
. Однако он изменяет семантику запроса. Например, следующий запрос получает все ссылки из данного проекта и условно расширяет целевой объект только в том случае, если он существует в одном проекте. Хотя этот метод является допустимым, он может привести к путанице, так как может быть трудно определить, не развернуто ли свойство, потому что оно null
, или потому что оно было отфильтровано. Используйте это решение только в том случае, если вам действительно нужно это конкретное поведение.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
$filter=ProjectSK eq {projectSK}
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)
Опция $filter
расширения полезна при использовании таких свойств коллекции расширения, как Children
в наборе сущностей WorkItems
. Например, следующий запрос возвращает все рабочие элементы из данного проекта вместе со всеми дочерними элементами, принадлежащими тому же проекту.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK}
&$select=WorkItemId, Title
&$expand=Children($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)
Укажите фильтр, если вы развернёте одно из следующих свойств:
-
WorkItems
набор сущностей:Parent
,Children
-
WorkItemLinks
набор сущностей:TargetWorkItem
.
✔️ РАССМОТРИТЕ возможность выполнения запросов через конечную точку, настроенную для проекта
Если вы интересуетесь данными из одного проекта, мы рекомендуем использовать конечную точку OData с областью проекта (/{ProjectName}/_odata/v1.0
). Это позволяет избежать проблем, описанных в предыдущих двух разделах, и неявно фильтрует данные в одном проекте, наборе сущностей со ссылкой и всех расширенных свойствах навигации.
Благодаря этому упрощению запросы из предыдущего раздела можно переписать на следующую форму. Не только фильтр в предложении expand исчез, но и не требуется фильтр для основного множества сущностей.
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItemLinks?
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($select=WorkItemId, Title)
Запрос к дочерним элементам рабочего элемента также значительно короче и проще.
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItems?
&$select=WorkItemId, Title
&$expand=Children($select=WorkItemId, Title)
Это решение можно применять только в том случае, если основное внимание уделяется данным из одного проекта. Для создания отчетов между проектами необходимо использовать стратегии фильтрации, описанные в предыдущих разделах.
✔️ Подождите или остановите операцию, если запрос превышает ограничения использования
Если вы выполняете много запросов, или если они требуют много ресурсов, вы можете превысить пределы использования службы, и вас могут временно заблокировать. Если вы превысите ограничения службы, остановите операцию, так как есть вероятность, что следующий запрос, который вы отправите, завершится сбоем с таким же сообщением об ошибке.
Запрос был заблокирован из-за превышения использования ресурса "{resource}" в пространстве имен "{пространство имен}".
Дополнительные сведения об ограничении скорости см. в разделе "Ограничения скорости". Чтобы узнать, как создавать эффективные запросы OData, ознакомьтесь с рекомендациями по производительности далее в этой статье.
✔️ Дожидайтесь или остановите операцию, если запрос завершается сбоем с истечением времени ожидания
Аналогично ситуации, когда превышены лимиты использования, нужно подождать или остановить операцию, если запрос вызывает тайм-аут. Это может сигнализировать о временной проблеме, поэтому вы можете повторить попытку один раз, чтобы узнать, устранена ли проблема. Однако постоянные тайм-ауты указывают на то, что запрос, вероятно, слишком ресурсоемкий для выполнения. Дальнейшие повторные попытки приводят только к превышению ограничений использования, и вы получаете блокировку.
TF400733: запрос отменен: запрос превысил время ожидания запроса, повторите попытку.
Время ожидания указывает, что запросу требуется оптимизация. Чтобы узнать, как создавать эффективные запросы OData, ознакомьтесь с рекомендациями по производительности далее в этой статье.
❌ [ЗАБЛОКИРОВАНО] Не используйте сущности моментального снимка для чего-либо, кроме агрегаций
Наборы сущностей моментальных снимков с суффиксом Snapshot
являются особыми, так как они моделироваются как ежедневные моментальные снимки. Их можно использовать для получения состояния сущностей в том виде, в котором они были в конце каждого дня в прошлом. Например, если вы запросите WorkItemSnapshot
и отфильтруете до одного WorkItemId
, вы получите одну запись за каждый день с момента создания рабочего элемента. Загрузка непосредственно всех этих данных будет дорогой и, скорее всего, превысит ограничения использования и будет заблокирована. Однако агрегирование для этих сущностей разрешено и рекомендуется. Фактически, наборы сущностей моментального снимка были разработаны с учетом сценариев агрегирования.
Например, следующий запрос показывает количество рабочих элементов по дате для наблюдения за тем, как изменилось число в январе 2020 года.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
$apply=
filter(DateSK ge 20200101 and DateSK le 20200131)/
groupby((DateSK), aggregate($count as Count))
Дополнительные сведения об агрегированиях см. в разделе "Статистические данные".
✔️ Включайте столбец DateSK
или DateValue
в предложение groupby
при агрегации данных по таблицам моментальных снимков.
Так как все сущности моментальных снимков моделироваются как ежедневные таблицы моментальных снимков, всегда следует включать одно из свойств дня (DateSK
или DateValue
) в предложение группирования. В противном случае результат может быть завышен и отображаться неправильно.
Например, если вы группируете только по WorkItemSnapshot
свойству и агрегируете AssignedTo
его с помощью подсчета, то количество рабочих элементов, назначенных людям, будет умножено на число дней, в течение которых каждое задание было активно. Хотя у вас может возникнуть ситуация, когда это желаемый результат, такие случаи редки.
❌ [ЗАБЛОКИРОВАНО] Не используйте ключи сущностей в путях ресурсов для адресации сущностей
Синтаксис OData предоставляет способ доступа к определенной сущности, включив его ключи непосредственно в сегменты URL-адресов. Дополнительные сведения см. в статье OData версии 4.0. Часть 2. Соглашения о URL-адресах — 4.3 Адресация сущностей. Хотя OData разрешает такую адресацию, Analytics блокирует ее. Включение данных в запрос приводит к следующей ошибке.
Запрос, указанный в URI, недействителен. Аналитика не поддерживает навигацию по ключам или свойствам, например WorkItems(Id) или WorkItem(Id)/AssignedTo. Если вы получаете эту ошибку в PowerBI, пожалуйста, перепишите ваш запрос, чтобы избежать неправильного свёртывания, которое вызывает проблему N+1.
Как намекают сообщения об ошибках, некоторые клиентские инструменты могут злоупотреблять прямой адресацией сущностей. Вместо загрузки всех данных в одном запросе такие клиенты могут самостоятельно запрашивать каждую сущность. Эта практика не рекомендуется, так как она может привести к большому количеству запросов. Вместо этого рекомендуется использовать явную адресацию сущностей, как описано в следующем разделе.
✔️ Явно обращайтесь к сущностям с условиями фильтра
Если вы хотите получить данные для одной сущности, следует использовать тот же подход, что и для коллекции сущностей, и явно определять фильтры в предложении $filter
.
Например, следующий запрос получает один рабочий элемент по его идентификатору.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=WorkItemId eq {id}
&$select=WorkItemId, Title
Если вы не уверены, какие свойства следует включить в такой фильтр, его можно найти в метаданных. Посмотрите создание запросов OData для аналитики и компоненты URL-адресов для запроса метаданных. Свойства находятся в Key
элементе EntityType
объекта . Например, WorkItemId
и Revision
являются ключевыми столбцами для сущности WorkItemRevision
.
<EntityType Name="WorkItemRevision">
<Key>
<PropertyRef Name="WorkItemId"/>
<PropertyRef Name="Revision"/>
</Key>
[...]
</EntityType>
❌ [ЗАБЛОКИРОВАНО] Не расширяйте Revisions
на WorkItem
сущность
Модель данных Аналитики запрещает определенные типы расширений. Один из них, который может быть удивительным для некоторых, является свойством Revisions
коллекции в сущности WorkItem
. Если вы попробуете развернуть это свойство, вы получите следующее сообщение об ошибке.
Запрос, указанный в URI, недействителен. Свойство "Редакции" нельзя использовать в опции запроса $expand.
Это ограничение было введено для поощрения всех использовать рекомендуемое решение, которое извлекает редакции из WorkItemRevisions
, как описано в следующем разделе.
✔️ Используйте набор сущностей WorkItemRevisions
для загрузки всех ревизий для данного рабочего элемента.
Используйте WorkItemRevisions
каждый раз, когда вы хотите получить всю историю для рабочего элемента или коллекции рабочих элементов.
Например, следующий запрос возвращает все редакции рабочего элемента с идентификатором {id}
.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
$filter=WorkItemId eq {id}
&$select=WorkItemId, Title
Если вы заботитесь о полной истории всех рабочих элементов, которые соответствуют определенным критериям, вы можете выразить его с помощью фильтра в свойстве WorkItem
навигации. Например, следующий запрос получает все редакции всех активных рабочих элементов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
$filter=WorkItem/State eq 'Active'
&$select=WorkItemId, Title
❌ [ЗАБЛОКИРОВАНО] Не группировать по отдельным столбцам
Для уменьшения количества записей используется операция группировки. Использование отдельных столбцов в groupby
предложении указывает на проблему, и запрос завершается сбоем немедленно. При случайном возникновении этой ситуации вы получите следующее сообщение об ошибке.
Не рекомендуется использовать один или несколько столбцов, указанных в предложении groupby этого запроса.
Чтобы устранить эту проблему, удалите отдельный столбец из groupby
предложения.
❌ [ЗАБЛОКИРОВАНО] Не используйте countdistinct
агрегирование
Аналитика не поддерживает функцию countdistinct
, даже несмотря на то, что OData поддерживает. Хотя мы планируем добавить поддержку в будущем, она в настоящее время недоступна. Запрос, содержащий эту функцию, возвращает следующее сообщение об ошибке.
Запросы, которые применяют подсчет уникальных значений с агрегированием, не поддерживаются.
❌ Избегайте агрегирования, которое может привести к арифметическому переполнению
В редких случаях запрос агрегирования может столкнуться с проблемами с арифметическим переполнением. Например, это может произойти при сумме некоторых числовых свойств, которые не предназначены для суммирования, например StackRank
в сущностях рабочего элемента. Поскольку стандарт OData для расширения агрегации данных не предоставляет способа приведения свойства к другому типу, единственный способ решения этой проблемы — удалить проблемное свойство из агрегации.
✔️ Используйте пакетную конечную точку для длинных запросов
Проблемы с длинными запросами могут возникнуть. В частности, могут возникнуть проблемы, когда:
- Вы выполняете запрос проекта с множеством настраиваемых полей.
- Запрос создается программным способом.
Текущее ограничение запросов OData, отправленных с HTTP GET
3000 символами. Если вы превышаете его, вы вернетесь к ответу "404 Не найдено".
HTTP/1.1 404 Not Found
Content-Length: 0
Чтобы устранить эту проблему, используйте пакетный конечный пункт OData, как указано в спецификации OData версии 4.0. Часть 1: Протокол — 11.7 Пакетные запросы. Возможность пакетирования в первую очередь предназначена для группировки нескольких операций в одни полезные данные запроса HTTP
, но её также можно использовать как обходное решение ограничения длины запроса. Отправив HTTP POST
запрос произвольной длины, служба правильно интерпретирует его.
❌ [ЗАБЛОКИРОВАНО] Не используйте пакетную конечную точку для отправки нескольких запросов
Мы ограничиваем использование конечной точки пакетной обработки для обработки пакета запросов одновременно. Один запрос по-прежнему может иметь только один запрос. Если вы пытаетесь отправить пакет из нескольких запросов, операция завершается ошибкой со следующим сообщением об ошибке. Единственным решением является разделение запросов на несколько запросов.
Аналитика не поддерживает обработку нескольких операций, содержащих текущее пакетное сообщение. Аналитика использует пакет OData для поддержки запросов POST, но требует, чтобы операция была ограничена одним запросом.
❌ [ЗАБЛОКИРОВАНО] Не используйте запросы, которые приводят к более чем 800 столбцам
Мы ограничиваем запросы, которые приводят к более чем 800 столбцам. Если вы недостаточно избирательно подходите к выбору столбцов, которые возвращает ваш запрос, может появиться следующее сообщение об ошибке.
VS403670. Указанный запрос возвращает столбцы N, превышающие допустимое ограничение в 800 столбцов. Пожалуйста, используйте явный $select (включая внутри $expand) для ограничения количества столбцов.
Добавьте предложение $select и операции $expand в свой запрос, чтобы избежать превышения этого ограничения.
❌ Избегайте создания длинных запросов
Рекомендуется оценивать подход всякий раз, когда вы создаете длинный запрос. Хотя существует множество сценариев, для которых требуется длинный запрос (например, сложные фильтры или длинный список свойств), обычно они предоставляют ранний индикатор неоптимальной структуры.
Если запрос содержит много ключей сущностей (например, WorkItemId eq {id 1} or WorkItemId eq {id 2} or ...
), то вы можете его переписать. Вместо передачи идентификаторов попробуйте определить другие критерии, которые выбирают тот же набор сущностей. Иногда может потребоваться изменить процесс (например, добавить новое поле или тег), но это обычно стоит. Запросы, использующие более абстрактные фильтры, проще поддерживать и иметь больший потенциал для улучшения работы.
Другой сценарий, который, как правило, создает длинные запросы, возникает при включении множества отдельных дат (например, DateSK eq {dateSK 1} or DateSK eq {dateSK 2} or ...
). Найдите другой шаблон, который можно использовать для создания более абстрактного фильтра. Например, следующий запрос возвращает все рабочие элементы, созданные в понедельник.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedOn/DayOfWeek eq 2
&$select=WorkItemId, Title, State
Укажите часовой пояс при фильтрации по столбцам дат
Часовой пояс (Edm.DateTimeOffset
) предоставляет все сведения о дате и времени со смещением, соответствующим параметрам часового пояса организации. Эти данные являются точными и простыми для интерпретации одновременно. Еще одно неочевидное следствие заключается в том, что все фильтры должны передавать сведения часового пояса. Если пропустить его, вы получите следующее сообщение об ошибке.
Запрос, указанный в URI, недействителен. Смещение времени и даты не задано. Чтобы указать все, начиная с полуночи, используйте один из этих форматов: YYYY-MM-ddZ. Чтобы указать смещение, используйте yyyy-MM-ddThh:mm-hh:mm (стандартное представление дат и времени ISO 8601).
Чтобы решить эту проблему, добавьте сведения о часовом поясе. Например, предположим, что организация настроена для отображения данных в часовом поясе "(UTC-08:00) Тихоокеанское время (США и Канада)", следующий запрос получает все рабочие элементы, созданные с начала 2020 года.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDate ge 2020-01-01T00:00:00-08:00
&$select=WorkItemId, Title, State
То же решение работает для часовых поясов с положительными смещениями, однако знак плюса (+
) имеет особое значение в URI, и его необходимо правильно обрабатывать. Если в качестве отправной точки указать 2020-01-01T00:00:00+08:00
(с символом +
), вы получите следующую ошибку.
Запрос, указанный в URI, недействителен. Синтаксическая ошибка в позиции 31 в "CreatedDate ge 2020-01-01T00000 08:00".
Чтобы решить эту проблему, замените +
символ его закодированной версией %2B
. Например, если организация настроена на отображение данных в часовом поясе "(UTC+08:00) Пекин, Чунгинг, Гонконг, Урумчи", следующий запрос возвращает все рабочие элементы, созданные с начала 2020 года.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDate ge 2020-01-01T00:00:00%2B08:00
&$select=WorkItemId, Title, State
Альтернативный подход — использовать суррогатные ключевые свойства даты, так как они не хранят сведения часового пояса. Например, следующий запрос возвращает все рабочие элементы, созданные с начала 2020 года, независимо от параметров организации.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge 20200101
&$select=WorkItemId, Title, State
Рекомендации по повышению производительности
Рекомендации
- ✔️ Измерьте эффект внедрения руководства по производительности
- ✔️ Рекомендуется использовать расширения агрегирования
-
✔️ Укажите столбцы в операторе
$select
- ✔️ Укажите столбцы в параметре
$select
расширения внутри$expand
предложения -
✔️ Определите фильтр
RevisedDateSK
при запросе данных исторических рабочих элементов (WorkItemRevisions
илиWorkItemSnapshot
наборов сущностей) - ✔️ Используйте еженедельные или ежемесячные моментальные снимки для запросов трендов, охватывающих длительный период времени
- ✔️ Используйте свойство коллекции для рабочих элементов при фильтрации по тегам
-
✔️ Используйте свойство
TagNames
, если хотите отобразить все теги рабочего элемента в виде текста - ✔️ Используйте разбиение на страницы на стороне сервера
- ✔️ Используйте параметр запроса для ограничения количества записей
Не делайте
-
❌ Не используйте функции
tolower
иtoupper
для сравнения без учета буквенного регистра -
❌ Не используйте неограниченное расширение с
$levels=max
-
❌ Не используйте параметры запроса
$top
и$skip
для реализации разбиения на страницы, управляемого клиентом
Рассмотрите
- ✔️ Рассмотрите возможность написания запросов для возврата небольших чисел записей
- ✔️ Рассмотрите возможность ограничения количества выбранных свойств минимальной
-
✔️ Рассмотреть фильтрацию по свойствам суррогатных ключей даты (суффикс
DateSK
) - ✔️ Рассмотрите возможность фильтрации по суррогатным ключевым столбцам
-
✔️ Рассмотрите возможность передачи
vsts.analytics.maxsize
предпочтений в заголовке
Избегайте
✔️ Обязательно измеряйте эффект внедрения руководства по производительности
Как и в случае с любыми рекомендациями по производительности, их не следует слепо реализовывать. Вместо этого всегда фиксируйте базовые показатели и измеряйте влияние внесенных изменений. Все рекомендации были созданы на основе взаимодействия с клиентами Аналитики, у которых были конкретные требования и проблемы. Эти рекомендации были рассмотрены как общие, так и потенциально полезны для тех, кто разрабатывает аналогичные запросы. Однако в редких случаях следование рекомендациям может не оказать никакого влияния или даже отрицательно сказаться на производительности. Вы должны измерить разницу, чтобы заметить это. Если это произойдет, предоставьте отзыв на портале Сообщество разработчиков.
Существует множество вариантов измерения производительности. Самым простым является запуск двух версий одного запроса непосредственно в браузере. Следите за временем, которое требуется в средствах разработчика. Например, можно использовать панель "Сеть" в средствах разработчика Microsoft Edge F12). Другим вариантом является запись этих сведений с помощью средства веб-отладки Fiddler.
Независимо от вашего подхода, выполните оба запроса несколько раз. Например, запустите запросы 30 раз каждый, чтобы иметь достаточно большой набор примеров. Затем определите характеристики производительности. Анализ следует многопользовательской архитектуре. Таким образом, другие операции, происходящие в то же время, могут повлиять на длительность запросов.
✔️ Используйте расширения агрегирования
Наиболее эффективным способом повышения производительности запросов является использование расширения — расширение OData для агрегирования данных. С расширением агрегирования попросите, чтобы служба суммировала данные на стороне сервера и вернула более короткий ответ, чем можно получить, применяя ту же функцию на стороне клиента. Наконец, аналитика оптимизирована для этого типа запросов, поэтому используйте ее.
Дополнительные сведения см. в разделе "Статистические данные".
✔️ Обязательно укажите столбцы в предложении $select
Укажите нужные столбцы в предложении $select
. Аналитика основана на технологии Index Columnstore . Это означает, что хранение данных и обработка запросов организованы по столбцам. Уменьшая набор свойств, на которые вы ссылаетесь в $select
предложении, можно уменьшить количество столбцов, которые необходимо сканировать, и повысить общую производительность запроса.
Например, следующий запрос указывает столбцы для рабочих элементов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$select=WorkItemId, Title, State
Примечание.
Azure DevOps поддерживает настройку процесса. Некоторые администраторы используют эту функцию и создают сотни настраиваемых полей. Если опущено $select
предложение, запрос возвращает все поля, включая настраиваемые поля.
✔️ Укажите столбцы в опции $select
развертывания внутри предложения $expand
Аналогично рекомендациям по клаузе $select
, укажите свойства в опции расширения $select
внутри клаузе $expand
. Это легко забыть, но если вы это упустите, результат будет содержать все свойства из расширенного объекта.
Например, следующий запрос указывает столбцы для рабочего элемента и его родительского элемента.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$select=WorkItemId, Title, State
&$expand=Parent($select=WorkItemId, Title, State)
✔️ Определите фильтр для RevisedDateSK
при запросе данных о исторических рабочих элементах (наборы сущностей WorkItemRevisions
или WorkItemSnapshot
)
При запросе исторических данных вероятность того, что вы заинтересованы в последнем периоде (например, 30 дней, 90 дней). Из-за реализации сущностей рабочих элементов удобно писать такие запросы, чтобы получить большую производительность. Каждый раз, когда вы обновляете рабочий элемент, он создает новую редакцию и записывает это действие в System.RevisedDate
поле, что делает его идеальным для фильтров журнала.
В Аналитике измененная дата отображается в свойствах RevisedDate
(Edm.DateTimeOffset
) и RevisedDateSK
(Edm.Int32
). Для обеспечения оптимальной производительности используйте последний. Это суррогатный ключ даты и он представляет дату создания редакции или используется для активных, незавершенных редакций. Если вы хотите получить все даты, начиная с {startDate}
включительно, добавьте следующий фильтр в запрос.
RevisedDateSK eq null or RevisedDateSK gt {startDateSK}
Например, следующий запрос возвращает количество рабочих элементов для каждого дня с начала 2020 года. Обратите внимание, что помимо очевидного фильтра в DateSK
столбце есть второй фильтр RevisedDateSK
. Хотя это может показаться избыточным, он помогает обработчику запросов отфильтровать редакции, которые не входят в область действия и значительно повышают производительность запросов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/v1.0/WorkItemSnapshot?
$apply=
filter(DateSK gt 20200101)/
filter(RevisedDateSK eq null or RevisedDateSK gt 20200101)/
groupby(
(DateValue),
aggregate($count as Count)
)
Примечание.
Мы придумали эту рекомендацию, когда мы работали над мини-приложениями Burndown. Первоначально мы определили фильтры только для DateSK
, но не смогли добиться, чтобы этот запрос хорошо масштабировался для организаций с большими наборами данных. Во время профилирования запросов мы заметили, что DateSK
не фильтрует исправления. Только после добавления фильтра на RevisedDateSK
мы смогли добиться высокой производительности.
~
Группа продуктов
✔️ Используйте еженедельные или ежемесячные моментальные снимки для запросов тренда, охватывающих длительный период времени.
По умолчанию все таблицы моментальных снимков моделируются как ежедневные моментальные таблицы фактов. Если вы запрашиваете диапазон времени, он получает значение для каждого дня. Длительные диапазоны времени приводят к большому количеству записей. Если вам не нужна такая высокая точность, можно использовать еженедельные или даже ежемесячные резервные копии.
Это можно сделать с другими выражениями фильтра, чтобы удалить дни, которые не завершают указанную неделю или месяц.
IsLastDayOfPeriod
Используйте свойство, которое было добавлено в Аналитику с учетом этого сценария. Это свойство имеет тип Microsoft.VisualStudio.Services.Analytics.Model.Period
и может определить, заканчивается ли день в разные периоды (например, недели, месяцы и т. д.).
<EnumType Name="Period" IsFlags="true">
<Member Name="None" Value="0"/>
<Member Name="Day" Value="1"/>
<Member Name="WeekEndingOnSunday" Value="2"/>
<Member Name="WeekEndingOnMonday" Value="4"/>
<Member Name="WeekEndingOnTuesday" Value="8"/>
<Member Name="WeekEndingOnWednesday" Value="16"/>
<Member Name="WeekEndingOnThursday" Value="32"/>
<Member Name="WeekEndingOnFriday" Value="64"/>
<Member Name="WeekEndingOnSaturday" Value="128"/>
<Member Name="Month" Value="256"/>
<Member Name="Quarter" Value="512"/>
<Member Name="Year" Value="1024"/>
<Member Name="All" Value="2047"/>
</EnumType>
Так как Microsoft.VisualStudio.Services.Analytics.Model.Period
определяется как перечисление с флагами, используйте оператор OData has
и укажите полный тип для литералы периода.
IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month'
Например, следующий запрос возвращает количество рабочих элементов, определенных в последний день каждого месяца.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
$apply=
filter(IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month')/
groupby(
(DateValue),
aggregate($count as Count)
)
✔️ Пожалуйста, используйте свойство коллекции Tags
для рабочих элементов при фильтрации по тегам
Вы можете использовать свойство TagNames
с функцией contains
, чтобы определить, помечена ли работа определенным тегом. Однако этот подход может привести к медленным запросам, особенно при проверке нескольких тегов одновременно. Для наилучшей производительности и результатов используйте свойство навигации Tags
вместо этого.
Например, следующий запрос получает все рабочие элементы, помеченные тегом {tag}
.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq '{tag}')
&$select=WorkItemId, Title, State
Этот подход также отлично подходит, если необходимо отфильтровать несколько тегов. Например, следующий запрос возвращает все рабочие элементы, помеченные как {tag1}
или{tag2}
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq {tag1} or t/TagName eq {tag2})
&$select=WorkItemId, Title, State
Эти фильтры также можно объединить с оператором "and". Например, следующий запрос получает все рабочие элементы, помеченные обоими тегами {tag1}
и{tag2}
.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq {tag1}) and Tags/any(t:t/TagName eq {tag2})
&$select=WorkItemId, Title, State
Используйте свойство TagNames
, если хотите отображать все теги рабочего элемента в виде текста.
Свойство Tags
навигации, описанное в предыдущем разделе, отлично подходит для фильтрации. Однако при работе с ними возникают некоторые проблемы, так как запрос возвращает теги в вложенной коллекции. Модель данных также содержит примитивное TagNames
свойство (Edm.String
), которое мы добавили для упрощения сценариев потребления тегов. Это одно текстовое значение, содержащее список всех тегов, объединенных с запятой "; " разделитель. Используйте это свойство, когда вам важно лишь совместное отображение тегов. Его можно объединить с фильтрами тегов, описанными ранее.
Например, следующий запрос получает все рабочие элементы, помеченные тегом {tag}
. Он возвращает идентификатор рабочего элемента, название, состояние и текстовое представление объединенных тегов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq '{tag}')
&$select=WorkItemId, Title, State, TagNames
Внимание
Свойство TagNames
имеет ограничение длины 1024 символов. Он содержит набор тегов, подходящих в рамках этого ограничения. Если рабочий элемент имеет много тегов или теги очень длинные, то TagNames
не содержит полный набор, и вместо этого следует использовать свойство навигации Tag
.
❌ Не используйте функции tolower
и toupper
для сравнения символов без учета регистра
Если вы работали с другими системами, вы можете ожидать, что будете использовать функции tolower
или toupper
для сравнения без учета регистра. По умолчанию, в аналитике все сравнения строк выполняются без учета регистра символов, поэтому функции для их явного использования не требуются.
Например, следующий запрос получает все рабочие элементы, помеченные как "QUALITY", "quality", или любое другое сочетание этого слова.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq 'quality')
&$select=WorkItemId, Title, State, TagNames
❌ НЕ используйте неограниченное расширение с $levels=max
OData имеет возможность расширить все уровни иерархической структуры. Например, отслеживание рабочих элементов содержит некоторые сущности, в которых можно применить неограниченное расширение. Эта операция работает только для организаций с небольшим объемом данных. Он не хорошо масштабируется для больших наборов данных. Не используйте его вообще, если:
- Вы работаете с большими наборами данных.
- Вы разрабатываете мини-приложение и не контролируете установку мини-приложения.
✔️ Используйте разбиение на страницы на основе сервера
Если вы запрашиваете слишком большой набор для отправки в одном ответе, аналитика применяет разбиение по страницам. Ответ включает только частичный набор и ссылку, которая позволяет получить следующий частичный набор элементов. Эта стратегия описана в спецификации OData — OData версии 4.0. Часть 1: Протокол — управляемое сервером разбиение на страницы. Позволяя службе управлять разбиением на страницы, вы добиваетесь наилучшей производительности, так как skiptoken
была тщательно разработана для каждой сущности, чтобы быть максимально эффективной.
Ссылка на следующую страницу включена в свойство @odata.nextLink
.
{
"@odata.context": "https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/$metadata#WorkItems(*)",
"value": [
...
],
"@odata.nextLink":"https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?$skiptoken=12345"}
Примечание.
Большинство существующих клиентов OData могут автоматически обрабатывать разбиение на страницы, управляемое сервером. Например, эта стратегия уже используется следующими средствами: Power BI, SQL Server Integration Services и Фабрика данных Azure.
❌ Не используйте параметры запроса $top
и $skip
для реализации разбиения по страницам, управляемым клиентом
С другими API REST, возможно, вы реализовали разбиение на страницы, управляемое клиентом, и параметры запроса $top
и $skip
. Не используйте их с аналитикой. Существует несколько проблем с этим подходом, и производительность является одной из них. Вместо этого используйте стратегию разбиения страниц, управляемую сервером, описанную в предыдущем разделе.
✔️ Используйте параметр запроса $top
для ограничения количества записей
Параметр $top
запроса не рекомендуется использовать только если он применяется вместе с $skip
. Если в вашем сценарии создания отчетов требуется только подмножество записей (например, пример), можно использовать $top
параметр запроса. Кроме того, если вам нужно ранжировать записи в соответствии с некоторыми критериями, вы всегда должны использовать $top
в сочетании с $orderby
, чтобы получить стабильный результат с записями с наивысшим рейтингом.
✔️ Рассмотрите возможность написания запроса для возврата небольшого количества записей
Написание запроса для возврата небольшого количества записей является наиболее интуитивно понятным руководством. Стремитесь всегда получать только те данные, которые действительно важны для вас. Вы можете добиться этого, сделав большинство мощных возможностей фильтрации доступными на языке запросов OData.
✔️ Рассмотрите возможность ограничения количества выбранных свойств минимальной
Некоторые администраторы проектов сильно настраивают свои процессы путем добавления настраиваемых полей. Глубокая настройка может привести к проблемам с производительностью системы при получении всех доступных столбцов в широких сущностях (например, WorkItems
). Аналитика основана на технологии Index Columnstore . Это означает, что хранение данных и обработка запросов организованы по столбцам. Таким образом, чем больше свойств, на которые ссылается запрос, тем дороже его обработка. Всегда стремитесь ограничивать набор свойств в запросах теми, которые действительно важны для вас в отчетном сценарии.
✔️ РАССМОТРИТЕ фильтрацию по свойствам суррогатного ключа даты (DateSK
суффикс)
Существует множество способов определения фильтра дат. Вы можете отфильтровать свойство даты напрямую (например, CreatedDate
), его аналог навигации (например, CreatedOnDate
) или его суррогатное представление ключа (например, CreatedDate
). Последний вариант обеспечивает оптимальную производительность и предпочтительнее, если требования к отчетам позволяют ему.
Например, следующий запрос получает все рабочие элементы, созданные с начала 2020 года.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge 20200101
✔️ Рассмотрите возможность фильтрации по суррогатным ключевым столбцам
Если вы хотите отфильтровать данные по значению связанного объекта (например, фильтрация рабочего элемента по имени проекта), у вас всегда есть два варианта. Вы можете использовать свойство навигации (например, Project/ProjectName
) или записать суррогатный ключ вперед и использовать его непосредственно в запросе (например, ProjectSK
).
Если вы создаете мини-приложение, рекомендуется использовать последний вариант. Когда ключ передается в рамках запроса, количество наборов сущностей, которые необходимо коснуться, уменьшается, а производительность улучшается.
Например, следующий запрос фильтруется с использованием WorkItems
свойства вместо ProjectSK
навигационного свойства.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK}
❌ ИЗБЕГАЙТЕ использования свойств Parent
, Children
или Revisions
в условиях $filter
или $expand
Рабочие элементы — это самые дорогие сущности в модели данных. У них есть несколько свойств навигации, которые можно использовать для доступа к связанным рабочим элементам: Parent
, , Children
Revisions
. Однако каждый раз, когда вы используете их в запросе, ожидается снижение производительности. Всегда спрашивайте, действительно ли вам нужно одно из этих свойств и потенциально обновите дизайн.
Например, вместо расширения Parent
можно получить больше рабочих элементов и использовать ParentWorkItemId
свойство для восстановления полной иерархии на стороне клиента. Проводите такую оптимизацию в каждом конкретном случае.
✔️ Рассмотрите возможность передачи VSTS.Analytics.MaxSize
предпочтений в заголовке
При выполнении запроса вы не знаете количество записей, возвращаемых запросом. Отправьте другой запрос с агрегатами или следуйте всем следующим ссылкам и получите весь набор данных. Аналитика учитывает VSTS.Analytics.MaxSize
предпочтения, что позволяет быстро обнаружить сбой в тех случаях, когда набор данных больше, чем может принять ваш клиент.
Этот параметр полезен в сценариях экспорта данных. Чтобы использовать его, необходимо добавить Prefer
заголовок в HTTP-запрос и задать VSTS.Analytics.MaxSize
неотрицательное значение. Значение VSTS.Analytics.MaxSize
представляет максимальное количество записей, которые можно принять. Если задать значение нулю, используется значение по умолчанию 200 K.
Например, следующий запрос возвращает рабочие элементы, если набор данных меньше или равен 1000 записям.
GET https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems HTTP/1.1
User-Agent: {application}
Prefer: VSTS.Analytics.MaxSize=1000
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Host: analytics.dev.azure.com/{OrganizationName}
Если набор данных превышает ограничение в 1000 записей, запрос немедленно завершается с следующей ошибкой.
Результат запроса содержит 1296 строк и превышает максимальный допустимый размер 1000. Уменьшите количество записей, применяя дополнительные фильтры.
Сведения о настройке максимального размера страницы см. в свойстве ODataPreferenceHeader.MaxPageSize.
Рекомендации по стилю запросов
-
✔️
$count
Используйте виртуальное свойство в методах агрегирования -
❌ Избегайте использования
$count
виртуального свойства в сегменте URL-адресов -
❌ ИЗБЕГАЙТЕ смешивания
$apply
и$filter
предложений в одном запросе - ✔️ Рассмотрите возможность использования псевдонимов параметров для разделения переменных частей запроса
- ✔️ Рассмотрите возможность структурирования запроса в соответствии с порядком оценки OData
- ✔️ Рассмотрите возможность проверки возможностей OData, описанных в заметках метаданных
✔️ Следует использовать $count
виртуальное свойство в методах агрегирования
Некоторые сущности обладают свойством Count
. Они упрощают некоторые сценарии создания отчетов при экспорте данных в другое хранилище. Однако эти столбцы не следует использовать в агрегациях в запросах OData. Вместо этого используйте виртуальное $count
свойство.
Например, следующий запрос возвращает общее количество рабочих элементов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$apply=aggregate($count as Count)
❌ Избегайте использования $count
виртуального свойства в сегменте URL-адресов
Хотя стандарт OData позволяет использовать $count
виртуальное свойство для наборов сущностей (например, _odata/v1.0/WorkItems/$count
не все клиенты могут правильно интерпретировать ответ. Поэтому рекомендуется использовать агрегации.
Например, следующий запрос возвращает общее количество рабочих элементов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$apply=aggregate($count as Count)
✔️ Рассмотрите возможность использования псевдонимов параметров для разделения переменных частей запроса
Псевдонимы параметров предоставляют элегантное решение для извлечения переменных, таких как значения параметров из основного текста запроса. Их можно использовать в выражениях, которые оценивают:
- Примитивное значение
- Комплексное значение
- Коллекция примитивных или сложных значений.
Дополнительные сведения см. в статье OData версии 4.0. Часть 2: Соглашения о URL-адресах - 5.1.1.13 Псевдонимы параметров. Параметры полезны, если текст запроса используется в качестве шаблона, который можно создать с помощью предоставленных пользователем значений.
Например, следующий запрос использует @createdDateSK
параметр для разделения значения от выражения фильтра.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge @createdDateSK
&$select=WorkItemId, Title, State
&@createdDateSK=20200101
❌ ИЗБЕГАЙТЕ смешивания $apply
и $filter
предложений в одном запросе
Если вы хотите добавить filter
в запрос, у вас есть два варианта. Вы можете сделать это либо с предложением $filter
, либо с сочетанием $apply=filter()
. Каждый из этих вариантов работает хорошо самостоятельно, но объединение их вместе может привести к некоторым непредвиденным результатам.
Несмотря на ожидание, которое может возникнуть, OData четко определяет порядок оценки. Кроме того, предложение $apply
имеет приоритет над $filter
. По этой причине следует выбрать один или другой, но избежать этих двух параметров фильтра в одном запросе. Важно, если запросы создаются автоматически.
Например, следующий запрос сначала фильтрует рабочие элементы StoryPoint gt 5
, агрегирует результаты по пути и, наконец, фильтрует результат по StoryPoints gt 2
. В этом порядке оценки запрос всегда возвращает пустой набор.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=StoryPoints gt 2
$apply=
filter(StoryPoints gt 5)/
groupby(
(Area/AreaPath),
aggregate(StoryPoints with sum as StoryPoints)
)
✔️ Рассмотрите возможность структурирования запроса в соответствии с порядком оценки OData
Так как сочетание условий $apply
и filter
в одном запросе может привести к потенциальной путанице, рекомендуется структурировать условия запросов в соответствии с порядком оценки.
$apply
$filter
$orderby
$expand
$select
$skip
$top
✔️ Рассмотрите возможность проверки возможностей OData, описанных в заметках метаданных
Если вы не уверены, какие возможности OData поддерживает Analytics, вы можете искать заметки в метаданных. Технический комитет по протоколу OData (OData) OASIS в репозитории TC GitHub содержит список доступных заметок.
Например, список поддерживаемых функций фильтра доступен в Org.OData.Capabilities.V1.FilterFunctions
заметке в контейнере сущностей.
<Annotation Term="Org.OData.Capabilities.V1.FilterFunctions">
<Collection>
<String>contains</String>
<String>endswith</String>
[...]
</Collection>
</Annotation>
Еще одна полезная заметка— Org.OData.Capabilities.V1.ExpandRestrictions
это описание свойств навигации, которые нельзя использовать в предложении $expand
. Например, следующая аннотация объясняет, что Revisions
в наборе сущностей WorkItems
не может быть развернут.
<EntitySet Name="WorkItems" EntityType="Microsoft.VisualStudio.Services.Analytics.Model.WorkItem">
[...]
<Annotation Term="Org.OData.Capabilities.V1.ExpandRestrictions">
<Record>
<PropertyValue Property="Expandable" Bool="true"/>
<PropertyValue Property="NonExpandableProperties">
<Collection>
<NavigationPropertyPath>Revisions</NavigationPropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</EntitySet>