TripPin ส่วนที่ 5 - การแบ่งหน้า

บทช่วยสอนหลายส่วนนี้ครอบคลุมถึงการสร้างส่วนขยายแหล่งข้อมูลใหม่Power Queryแหล่งข้อมูล บทช่วยสอนมีไว้เพื่อให้แต่ละบทเรียนที่สร้างบนตัวเชื่อมต่อที่สร้างขึ้นในบทเรียนก่อนหน้านี้ เพิ่มความสามารถใหม่ๆ — ลงในตัวเชื่อมต่อของคุณแบบเพิ่มหน่วย

ในบทเรียนนี้ คุณจะต้อง:

  • เพิ่มการสนับสนุนการแบ่งหน้าไปยังตัวเชื่อมต่อ

Rest API หลายตัวจะส่งกลับข้อมูลใน "หน้า" ซึ่งไคลเอ็นต์ร้องขอหลายข้อเพื่อให้ผลลัพธ์รวมกัน แม้ว่าจะมีบางแบบทั่วไปเกี่ยวกับการแบ่งหน้า (เช่น RFC 5988)โดยทั่วไปแล้วจะแตกต่างกันจาก API ไปยัง API โชคดีที่ TripPin เป็นบริการ OData และมาตรฐาน OData กําหนดวิธีการแบ่งหน้าโดยใช้ ค่า odata.nextLink ที่ส่งกลับในเนื้อความของการตอบสนอง

เพื่อลด ความซับซ้อนของการ iterationsก่อนหน้าของตัวเชื่อมต่อ TripPin.Feed ฟังก์ชันไม่ทราบ หน้า ซึ่งแยกวิเคราะห์สิ่ง JSON ที่ถูกส่งกลับจากการร้องขอ และจัดรูปแบบเป็นตาราง ผู้ที่คุ้นเคยกับโพรโทคอล OData อาจสังเกตเห็นว่า มีสมมติฐานที่ไม่ถูกต้องจํานวนมากเกี่ยวกับรูปแบบของการตอบสนอง (เช่น สมมติว่ามีเขตข้อมูลที่ประกอบด้วยอาร์เรย์ของ value ระเบียน)

ในบทเรียนนี้ คุณจะปรับปรุงตรรกะการจัดการการตอบสนองของคุณ โดยไม่ทราบหน้าเพจ บทช่วยสอนในอนาคตจะทให้การจัดการหน้ามีตรรกะที่มีประสิทธิภาพมากขึ้นและสามารถจัดการรูปแบบการตอบสนองหลายรูปแบบ (รวมถึงข้อผิดพลาดจากบริการ)

หมายเหตุ

คุณไม่ต้องใช้ตรรกะการแบ่งหน้าของคุณเองด้วยตัวเชื่อมต่อที่ยึดตาม OData.Feedเนื่องจากจะจัดการทั้งหมดให้คุณโดยอัตโนมัติ

รายการตรวจสอบการแบ่งหน้า

เมื่อใช้การสนับสนุนการแบ่งหน้า คุณจะต้องทราบสิ่งต่อไปนี้เกี่ยวกับ API ของคุณ:

  • คุณจะขอหน้าถัดไปของข้อมูลได้อย่างไร
  • กลไกการแบ่งหน้าเกี่ยวข้องกับการคํานวณค่า หรือคุณแยก URL ของหน้าถัดไปออกจากการตอบสนองหรือไม่
  • คุณจะทราบได้อย่างไรว่าเมื่อใดที่ควรหยุดการแบ่งหน้า
  • มีพารามิเตอร์ที่เกี่ยวข้องกับการแบ่งหน้าที่คุณควรทราบหรือไม่ (เช่น "ขนาดหน้า")

