K2 Listview pagination with Rest Api datasource

Ton Wanchai
3 min readMay 16, 2020

--

เมื่อพูดถึง K2 SmartForm เชื่อว่า K2 developer ทุกคนต้องเคยใช้งาน List View แน่นอน เพราะเป็น Tool ที่ช่วยให้เราสามารถแสดงผลข้อมูลจาก SmartObject ออกมาเป็นตารางหรือ Grid ได้โดยตรง และสิ่งนึงที่มักมีคู่กับการแสดงผลแบบ List ก็คือการแบ่ง Page การแสดงผลของข้อมูล เพื่อไม่ให้มันแสดงออกมาเยอะเกินไปจน user ตาลาย และยังช่วยลด overhead ที่เกิดจากการโหลดข้อมูลจาก Database ทั้ง Table ด้วย

แต่การทำ Paging ของ List View มีจุดสำคัญที่ Developer ควรคำนึงถึง (เนื้อหาใน blog นี้จะลงไปแตะ technical ระดับนึงนะครับ)

[Data source ที่เป็น SQL Server]

รวมถึง Database provider อื่นๆ การทำ paging ของ SmartObject จะเหมือนการใช้คำสั่ง limit, offset ใน sql query โดยตรง นั่นคือ record ที่ return กลับมาที่ SmartObject จะมีจำนวนเท่ากับ page size ที่เรากำหนดไว้จริงๆ ซึ่งเท่ากับการลด overhead ได้ตามวัตถุประสงค์ของการทำ paging

จากรูปตัวอย่าง ผมมีตารางเก็บข้อมูลงานวิจัยบน SQL Server จำนวน 12,856 รายการ ใช้ SmartObject ดึงข้อมูลจำนวน 10 รายการ ใช้เวลาแค่ 0.02 วินาที (ซึ่งถือว่าค่อนข้างเร็วเลย ใครที่เจอเหตุการณ์ว่า K2 ดึงข้อมูลช้า อาจต้องไปเช็คเรื่อง sql query หรือการทำ index ดูนะครับ)

SmartObject ดึงข้อมูลตรงจาก SQL Server database

[Data source ที่เป็น REST Api]

ทั้ง data source ที่เป็น REST api รวมถึง Web Service และ WCF นั้น การทำ paging ของ SmartObject จะทำแค่ที่ฝั่ง K2 Server เพราะข้อมูลจาก REST api ทั้งหมดจะถูกส่งมากองไว้ที่ K2 ก่อน จากนั้นตัว SmartObject จึงจะหยิบเฉพาะ record ที่ตรงกับ Page Number / Page Size ส่งกลับไปแสดงผล

จากรูปตัวอย่าง ฐานข้อมูลงานวิจัยเดิม แต่ผมเขียน REST Api ครอบแล้วเอา SmartObject ต่อเพื่อดึงข้อมูล โดยมีการรับ parameter 2 ตัว ได้แก่ pageSize และ pageNumber โดยหาก pageSize เป็น null จะหมายถึงการดึงข้อมูลทั้งหมด

REST Api code

จากนั้นลองสร้าง SmartObject และเรียกข้อมูลดู ผลที่ได้เป็นดังนี้ (scnull = null)

SmartObject ดึงข้อมูลตรงจาก REST Api

จะเห็นว่าใช้เวลาถึง 3.4 วินาทีสำหรับการดึงข้อมูลแค่ 10 record สาเหตุก็เพราะตัว REST service broker นั้นจะดึงข้อมูลจาก REST api โดยที่ไม่ได้ส่ง parameter เกี่ยวกับ paging ไปด้วยนั่นเอง แปลว่าข้อมูลทั้ง 12856 record จะถูกดึงมากองไว้ที่ K2 server ก่อนค่อยทำ paging

ถ้าเราลองไปดู Log ที่ฝั่ง REST Api จะเห็นว่ามี parameter แค่ pageSize=0, pageNumber=0 ซึ่งก็เป็น parameter ของ action ที่เราสร้างไว้ (พอกำหนดเป็น null ตัว broker เลยใส่ default ให้เราเป็น 0 เลยทำให้ระบบ return ข้อมูลทั้งหมดกลับมา) และในส่วนของ header ก็ไม่เห็นข้อมูลที่เกี่ยวกับการทำ paging เช่นกัน

[List View ที่เรียกข้อมูลจาก REST Api ผ่านทาง SmartObject]

ถ้าเราสร้าง List view จาก SmartObject method ตามที่อธิบายไปด้านบนจะเกิดอะไรขึ้น? เราก็จะสามารถทำ List view ที่ทำ paging ได้ปกตินั่นแหละ แต่เบื้องหลังนั้น ทุกครั้งที่มีการคลิกเปลี่ยน page ระบบจะไปดึง record ทั้ง 1 หมื่นกว่า record มาใหม่เสมอ!! แล้วถ้าข้อมูลที่มีโอกาส return จาก REST api มีหลักแสน หรือหลักล้าน record ล่ะ? เราก็คงต้องนั่งจิบกาแฟรอวนไปเวลาคลิกเปลี่ยนเพจ

