Рекомендации по запросам OData Analytics для Azure DevOps
Azure DevOps Services | Azure DevOps Server 2022 — Azure DevOps Server 2019
Разработчики расширений могут воспользоваться рекомендациями, приведенными в этой статье, для разработки эффективных запросов OData к Аналитике для Azure DevOps. Следуя этим рекомендациям, можно убедиться, что запросы имеют хорошую производительность для времени выполнения и потребления ресурсов. Запросы, которые не соответствуют этим рекомендациям, могут привести к снижению производительности, с длительным временем ожидания отчета, запросами, превышающими допустимое потребление ресурсов или блоки служб.
Примечание.
Служба Аналитики автоматически включается и поддерживается в рабочей среде для всех Служб Azure DevOps Services. Интеграция Power BI и доступ к веб-каналу OData службы Аналитики общедоступны. Мы рекомендуем вам использовать его и дать нам отзыв.
Доступные данные зависят от версий. Последняя поддерживаемая версия: v2.0
последняя предварительная версия v4.0-preview
. Дополнительные сведения см. в разделе "Управление версиями API OData".
Примечание.
Служба Аналитики автоматически устанавливается и поддерживается в рабочей среде для всех новых коллекций проектов для Azure DevOps Server 2020 и более поздних версий. Интеграция Power BI и доступ к веб-каналу OData службы Аналитики общедоступны. Мы рекомендуем вам использовать его и дать нам отзыв. При обновлении с Azure DevOps Server 2019 можно установить службу Аналитики во время обновления.
Доступные данные зависят от версий. Последняя поддерживаемая версия: v2.0
последняя предварительная версия v4.0-preview
. Дополнительные сведения см. в разделе "Управление версиями API OData".
Примечание.
Служба Аналитики доступна в предварительной версии для Azure DevOps Server 2019. Его можно включить или установить для коллекции проектов. Интеграция Power BI и доступ к веб-каналу 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."
}
}
Ограничения
Do's
- ✔️ Do limit the query to the project(s), к которых у вас есть доступ
- ✔️ Укажите фильтр проекта внутри
$expand
предложения, если расширение может включать данные в другие, потенциально недоступные проекты - ✔️ Подождите или остановите операцию, если запрос превышает ограничения использования
- ✔️ Дожидайтесь или остановите операцию, если запрос завершается сбоем с истечением времени ожидания
- ✔️
DateSK
Включение илиDateValue
столбец в предложение приgroupby
агрегации по таблицам моментальных снимков - ✔️ Явно адресовать сущности с предложениями фильтра
- ✔️ Do use
WorkItemRevisions
entity set to load all the revisions for a given work item - ✔️ Использование пакетной конечной точки для длинных запросов
- ✔️ Задание часового пояса при фильтрации по столбцам дат
Consider
Заблокировано
- ❌ [ЗАБЛОКИРОВАНО] Не используйте сущности моментального снимка для других агрегатов
- ❌ [ЗАБЛОКИРОВАНО] Не используйте ключи сущностей в путях ресурсов для адресации сущностей
- ❌ [ЗАБЛОКИРОВАНО] Не расширяйте
Revisions
WorkItem
сущность - ❌ [ЗАБЛОКИРОВАНО] Не группировать по отдельным столбцам
- ❌ [ЗАБЛОКИРОВАНО] Не используйте
countdistinct
агрегирование - ❌ [ЗАБЛОКИРОВАНО] Не используйте пакетную конечную точку для отправки нескольких запросов
- ❌ [ЗАБЛОКИРОВАНО] Не используйте запросы, которые приводят к более чем 800 столбцам
Избегайте
- ❌ Избегайте агрегирования, которые могут привести к арифметическому переполнению
- ❌ Избегайте создания длинных запросов
✔️ Do limit the query to the project(s), к которых у вас есть доступ
Если запрос предназначен для данных из проекта, к которым у вас нет доступа, запрос возвращает сообщение "Доступ к проекту запрещен". Чтобы убедиться, что у вас есть доступ, убедитесь , что для всех проектов, запрашиваемых вами, задано разрешение "Просмотр аналитики ". Дополнительные сведения см. в разделе "Разрешения", необходимые для доступа к аналитике.
Если у вас нет доступа к проекту, отобразится следующее сообщение:
Результаты запроса включают данные в один или несколько проектов, для которых у вас нет доступа. Добавьте один или несколько фильтров проектов, чтобы указать проекты, к которые у вас есть доступ в сущности 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
) в предложение группирования. В противном случае результат может отображаться неправильно.
Например, если вы группировались только по AssignedTo
свойству и агрегировали WorkItemSnapshot
его с подсчетом, все числа рабочих элементов, назначенных людям, будут умножены на количество дней, когда каждое задание было активным. Хотя у вас может возникнуть ситуация, когда это нужный результат, такие случаи редки.
❌ [ЗАБЛОКИРОВАНО] Не используйте ключи сущностей в путях ресурсов для адресации сущностей
Синтаксис OData предоставляет способ доступа к определенной сущности, включив его ключи непосредственно в сегменты URL-адресов. Дополнительные сведения см. в статье OData версии 4.0. Часть 2. Соглашения о URL-адресах — 4.3, касающиеся сущностей. Хотя OData разрешает такое адресация, аналитика блокирует ее. Включение в запрос приводит к следующей ошибке.
Недопустимый запрос, указанный в 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
как описано в следующем разделе.
✔️ Do use WorkItemRevisions
entity set to load all the revisions for a given work item
Используйте 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. Смещение даты и времени не указано. Чтобы указать смещение, используйте любой из этих форматов ггГГ-ММ-ддZ, чтобы указать все, начиная с полуночи или гггг-ММ-ддThh: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
Рекомендации по повышению производительности
Do's
- ✔️ Измерение эффекта реализации руководства по производительности
- ✔️ Использование расширений агрегирования
- ✔️ Do укажите столбцы в предложении
$select
- ✔️ Do указать столбцы в параметре
$select
развертывания внутри$expand
предложения - ✔️ Определите фильтр
RevisedDateSK
при запросе данных исторических рабочих элементов (WorkItemRevisions
илиWorkItemSnapshot
наборов сущностей) - ✔️ Использование еженедельных или ежемесячных моментальных снимков для запросов тренда, охватывающих длительный период времени
- ✔️
Tags
Использование свойства коллекции для рабочих элементов при фильтрации по тегам - ✔️ Свойство DO use
TagNames
, если вы хотите отобразить все теги в рабочем элементе в виде текста - ✔️ Использование разбиения на страницах на основе сервера
- ✔️
$top
Использование параметра запроса для ограничения количества записей
Не давайте
- ❌ Не используйте
tolower
иtoupper
функции для сравнения без учета регистра - ❌ Не используйте несвязанное расширение с
$levels=max
- ❌ Не используйте
$top
и$skip
не запрашивайте параметры для реализации разбиения по страницам, управляемым клиентом
Consider
- ✔️ Рассмотрите возможность написания запросов для возврата небольших чисел записей
- ✔️ Рассмотрите возможность ограничения количества выбранных свойств минимальной
- ✔️ РАССМОТРИМ фильтрацию по свойствам ключей с суррогатной датой (
DateSK
суффикс) - ✔️ Рассмотрите возможность фильтрации по суррогатным ключевым столбцам
- ✔️ Рассмотрите возможность передачи
vsts.analytics.maxsize
предпочтений в заголовке
Избегайте
✔️ Измерение эффекта реализации руководства по производительности
Как и в случае с любыми рекомендациями по производительности, их не следует слепо реализовывать. Вместо этого всегда фиксируйте базовые показатели и измеряйте влияние внесенных изменений. Все рекомендации были созданы на основе взаимодействия с клиентами Аналитики, у которых были конкретные требования и проблемы. Эти рекомендации были рассмотрены как общие, так и потенциально полезны для тех, кто разрабатывает аналогичные запросы. Однако в редких случаях, следуя рекомендациям, не может оказать никакого влияния или даже негативного влияния на производительность. Вы должны измерить разницу, чтобы заметить это. Если это произойдет, предоставьте отзыв на портале Сообщество разработчиков.
Существует множество вариантов измерения производительности. Самым простым является запуск двух версий одного запроса непосредственно в браузере. Следите за временем, которое требуется в средствах разработчика. Например, можно использовать панель "Сеть" в средствах разработчика Microsoft Edge F12). Другим вариантом является запись этих сведений с помощью средства веб-отладки Fiddler.
Независимо от вашего подхода, выполните оба запроса несколько раз. Например, запустите запросы 30 раз каждый, чтобы иметь достаточно большой набор примеров. Затем определите характеристики производительности. Аналитика следует архитектуре с несколькими клиентами. Таким образом, другие операции, происходящие в то же время, могут повлиять на длительность запросов.
✔️ Использование расширений агрегирования
По крайней мере лучше всего повысить производительность запросов— использовать расширение агрегирования — расширение OData для агрегирования для агрегирования данных. С расширением агрегирования попросите службу сводные данные на стороне сервера и вернуть меньший ответ, чем то, что можно получить, применяя ту же функцию на стороне клиента. Наконец, аналитика оптимизирована для этого типа запросов, поэтому используйте ее.
Дополнительные сведения см. в разделе "Статистические данные".
✔️ Do укажите столбцы в предложении $select
Укажите нужные столбцы в предложении $select
. Аналитика основана на технологии Index Columnstore . Это означает, что данные являются как хранилищем, так и обработкой запросов, основаны на столбцах. Уменьшая набор свойств, вы ссылаетесь в $select
предложении, можно уменьшить количество столбцов, которые необходимо сканировать и повысить общую производительность запроса.
Например, следующий запрос указывает столбцы для рабочих элементов.
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$select=WorkItemId, Title, State
Примечание.
Azure DevOps поддерживает настройку процесса. Некоторые администраторы используют эту функцию и создают сотни настраиваемых полей. Если опущено $select
предложение, запрос возвращает все поля, включая настраиваемые поля.
✔️ Do указать столбцы в параметре $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
). Для обеспечения оптимальной производительности используйте последний. Это суррогатный ключ даты и представляет дату создания редакции или ее null
наличие для активных неполных редакций. Если вы хотите все даты, начиная с инклюзивного {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
✔️ Свойство DO use 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
не запрашивайте параметры для реализации разбиения по страницам, управляемым клиентом
С другими ИНТЕРФЕЙСами REST API, возможно, вы реализовали разбиение на страницах на основе клиента и $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
свойство, а не Project/ProjectName
свойство навигации.
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>