คําตอบของคําถามเหล่านี้จะส่งผลกระทบต่อวิธีที่คุณจะใช้ตรรกะการแบ่งหน้าของคุณ ในขณะที่มีโค้ดบางส่วนถูกใช้งานซ้.แบบแบ่งหน้า (เช่น การใช้ Table.GenerateByPageตัวเชื่อมต่อส่วนใหญ่จะต้องใช้ตรรกะแบบปรับแต่งเอง

หมายเหตุ

บทเรียนนี้ประกอบด้วยตรรกะการเพจของบริการ OData ซึ่งเป็นไปตามรูปแบบเฉพาะ ตรวจสอบเอกสารประกอบของ API ของคุณเพื่อตรวจสอบการเปลี่ยนแปลงที่คุณจะต้องแก้ไขในตัวเชื่อมต่อของคุณเพื่อสนับสนุนรูปแบบการแบ่งหน้า

ภาพรวมของ OData Paging

OData paging ถูกขับเคลื่อน โดย nextLinknotations ที่มีอยู่ภายในส่วนข้อมูลการตอบสนอง ค่า nextLink ประกอบด้วย URL ในหน้าถัดไปของข้อมูล คุณจะทราบว่ามีหน้าอื่นของข้อมูลโดยการค้นหาเขตข้อมูล odata.nextLink ในวัตถุภายนอกสุดในการตอบกลับหรือไม่ ถ้าไม่มีเขตข้อมูล odata.nextLink คุณได้อ่านข้อมูลของคุณทั้งหมด

{
  "odata.context": "...",
  "odata.count": 37,
  "value": [
    { },
    { },
    { }
  ],
  "odata.nextLink": "...?$skiptoken=342r89"
}

บริการ OData บางตัวอนุญาตให้ไคลเอ็นต์ ใส่ขนาดหน้าสูงสุดแต่ขึ้นอยู่กับบริการไม่ว่าจะให้ตามลักษณะนั้นหรือไม่ Power Queryตอบสนองทุกขนาด เพื่อให้คุณไม่ต้องกังวลเกี่ยวกับการระบุการตั้งค่าขนาดหน้า คุณสามารถสนับสนุนสิ่งที่บริการแสดงให้คุณ —

ข้อมูลเพิ่มเติมเกี่ยวกับ Server-Driven Paging สามารถดูได้ที่ข้อมูลเฉพาะของ OData

การทดสอบ TripPin

ก่อนที่จะแก้ไขการดําเนินการการแบ่งหน้าของคุณ ให้ยืนยันลักษณะการดําเนินการปัจจุบันของส่วนขยายจาก บทช่วยสอนก่อนหน้า คิวรีทดสอบต่อไปนี้จะดึงข้อมูลตาราง บุคคล และเพิ่มคอลัมน์ดัชนีเพื่อแสดงการนับแถวปัจจุบันของคุณ

let
    source = TripPin.Contents(),
    data = source{[Name="People"]}[Data],
    withRowCount = Table.AddIndexColumn(data, "Index")
in
    withRowCount

เปิด fiddler และเรียกใช้คิวรีVisual Studioคิวรี คุณจะสังเกตเห็นว่าคิวรีส่งกลับตารางที่มี 8 แถว (ดัชนี 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 พารามิเตอร์ซ้ํา ๆ จนกว่าจะ null ได้รับ พารามิเตอร์ของฟังก์ชันต้องใช้อาร์กิวเมนต์เดียว nullable table และส่งกลับ

getNextPage = (lastPage) as nullable table => ...

การโทรแต่ละครั้ง getNextPage จะได้รับผลลัพธ์จากการเรียกก่อนหน้านี้

// The getNextPage function takes a single argument and is expected to return a nullable table
Table.GenerateByPage = (getNextPage as function) as table =>
    let        
        listOfPages = List.Generate(
            () => getNextPage(null),            // get the first page of data
            (lastPage) => lastPage <> null,     // stop when the function returns null
            (lastPage) => getNextPage(lastPage) // pass the previous page to the next function call
        ),
        // concatenate the pages together
        tableOfPages = Table.FromList(listOfPages, Splitter.SplitByNothing(), {"Column1"}),
        firstRow = tableOfPages{0}?
    in
        // if we didn't get back any pages of data, return an empty table
        // otherwise set the table type based on the columns of the first page
        if (firstRow = null) then
            Table.FromRows({})
        else        
            Value.ReplaceType(
                Table.ExpandTableColumn(tableOfPages, "Column1", Table.ColumnNames(firstRow[Column1])),
                Value.Type(firstRow[Column1])
            );

บันทึกย่อบางอย่างเกี่ยวกับ Table.GenerateByPage :

  • ฟังก์ชัน getNextPage จะต้องเรียกใช้ URL หน้าถัดไป (หรือหมายเลขหน้า หรือค่าอื่นที่ใช้เพื่อใช้ตรรกะการแบ่งหน้า) ซึ่งโดยทั่วไปแล้วจะเสร็จสิ้น meta ได้โดยการเพิ่มค่าลงในหน้าก่อนที่จะส่งกลับ
  • คอลัมน์และชนิดตารางของตารางรวม (เช่น ทุกหน้าเข้าด้วยกัน) จะได้มาจากหน้าแรกของข้อมูล ฟังก์ชัน getNextPage ควรปรับมาตรฐานข้อมูลแต่ละหน้า
  • การเรียกครั้งแรก getNextPage เพื่อรับพารามิเตอร์ null
  • getNextPage ต้องส่งกลับค่า null เมื่อไม่มีหน้าที่เหลืออยู่

เนื้อความของ 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.Contents ไปยังฟังก์ชัน เพื่อแยก URL ของหน้าถัดไป และตั้งค่าบน GetNextLink ระเบียนของ 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);

ถ้าคุณเรียกใช้คิวรีทดสอบ เดียวกันอีกครั้ง จากก่อนหน้านี้ในบทช่วยสอน ตอนนี้คุณควรเห็นโปรแกรมอ่านหน้าในการปฏิบัติการ นอกจากนี้คุณควรเห็นว่าคุณมี 20 แถวในการตอบกลับแทนที่จะเป็น 8

QueryWithPaging

ถ้าคุณดูการร้องขอใน fiddler ขณะนี้คุณควรเห็นการร้องขอแยกต่างหากของแต่ละหน้าของข้อมูล

Fiddler

หมายเหตุ

คุณจะสังเกตเห็นการร้องขอที่แตกต่างจากการร้องขอหน้าแรกของข้อมูลจากบริการซึ่งไม่เหมาะอย่างยิ่ง การร้องขอเพิ่มเติมเป็นผลมาจากการลักษณะการงานตรวจสอบ Schema ของกลไกจัดการ M ละเว้นปัญหานี้ในขณะนี้และแก้ไขปัญหาในบท ช่วยสอนถัดไป ที่คุณจะใช้รูปแบบการอธิบาย

บทสรุป

บทเรียนนี้แสดงวิธีการใช้การสนับสนุนการแบ่งหน้าของ Rest API ในขณะที่ตรรกะจะมีแนวโน้มที่จะแตกต่างกันระหว่าง API รูปแบบที่สร้างที่นี่ควรสามารถใช้งานใหม่ด้วยการปรับเปลี่ยนเพียงเล็กน้อย

ในบทเรียนถัดไป คุณจะได้ดูวิธีการใช้สคีมาที่ชัดเจนกับข้อมูลของคุณ หากเกินชนิดของ text ข้อมูล number และอย่างง่ายที่คุณจะได้รับ Json.Document จาก

ขั้นตอนถัดไป

TripPin ส่วนที่ 6 - Schema