คำถามต่อมา แล้วถ้าเราดึงข้อมูลมาแบบ Nested object โดยเอา serialized data มา กองไว้ที่ Textarea ก่อน แล้วค่อยส่งเข้าไป deserialized type array ล่ะ จะดีขึ้นไหม? คำตอบคือ เราจะลดการดึงข้อมูลจาก REST api เหลือแค่รอบเดียว ไม่ว่าจะเปลี่ยน page กี่ครั้งก็ตาม

แต่… ปัญหาคือ เราต้องส่งก้อน serialized data ไปที่ K2 server ทุกครั้งเวลาเปลี่ยน page มันจึงเกิดปัญหาใหม่ขึ้นมา คือเรื่องของ network traffic โดย REST api ที่ return thesis กว่าหมื่น record กลับมามีขนาด 8 MB เท่ากับทุกครั้งที่เปลี่ยน page ใน List view ตัว SmartForm จะต้อง post data ขนาด 8MB กลับไปที่ server ด้วย

ขนาดของ Content length ที่ post กลับไป K2 server เวลาคลิกเปลี่ยน page

[แล้วถ้าต้องใช้ SmartObject ต่อกับ REST Api จริงๆ ต้องทำยังไง?]

ถ้า K2 developer ต้องเจอสถานการณ์ที่ต้องดึง data จาก REST หรือ web service จริงๆ อย่างหลีกเลี่ยงไม่ได้ ควรทำยังไงดี? เพราะหน่วยงานที่เราไปทำ solution ให้ก็อาจมี service ของเค้าเอง ที่ทำงานเป็น REST Api อยู่แล้ว (ยุคนี้เห็นเค้าฮิตทำ micro service กันด้วย) เราก็คงต้องหาวิธีไป Connect กับ service ที่มีอยู่แล้วของเค้า

โดยส่วนตัวคิดว่าอาจต้องพิจารณาเป็นกรณีไป

1. ถ้าข้อมูลที่จะส่งกลับมาจาก REST ไม่เยอะมาก เช่น ไม่เกินหลัก 1–2 พัน record อาจไม่มีผลอะไร เพราะต่อให้ต้องดึงมากองไว้ที่ K2 server ก่อน แต่ถ้าขนาดข้อมูลไม่เยอะ ก็อาจไม่กระทบกับ performance แบบมีนัยสำคัญ (ยกเว้นแต่ละ record ต้องไปเรียก function check ค่าบางอย่างกับระบบก่อน return กลับมา เคสนี้มีผลอยู่ดี)

2. ถ้าข้อมูลที่จะส่งกลับมาจาก REST api มีเยอะ เช่น หลักหมื่น หลักแสน record ขึ้นไป เราอาจต้องจำกัดจำนวน record ที่จะส่งกลับมาที่ฝั่ง REST api เลย เช่น ไม่ว่าจะเจอ record ที่ตรงกับเงื่อนไขจำนวนเท่าไหร่ แต่เราจะส่งกลับมาแค่ 200 record เท่านั้น

วิธีนี้อาจแก้ปัญหาได้เบื้องต้น แต่ข้อเสียคือ user จะไม่รู้เลยว่าจริงๆ แล้วข้อมูลที่ตรงกับเงื่อนไขมีจำนวนเท่าไรกันแน่ และจำนวน page ที่คลิกได้ใน List view ก็จะไม่สัมพันธ์กับจำนวน record ที่แท้จริง เพราะมันจะเปลี่ยน page ไปได้ถึงแค่ record ที่ 200 เท่านั้นเอง

3. ทำให้ List view สามารถส่ง parameter pageNumber / pageSize กลับไปยัง REST api ผ่านทาง SmartObject method ได้จริงๆ แต่ ณ ตอนนี้ผมยังไม่เจอ event อะไรเกี่ยวกับการเปลี่ยน page บน List view เหมือนกัน ดังนั้นถ้าเราจะเลือกทางนี้ ก็คงต้องลงแรงกันหน่อย ด้วยการสร้าง Custom pagination control ขึ้นมาใช้งานเอง (ถ้าใครเจอวิธีจับ event page changed ของ list view ฝากสะกิดผมด้วยนะครับ ฮ่า)

สำหรับ blog นี้ขอจบไว้เพียงเท่านี้ก่อนครับ ครั้งหน้าผมจะเอาวิธีแก้ปัญหาแบบที่ 3 มาเล่าให้ฟัง ตอนนี้ทำออกมาใกล้จะสำเร็จแล้ว ตัวอย่างดังรูปด้านล่าง

--

--

Ton Wanchai

Full stack developer and business process automation consultant