TripPin часть 5 — разбиение по страницам
В этом руководстве рассматривается создание нового расширения источника данных для Power Query. Это руководство предназначено для последовательного выполнения каждого урока— каждый урок, созданный на основе соединителя, созданного на предыдущих уроках, постепенно добавляя новые возможности в соединитель.
В этом уроке вы научитесь:
- Добавление поддержки разбиения по страницам в соединитель
Многие ИНТЕРФЕЙСы REST API возвращают данные на страницах, требуя, чтобы клиенты выполняли несколько запросов для объединения результатов. Хотя существуют некоторые распространенные соглашения для разбиения на страницы (например , RFC 5988), они обычно различаются от API к API. К счастью, TripPin — это служба OData, а стандарт OData определяет способ выполнения разбиения на страницы с помощью значений odata.nextLink , возвращаемых в тексте ответа.
Чтобы упростить предыдущие итерации соединителя, TripPin.Feed
функция не знала о странице. Он просто анализирует то, что JSON был возвращен из запроса и отформатировал его как таблицу. Те, кто знаком с протоколом OData, могли заметить, что многие неправильные предположения были сделаны в формате ответа (например, если есть value
поле, содержащее массив записей).
На этом занятии вы улучшаете логику обработки ответов, учитывая ее страницу. Будущие учебники делают логику обработки страниц более надежной и способной обрабатывать несколько форматов ответа (включая ошибки из службы).
Примечание.
Вам не нужно реализовать собственную логику разбиения на страницы с соединителями на основе OData.Feed, так как она обрабатывает все это автоматически.
Список проверка по страницам
При реализации поддержки разбиения по страницам вам потребуется знать следующие сведения о API:
- Как запросить следующую страницу данных?
- Включает ли механизм разбиения страниц вычисление значений или вы извлекаете URL-адрес для следующей страницы из ответа?
- Как вы знаете, когда остановить разбиение по страницам?
- Существуют ли параметры, связанные с разбиением по страницам, о которых следует знать? (например, "размер страницы")
Ответ на эти вопросы влияет на то, как вы реализуете логику разбиения по страницам. Хотя существует некоторое количество повторного использования кода в реализации разбиения на страницы (например, использование Table.GenerateByPage, большинство соединителей в конечном итоге требуют пользовательской логики.
Примечание.
Этот урок содержит логику разбиения по страницам для службы OData, которая соответствует определенному формату. Ознакомьтесь с документацией по API, чтобы определить изменения, которые необходимо внести в соединитель для поддержки формата разбиения по страницам.
Общие сведения о разбиении на страницы OData
Разбиение по страницам OData определяется заметками nextLink, содержащимися в полезных данных ответа. Значение nextLink содержит URL-адрес следующей страницы данных. Вы узнаете, есть ли другая страница данных, ищете odata.nextLink
поле во внешнем объекте в ответе. odata.nextLink
Если нет поля, вы прочитали все данные.
{
"odata.context": "...",
"odata.count": 37,
"value": [
{ },
{ },
{ }
],
"odata.nextLink": "...?$skiptoken=342r89"
}
Некоторые службы OData позволяют клиентам предоставлять максимальный размер страницы, но это зависит от службы независимо от того, следует ли его учитывать. Power Query должен иметь возможность обрабатывать ответы любого размера, поэтому вам не нужно беспокоиться об указании предпочтения размера страницы— вы можете поддерживать то, что служба вызывает у вас.
Дополнительные сведения о разбиении по страницам на основе сервера см. в спецификации OData.
Тестирование TripPin
Перед исправлением реализации разбиения на страницы проверьте текущее поведение расширения из предыдущего руководства. Следующий тестовый запрос извлекает таблицу Люди и добавляет столбец индекса для отображения текущего количества строк.
let
source = TripPin.Contents(),
data = source{[Name="People"]}[Data],
withRowCount = Table.AddIndexColumn(data, "Index")
in
withRowCount
Включите Fiddler и запустите запрос в пакете SDK Power Query. Обратите внимание, что запрос возвращает таблицу с восемью строками (индекс 0–7).
Если посмотреть на текст ответа от fiddler, вы увидите, что он фактически содержит @odata.nextLink
поле, указывающее, что есть больше страниц данных.
{
"@odata.context": "https://services.odata.org/V4/TripPinService/$metadata#People",
"@odata.nextLink": "https://services.odata.org/v4/TripPinService/People?%24skiptoken=8",
"value": [
{ },
{ },
{ }
]
}
Реализация разбиения по страницам для TripPin
Теперь вы собираетесь внести следующие изменения в расширение:
- Импорт общей
Table.GenerateByPage
функции GetAllPagesByNextLink
Добавление функции, которая используетсяTable.GenerateByPage
для приклеивания всех страниц вместеGetPage
Добавление функции, которая может считывать одну страницу данныхGetNextLink
Добавление функции для извлечения следующего URL-адреса из ответа- Обновление
TripPin.Feed
для использования новых функций чтения страниц
Примечание.
Как указано ранее в этом руководстве, логика разбиения по страницам будет отличаться от источников данных. Реализация здесь пытается разбить логику на функции, которые следует повторно использовать для источников, использующих следующие ссылки , возвращаемые в ответе.
Table.GenerateByPage
Чтобы объединить (потенциально) несколько страниц, возвращаемых источником в одну таблицу, мы будем использовать Table.GenerateByPage
. Эта функция принимает в качестве аргумента getNextPage
функцию, которая должна делать только то, что его имя предлагает: получение следующей страницы данных. Table.GenerateByPage
будет многократно вызывать getNextPage
функцию, каждый раз при передаче результатов, созданных при последнем вызове, пока он не возвращает null
сигнал обратно, что больше страниц не доступны.
Так как эта функция не является частью стандартной библиотеки Power Query, необходимо скопировать его исходный код в PQ-файл.
Реализация GetAllPagesByNextLink
Текст GetAllPagesByNextLink
функции реализует getNextPage
аргумент функции для Table.GenerateByPage
. Он вызывает GetPage
функцию и получает URL-адрес для следующей страницы данных из NextLink
поля записи из предыдущего meta
вызова.
// Read all pages of data.
// After every page, we check the "NextLink" record on the metadata of the previous request.
// Table.GenerateByPage will keep asking for more pages until we return null.
GetAllPagesByNextLink = (url as text) as table =>
Table.GenerateByPage((previous) =>
let
// if previous is null, then this is our first page of data
nextLink = if (previous = null) then url else Value.Metadata(previous)[NextLink]?,
// if NextLink was set to null by the previous call, we know we have no more data
page = if (nextLink <> null) then GetPage(nextLink) else null
in
page
);
Реализация GetPage
Функция GetPage
будет использовать Web.Contents для получения одной страницы данных из службы TripPin и преобразования ответа в таблицу. Он передает ответ из Web.ContentsGetNextLink
функции, чтобы извлечь URL-адрес следующей страницы и задать его в meta
записи возвращаемой таблицы (страница данных).
Эта реализация представляет собой немного измененную версию TripPin.Feed
вызова из предыдущих учебников.
GetPage = (url as text) as table =>
let
response = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
body = Json.Document(response),
nextLink = GetNextLink(body),
data = Table.FromRecords(body[value])
in
data meta [NextLink = nextLink];
Реализация GetNextLink
Функция GetNextLink
просто проверка текст ответа для @odata.nextLink
поля и возвращает его значение.
// In this implementation, 'response' will be the parsed body of the response after the call to Json.Document.
// Look for the '@odata.nextLink' field and simply return null if it doesn't exist.
GetNextLink = (response) as nullable text => Record.FieldOrDefault(response, "@odata.nextLink");
Сборка
Последним шагом для реализации логики разбиения на страницах является обновление TripPin.Feed
для использования новых функций. Теперь вы просто вызываете эту GetAllPagesByNextLink
функцию, но в последующих руководствах вы добавите новые возможности (например, принудительное применение схемы и логику параметра запроса).
TripPin.Feed = (url as text) as table => GetAllPagesByNextLink(url);
При повторном выполнении того же тестового запроса , полученного ранее в руководстве, теперь вы увидите средство чтения страниц в действии. Вы также должны увидеть, что в ответе есть 24 строки, а не восемь.
Если вы посмотрите на запросы в fiddler, теперь вы увидите отдельные запросы для каждой страницы данных.
Примечание.
Вы заметите повторяющиеся запросы на первую страницу данных из службы, которая не является идеальной. Дополнительный запрос является результатом проверка поведения подсистемы M. Пропустить эту проблему и устранить ее в следующем руководстве, где будет применена явная схема.
Заключение
На этом занятии показано, как реализовать поддержку разбиения на страницы для REST API. Хотя логика, скорее всего, зависит от API, шаблон, установленный здесь, должен быть повторно использован с незначительными изменениями.
На следующем занятии вы узнаете, как применить явную схему к данным, перейдя за рамки простых text
и number
полученных Json.Document
типов данных.