ویرگول
ورودثبت نام
onionj
onionjبرنامه نویس و مهندس عمران!
onionj
onionj
خواندن ۴ دقیقه·۴ سال پیش

انواع روش‌های پیاده سازی api Pagination

سلام , در این نوشته میخواهم چند روش مرسوم صفحه‌بندی در ای‌پی‌آی را معرفی کنم :)

در اندپوینت هایی که یک لیست بزرگ از آیتمها را بر می‌گردانند , ما به صفحه‌بندی نیاز داریم تا فشار زیادی به سرور, شبکه و... وارد نشود (آیتمها در اینجا میتواند لیستی از پستها و یا محصولات ما باشند).



اولین روش Offset Pagination :

این روش که دلیل معروفیتش قابلیت LIMIT و OFFSET در SQL ها است, میتواند در سرویس های کوچک استفاده شود, برای مثال با صدا زدن این اندپوینت سرور بیست آیتم بر می‌گرداند (آیتم 100 تا 120) :

GET/items?limit=20&offset=100

در حقیقت کاربر با offset به ما میگوید از آیتم چندم به بعد را میخواهد و با limit حداکثر تعداد آیتم های که میخواهد را مشخص میکند.

مثال:

کاربر این درخواست را میفرستد و 20 آیتم اول را میگیرد :

GET /items?limit=20

سپس درخواست دوم را میفرستد و 20 آیتم بعدی را میگیرد :

GET /items?limit=20&offset=20

باز هم وقتی کاربر به صفحه سوم میرود برای گرفتن 20 آیتم بعدی این درخواست را میفرستد و از آیتم 40ام به بعد را میگیرد:

GET /items?limit=20&offset=40

این درخواست ها در سمت سرور به کوئری های دیتابیس تبدیل میشوند, برای مثال کوئری درخواست سوم:

SELECT * FROM Items ORDER BY Id LIMIT 20 OFFSET 40;

مزیت ها:

  • پیاده سازی راحت , در بیشتر مواقع فقط باید پارامترها را مستقیم به کوئری های SQL بدهیم.
  • درخواست ها در سرور Stateless میباشد.
  • با پارامترهای sort_by سفارشی کار می کند.

نقاط ضعف:

  • این روش برای offset های بزرگ بهینه نیست و فشار زیادی به دیتابیس می‌آورد, برای مثال دیتابیس برای یک offset با عدد 1000000 باید از صفر شروع کند و یک‌میلیون آیتم را بشمارد ,سپس از آنجا 20 آیتم را بخواند و آن 20 آیتم را برگرداند , (یک میلیون آیتم اول را هم که شمرده بود دور بریزد!).
  • اگر آیتم جدید به دیتابیس اضافه کنیم امکان این وجود دارد که آیتم های تکراری برای کاربر ارسال شود.

سناریو آیتم تکراری:

  • درخواست اول کاربر:GET /items?offset=0&limit=15
  • در این مرحله ده آیتم به دیتابیس اضافه میکنیم...
  • درخواست دوم کاربر: GET /items?offset=15&limit=15 این درخواست فقط پنج آیتم جدید برای کاربر ارسال میکند و ده تا از آیتم‌ها تکراری هستند و کاربر آنها را در درخواست اول گرفته است ( در صفحه اول ده آیتم اضافه شده , به همین دلیل ده تا از آیتم های که قبلا عضو صفحه اول بودند به صفحه دوم منتقل شدند), کاربر برای حل کردن این مشکل باید این درخواست را به عنوان درخواست دومش ارسال میکرد تا آیتم تکراری نگیرد:
    /items?offset=25&limit=15
    اما کاربر که نمیدانست چند آیتم جدید به دیتابیس اضافه شده؟! :( .

حتی با وجود محدودیت‌ها، پیاده‌سازی و درک صفحه‌بندی Offset آسان است و می‌توان از آن در برنامه‌هایی استفاده کرد که لیست داده‌ها دارای تعداد و تغیرات کم هستند.



روش دوم keyset pagination :

این روش با استفاده از یکی از فیلد های دیتابیس عملیات مرتب کردن یا Sorting را انجام میدهد.

مثال:

فرض کنید آیتم های دیتابیس بر اساس تاریخ ایجاد آیتم به صورت نزولی مرتب شده است

  • کاربر برای گرفتن آخرین آیتم های ساخته شده درخواست میفرستد و 20 آیتم میگیرد :
    GET /items?limit=20
  • برای گرفتن آیتم های بعدی کاربر باید کمترین مقدار فیلد created یا همان تاریخ ایجاد را در آیتم های که در درخواست اول گرفته است بیابد (created : 2021-01-20T00:00:00) و برای درخواست دوم آنرا به عنوان فیلتر ارسال کند تا فقط آیتم های که تاریخ ایجادشان با این تاریخ مساوی و یا از آن کمتر است را دریافت کند :
    GET /items?limit=20&created:lte:2021-01-20T00:00:00
  • و باز هم وقتی کاربر میخواهد به صفحه بعد برود باید کمترین مقدار created را بیابد و درخواست سوم را بسازد :
    GET /items?limit=20&created:lte:2021-01-19T00:00:00

درخواست دوم وقتی به کوئری دیتابیس تبدیل میشود:

SELECT * FROM Items WHERE created <= '2021-01-20T00:00:00' ORDER BY Id LIMIT 20


مزیت ها:

  • با فیلترهای موجود بدون منطق اضافی کار می کند. فقط به یک پارامتر URL اضافی نیاز دارید.
  • وقتی داریم نسبت به جدیدترین آیتم ها آنها را مرتب میکنیم, زمانی که به دیتابیس آیتم های جدیدی اضافه شود باز هم به خوبی کار میکند.
  • فشار زیادی به دیتابیس وارد نمیشود.

نقاط ضعف:

  • حتی زمانی که کاربر نیازی به فیلتر کردن ندارد او را مجبور به اینکار میکند.
  • برای فیلدهای که امکان تکراری بودن در انها زیاد است کاربرد ندارد, مثل فیلد های دسته بندی.
  • برای کاربران API استفاده از فیلدهای sort_by سفارشی پیچیده است زیرا کاربر باید فیلتر را بر اساس فیلد مورد استفاده برای مرتب سازی تنظیم کند.

این نوع صفحه‌بندی میتواند برای داده‌های که دارای فیلد های مثل timestamp هستند استفاده شود.



روش سوم seek pagination :

این روش کامل کننده روش قبل است , با اضافه کردن یک after_id یا star_id میتوان بعضی از محدودیت های روش قبل را حذف کرد

مثال:

(فرض کنید میخواهیم آیتمها نسبت به زمان ساخته شدن آنها به صورت صعودی صفحه بندی شوند)

  • کاربر برای گرفتن 20 آیتم درخواست میفرستد :
    GET /items?limit=20
  • سپس وقتی میخواهد آیتم های صفحه بعد را بگیرد باید id آخرین آیتم گرفته شده را نیز برای سرور بفرستد , برای مثال در اینجا عدد 20 است :
    GET /items?limit=20&after_id=20
  • و به همین ترتیب برای درخواست سوم باید id آخرین آیتم گرفته شده در درخواست قبلی را برای سرور بفرستد تا سرور بداند از آن آیتم به بعد را باید برای کاربر برگرداند:
    GET /items?limit=20&after_id=40

درخواست دوم وقتی به کوئری دیتابیس تبدیل میشود :

SELECT * FROM Items WHERE Id > 20 LIMIT 20

در مثال بالا همه چیز به خوبی کار میکند , اما اگر بخواهیم دیتا را با فیلد ایمیل مرتب کنیم چه میشود؟
در این حالت کلاینت چنین درخواستی برای بک‌اند میفرستد:
GET /items?limit=20&after_id=20&sort_by=email
بک‌اند برای هر درخواست نیاز دارد دو کوئری دیتابیس بزند, ابتدا باید بداند ایمیل آن آیتم که کاربر id آن را در پارامتر after_id ارسال کرده است چیست, پس با کوئری اول ایمیل آن آیتم را میگرد, سپس در کوئری دوم آیتم هارا با استفاده از ایمیل و id مرتب میکند و فقط آیتم های که ایمیل انها بعد از آن ایمیل هست را بر میگرداند (ما بر اساس هر دو ستون، ایمیل و id مرتب‌سازی می‌کنیم تا اطمینان حاصل کنیم که مرتب‌سازی پایداری در صورت یکسان بودن دو ایمیل داریم؛ این برای حالت های که از فیلدی با خصوصیت تکرار بالا استفاده میکنیم حیاطی است)

کوئری اول:

SELECT email AS AFTER_EMAIL FROM Items WHERE Id = 20

کوئری دوم:

SELECT * FROM Items WHERE Email >= [AFTER_EMAIL] ORDER BY Email, Id LIMIT 20

مزیت ها:

  • زمانی که به دیتابیس آیتم های جدیدی اضافه شود باز هم به خوبی کار میکند.
  • عملکرد ثابت حتی با افست های بزرگ.


نقاط ضعف:

  • برای پیاده سازی در بک‌اند دارای پیچیدگی بیشتری نسبت به دو مورد اول دارد.
  • اگر آیتمی از دیتابیس حذف شوند، after_id ممکن است یک شناسه معتبر نباشد.

این روش صفحه‌بندی با اینکه به کمی کار بیشتر در بک‌اند نیاز دارد، اما تضمین می‌کند که پیچیدگی اضافی به کلاینت‌ها/کاربران API اضافه نمی‌شود، و حتی با جستجوهای بزرگ‌تر هم کارآمد باقی می‌ماند.


لینک اصلی مقاله :

https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination







۷
۲
onionj
onionj
برنامه نویس و مهندس عمران!
شاید از این پست‌ها خوشتان بیاید