Cursor Pagination ڈیٹا لوڈ کرنے کا ایک صفحہ وار طریقہ ہے جو ریکارڈز کے ترتیب شدہ سیٹ میں نیویگیشن کے لیے ایک منفرد کرسر استعمال کرتا ہے۔ GraphQL Specification (2025) کے مطابق، کرسر پر مبنی صفحہ بندی متحرک ڈیٹا کے ساتھ کام کرنے والے APIs کے لیے تجویز کردہ معیار ہے۔ Cursor pagination Offset طریقہ کار کی اہم خامیوں کو ختم کرتا ہے: اندراج کے دوران عدم استحکام اور بڑی آفسیٹس پر کارکردگی کا بگاڑ۔
اہم نکات
Cursor Pagination ایک صفحہ وار لوڈنگ کا طریقہ ہے جہاں سرور ڈیٹا کے ساتھ ایک خاص پوائنٹر — کرسر — واپس کرتا ہے۔ کلائنٹ ریکارڈز کا اگلا بیچ حاصل کرنے کے لیے اگلی درخواست میں اس کرسر کا استعمال کرتا ہے۔ کرسر موجودہ صفحہ کے آخری عنصر کی منفرد شناخت ہے۔
Offset صفحہ بندی کے برعکس، جہاں کلائنٹ کہتا ہے “مجھے 20 ریکارڈز کے ساتھ صفحہ 5 دیں،” کرسر صفحہ بندی مختلف طریقے سے کام کرتی ہے: “مجھے ID = 83 والے ریکارڈ کے بعد 20 ریکارڈ دیں۔” سرور WHERE id > 83 اور LIMIT 20 کے ساتھ استفسار چلاتا ہے۔ یہ طریقہ اس بات کی ضمانت دیتا ہے کہ ہر ریکارڈ اندراج سے قطع نظر بالکل ایک صفحہ میں آتا ہے۔
کرسر صفحہ بندی کے تصور کو Relay Connection (GraphQL) تصریح کی بدولت وسیع پیمانے پر اپنایا گیا، جس نے کرسر پر مبنی صفحہ بندی کو جدید APIs کے لیے معیار بنا دیا۔ Relay جوابی فارمیٹ کی وضاحت کرتا ہے: edges (کرسرز کے ساتھ ریکارڈز کی صف)، pageInfo (hasNextPage، hasPreviousPage، startCursor، endCursor)۔
کرسر صفحہ بندی کوئی نئی تکنیک نہیں ہے — یہ ویب سے بہت پہلے ڈیٹابیسز میں استعمال ہوتی تھی۔ SQL میں اسے keyset pagination یا seek method کہا جاتا ہے۔ یہ طریقہ 2015 میں Relay تصریح کی اشاعت کے بعد APIs میں مقبول ہوا، جس نے HTTP ٹرانسپورٹ پر یکسانیت کے لیے کرسر فارمیٹ کو base64-انکوڈڈ سٹرنگ کے طور پر رسمی شکل دی۔
کرسر صفحہ بندی کا بنیادی اصول یہ ہے کہ استفسار مقام تعین کرنے کے لیے آفسیٹ کی بجائے انڈیکسڈ فیلڈ پر WHERE شرط استعمال کرتا ہے۔ آگے کی سمت کے لیے WHERE id > last_id استعمال ہوتا ہے؛ پیچھے کی سمت کے لیے WHERE id < first_id۔ B-tree انڈیکس کرسر کے بعد پہلا ریکارڈ O(log n) میں ڈھونڈتا ہے، جو مستحکم جوابی وقت فراہم کرتا ہے۔
-- کرسر '83' کے بعد 20 ریکارڈ حاصل کریں
SELECT id, title, created_at
FROM posts
WHERE id < 83
ORDER BY id DESC
LIMIT 20;
-- کرسر '83' سے پہلے 20 ریکارڈ حاصل کریں (پیچھے)
SELECT id, title, created_at
FROM posts
WHERE id > 83
ORDER BY id ASC
LIMIT 20;
کرسر سادہ (ایک ID قدر) یا پیچیدہ (متعدد فیلڈز پر مشتمل) ہو سکتا ہے۔ سادہ کرسرز ریکارڈ کی بنیادی کلید ہوتے ہیں، مثال کے طور پر، خودکار اضافہ id یا UUID۔ مرکب کرسرز غیر منفرد فیلڈز کے مطابق ترتیب دینے کے لیے استعمال ہوتے ہیں، مثال کے طور پر (created_at, id)، جہاں id ایک جیسے ٹائم سٹیمپ پر انفرادیت کو یقینی بناتا ہے۔
ایک عام API فارمیٹ base64-انکوڈڈ سٹرنگ کے طور پر کرسر ہے۔ سرور کرسر کو ڈی کوڈ کرتا ہے، قدر نکالتا ہے، اور SQL استفسار بناتا ہے۔ Base64 انکوڈنگ کرسر کی داخلی ساخت کو کلائنٹ سے چھپاتی ہے اور پسماندہ مطابقت کو توڑے بغیر فارمیٹ تبدیل کرنے کی اجازت دیتی ہے۔ کلائنٹ جواب کے endCursor فیلڈ میں کرسرز وصول کرتا ہے اور انہیں اگلی درخواست میں سٹرنگ کے طور پر منتقل کرتا ہے۔
کرسر صفحہ بندی دو طرفہ نیویگیشن کی حمایت کرتی ہے۔ آگے بڑھنے (next) کے لیے موجودہ صفحہ کے آخری عنصر کا کرسر استعمال ہوتا ہے؛ پیچھے (previous) کے لیے پہلے عنصر کا کرسر۔ درخواست میں after اور before پیرامیٹرز سمت کا تعین کرتے ہیں: after کرسر کے بعد ریکارڈ لیتا ہے، before کرسر سے پہلے ریکارڈ لیتا ہے۔
کرسر اور Offset صفحہ بندی کے درمیان انتخاب API ڈیزائن کرتے وقت اہم تعمیراتی فیصلوں میں سے ایک ہے۔ ہر طریقہ میں طاقت اور کمزوریاں ہیں جو اس کی اطلاقیت کا تعین کرتی ہیں۔ Cursor pagination متحرک ڈیٹا والے منظرناموں میں جیتتا ہے؛ Offset صوابدیدی نیویگیشن والے منظرناموں میں جیتتا ہے۔
| خصوصیت | Cursor | Offset |
|---|---|---|
| اندراج پر استحکام | اعلی (کوئی نقل نہیں) | کم (صفحات کی منتقلی) |
| بڑے سیٹوں پر کارکردگی | O(log n) — مستحکم | O(n) — بڑھنے سے گھٹتی ہے |
| صفحہ نمبر کے ذریعے نیویگیشن | نہیں | ہاں (page=5) |
| نفاذ کی پیچیدگی | درمیانی | کم |
| REST حمایت | cursor/before/after | page/offset |
| GraphQL حمایت | Relay معیار | تجویز نہیں کیا جاتا |
Offset صفحہ بندی OFFSET مقام تک مکمل ٹیبل سکین کرتی ہے۔ offset=100000 پر، ڈیٹابیس 100000 قطاریں پڑھتا اور چھوڑتا ہے، چاہے LIMIT 20 ہی کیوں نہ ہو۔ MySQL اور PostgreSQL OFFSET کو بہتر نہیں کر سکتے — یہ SQL میں LIMIT/OFFSET کے نفاذ کی ایک خصوصیت ہے۔ کرسر صفحہ بندی B-tree انڈیکس استعمال کرتی ہے جو O(log n) میں مقام ڈھونڈتا ہے۔
Offset کا ایک اضافی مسئلہ پیچھے کی طرف صفحہ بندی کرتے وقت ریکارڈز کا “چھوڑنا” ہے۔ اگر کسی صارف نے صفحہ 5 لوڈ کیا اور اسی لمحے نئے ریکارڈز شامل ہو گئے، تو صفحہ 6 کی درخواست کرنے پر وہ صفحہ 5 کا ریکارڈ دوبارہ دیکھیں گے یا نئے ریکارڈز سے محروم ہو جائیں گے۔ Cursor pagination اس منظرنامے کو مکمل طور پر ختم کرتا ہے: کرسر سیٹ میں ایک مخصوص جگہ کی طرف اشارہ کرتا ہے اور اندراج مقام کو تبدیل نہیں کرتا۔
آئیے بیک اینڈ (Kotlin + Spring) اور کلائنٹ (Android + Retrofit) پر کرسر صفحہ بندی کے نفاذ کو دیکھتے ہیں۔ سرور after، before، limit پیرامیٹرز قبول کرتا ہے اور کرسرز اور pageInfo کے ساتھ ریکارڈز کی فہرست واپس کرتا ہے۔ ایک عام جواب میں صفحہ بندی UI کے انتظام کے لیے hasNextPage اور hasPreviousPage ہوتا ہے۔
@GetMapping("/posts")
fun getPosts(
@RequestParam after: Long?,
@RequestParam(defaultValue = "20") limit: Int
): CursorResponse<Post> {
val cursor = after ?: Long.MAX_VALUE
val posts = repository.findByIdLessThanOrderByIdDesc(
cursor, PageRequest.of(0, limit)
)
val endCursor = posts.lastOrNull()?.id
return CursorResponse(
data = posts,
pageInfo = PageInfo(
hasNextPage = posts.size == limit,
endCursor = endCursor
)
)
}
کلائنٹ پر، کرسر صفحہ بندی Paging 3 سے PagingSource کے ذریعے نافذ کی جاتی ہے، جہاں کلید ایک کرسر (Long) ہے۔ PagingSource.load LoadParams.key وصول کرتا ہے — آخری لوڈ کردہ ریکارڈ کا کرسر۔ LoadResult.Page ڈیٹا اور nextKey — اگلے صفحے کے لیے کرسر — واپس کرتا ہے۔ جب nextKey = null ہو، صفحہ بندی مکمل ہو جاتی ہے۔
// Retrofit API
interface PostApi {
@GET("posts")
suspend fun getPosts(
@Query("after") after: Long?,
@Query("limit") limit: Int = 20
): CursorResponse<Post>
}
// کرسر کلید کے ساتھ PagingSource
class PostPagingSource(
private val api: PostApi
) : PagingSource<Long, Post>() {
override suspend fun load(
params: LoadParams<Long>
): LoadResult<Long, Post> = try {
val response = api.getPosts(
after = params.key,
limit = params.loadSize
)
val nextKey = response.pageInfo.endCursor
LoadResult.Page(
data = response.data,
prevKey = null,
nextKey = nextKey
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
GraphQL میں، کرسر صفحہ بندی Relay Connection پیٹرن کے ذریعے نافذ کی جاتی ہے۔ ہر قسم میں ایک Connection (pageInfo اور edges کے ساتھ) اور ایک Edge (node + cursor) ہوتا ہے۔ استفسار first، after، last، before پیرامیٹرز منتقل کرتا ہے۔ سرور کرسرز کے ساتھ edges کی صف اور hasNextPage/hasPreviousPage کے ساتھ pageInfo واپس کرتا ہے۔
کرسر صفحہ بندی متحرک ڈیٹا کے ساتھ کام کرنے والے APIs کے لیے تجویز کی جاتی ہے جہاں ریکارڈز بار بار شامل یا حذف کیے جاتے ہیں۔ کلاسک مثالیں: سوشل نیٹ ورک میں نیوز فیڈ، چیٹ پیغامات، لین دین کی تاریخ، پوسٹ کے تبصرے۔ ان تمام منظرناموں میں، مستقل مزاجی اور نقلوں کی عدم موجودگی اہم ہے۔
ایسے منظرنامے ہیں جہاں Offset صفحہ بندی زیادہ آسان ہے: ایڈمن پینلز جہاں صفحہ نمبر کے ذریعے نیویگیشن کی ضرورت ہے؛ صفحہ بندی کے ساتھ تلاش جہاں نتائج تبدیل ہو سکتے ہیں؛ رپورٹس اور تجزیہ جہاں صفحہ 5 کے لیے ایک مقررہ لنک کی ضرورت ہے۔ ان صورتوں میں، کرسر کے فوائد نفاذ کی پیچیدگی سے زیادہ نہیں ہوتے۔
کرسر صفحہ بندی صوابدیدی صفحہ پر “چھلانگ” لگانے کی حمایت نہیں کرتی — صارف “صفحہ 5” پر کلک کرکے وہاں نہیں جا سکتا۔ یہ ایک تعمیراتی حد ہے: صفحات کی کل تعداد گننا علیحدہ COUNT استفسار کی ضرورت ہے، جو بڑی ٹیبلز کے لیے مہنگا ہو سکتا ہے۔ ایسے معاملات میں ایک ملا جلا طریقہ: ڈیٹا کے لیے کرسر + صفحہ بندی کے لیے count۔
اکثر پوچھے گئے سوالات
کرسر ایک منفرد ریکارڈ شناخت کنندہ ہے جو ڈیٹا سیٹ میں ایک مقام کی طرف اشارہ کرتا ہے۔ یہ سادہ (ایک ریکارڈ ID) یا مرکب (متعدد فیلڈز) ہو سکتا ہے۔ کلائنٹ صفحہ کے آخری ریکارڈ کا کرسر وصول کرتا ہے اور اگلا بیچ حاصل کرنے کے لیے اسے اگلی درخواست میں منتقل کرتا ہے۔
Cursor pagination نئے ریکارڈز شامل ہونے پر منتقلی کا شکار نہیں ہوتا — ہر عنصر بالکل ایک صفحہ میں آتا ہے۔ یہ پہلی n قطاروں کو اسکین کرنے کے بجائے انڈیکس استعمال کرکے بڑے والیوم پر رفتار بھی برقرار رکھتا ہے۔ Offset آسان ہے لیکن متحرک ڈیٹا کے لیے غیر مستحکم ہے۔
ہاں، کرسر صفحہ بندی GraphQL سے منسلک نہیں ہے۔ اسے کسی بھی REST API میں کرسر کو استفساری پیرامیٹر ?after=83&limit=20 کے طور پر منتقل کرکے نافذ کیا جا سکتا ہے۔ جواب میں endCursor اور hasNextPage کے ساتھ pageInfo ہونا چاہیے — یہ کلائنٹ کو داخلی کرسر ساخت جانے بغیر لوڈنگ کو منظم کرنے کی اجازت دیتا ہے۔
خودکار اضافہ ID بہترین انتخاب ہے: یکساں طور پر بڑھتا ہے، تبدیل نہیں ہوتا، مؤثر طریقے سے انڈیکس ہوتا ہے۔ UUID v7 (وقت کے مطابق ترتیب شدہ) بھی موزوں ہے۔ ٹائم سٹیمپ ایک ہی وقت میں نقل پیدا کر سکتے ہیں، لہٰذا اسے ID کے ساتھ ملائیں: (created_at, id) کرسر کی انفرادیت کو یقینی بنانے کے لیے۔
کرسر صفحہ بندی صفحات کی کل تعداد فراہم نہیں کرتی — یہ اس کی حد ہے۔ اگر آپ کو کل معلومات کی ضرورت ہے تو ایک ہی فلٹرز کے ساتھ علیحدہ COUNT استفسار چلائیں۔ بڑی ٹیبلز کے لیے، EXPLAIN کے ذریعے تخمینی گنتی یا تجزیات سے کیشڈ کل استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں