Поделиться через


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).

QueryWithoutPaging.

Если посмотреть на текст ответа от 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

Теперь вы собираетесь внести следующие изменения в расширение:

  1. Импорт общей Table.GenerateByPage функции
  2. GetAllPagesByNextLink Добавление функции, которая используется Table.GenerateByPage для приклеивания всех страниц вместе
  3. GetPage Добавление функции, которая может считывать одну страницу данных
  4. GetNextLink Добавление функции для извлечения следующего URL-адреса из ответа
  5. Обновление TripPin.Feed для использования новых функций чтения страниц

Примечание.

Как указано ранее в этом руководстве, логика разбиения по страницам будет отличаться от источников данных. Реализация здесь пытается разбить логику на функции, которые следует повторно использовать для источников, использующих следующие ссылки , возвращаемые в ответе.

Table.GenerateByPage

Чтобы объединить (потенциально) несколько страниц, возвращаемых источником в одну таблицу, мы будем использовать Table.GenerateByPage. Эта функция принимает в качестве аргумента getNextPage функцию, которая должна делать только то, что его имя предлагает: получение следующей страницы данных. Table.GenerateByPage будет многократно вызывать getNextPage функцию, каждый раз при передаче результатов, созданных при последнем вызове, пока он не возвращает null сигнал обратно, что больше страниц не доступны.

Так как эта функция не является частью стандартной библиотеки Power Query, необходимо скопировать его исходный код в PQ-файл.

Текст 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 просто проверка текст ответа для @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 строки, а не восемь.

QueryWithPaging.

Если вы посмотрите на запросы в fiddler, теперь вы увидите отдельные запросы для каждой страницы данных.

Fiddler.

Примечание.

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

Заключение

На этом занятии показано, как реализовать поддержку разбиения на страницы для REST API. Хотя логика, скорее всего, зависит от API, шаблон, установленный здесь, должен быть повторно использован с незначительными изменениями.

На следующем занятии вы узнаете, как применить явную схему к данным, перейдя за рамки простых text и number полученных Json.Documentтипов данных.

Следующие шаги

Часть 6 TripPin — схема