Cursor Pagination mobil inkişafda — bu nədir, prinsipi və reallaşdırılması

Müəllif: IT Sectr Dərc olunub: 2026-03-11 Oxuma vaxtı: 9 dəq

Cursor Pagination — sıralanmış qeyd dəstində naviqasiya üçün unikal kursor istifadə edən məlumatların səhifələnmə üsuludur. GraphQL Specification (2025)-ə görə, kursor səhifələnməsi dinamik məlumatlarla işləyən API-lər üçün tövsiyə olunan standartdır. Cursor Pagination Offset yanaşmasının əsas çatışmazlıqlarını aradan qaldırır: əlavələr zamanı qeyri-sabitlik və böyük yerdəyişmələrdə performans itkisi.

Əsas məqamlar

  • Cursor Pagination — hər qeydin naviqasiya üçün unikal identifikator-kursora malik olduğu səhifələnmə üsulu.
  • Kursor — məlumat dəstində mövqeyin unikal markeri (adətən ID, UUID, timestamp), əlavələr zamanı dəyişmir.
  • Sabitlik — sorğular arasında əlavə edilmiş yeni qeydlər kursoru dəyişdirmir, dublikat və boşluqları aradan qaldırır.
  • Performans — WHERE id > cursor sorğusu indeksdən səmərəli istifadə edir, böyük dəstlərdə sürət itirmir.
  • Məhdudiyyət — kursor səhifələnməsi səhifə nömrəsi ilə naviqasiyanı dəstəkləmir (5-ci səhifəyə atlamaq mümkün deyil).

Cursor Pagination nədir?

Cursor Pagination (kursor səhifələnməsi) — serverin məlumatlarla birlikdə xüsusi göstərici — kursor qaytardığı səhifələnmə üsuludur. Müştəri növbəti sorğuda bu kursoru istifadə edərək növbəti qeyd hissəsini əldə edir. Kursor cari səhifənin son elementinin unikal identifikatorudur.

Offset səhifələnməsindən fərqli olaraq, burada müştəri “mənə 20 qeyddən 5-ci səhifəni ver” demir, kursor səhifələnməsi fərqli işləyir: “mənə ID = 83 olan qeyddən sonra 20 qeyd ver”. Server WHERE id > 83 şərti və LIMIT 20 ilə sorğu yerinə yetirir. Bu yanaşma hər bir qeydin əlavələrdən asılı olmayaraq dəqiq bir səhifəyə düşəcəyinə zəmanət verir.

Kursor səhifələnməsi konsepsiyası Relay Connection (GraphQL) spesifikasiyası sayəsində geniş yayılmışdır və cursor-based pagination-ı müasir API-lər üçün standart etmişdir. Relay cavab formatını müəyyənləşdirir: edges (kursorlarla qeydlər massivi), pageInfo (hasNextPage, hasPreviousPage, startCursor, endCursor).

Yaranma tarixi

Cursor-pagination yeni texnika deyil — veb meydana çıxmamışdan çox əvvəl verilənlər bazalarında istifadə olunurdu. SQL-də buna keyset pagination və ya seek method deyilir. Metod 2015-ci ildə Relay spesifikasiyasının dərcindən sonra API-lərdə populyarlaşdı və kursor formatını HTTP vasitəsilə ötürmə üçün base64 kodlaşdırılmış sətir kimi rəsmiləşdirdi.

Kursor səhifələnməsi necə işləyir

Kursor səhifələnməsinin əsas prinsipi — sorğu yerdəyişmə deyil, mövqeləşdirmə üçün indeksləşdirilmiş sahədə WHERE şərtindən istifadə edir. İrəli istiqamət üçün WHERE id > last_id, geri istiqamət üçün isə WHERE id < first_id tətbiq olunur. B-tree indeksi kursor daxil olduqdan sonra ilk qeydi O(log n) müddətində tapmağa imkan verir ki, bu da sabit cavab müddətini təmin edir.

sql
-- Kursor '83'-dən sonra 20 qeyd əldə et
SELECT id, title, created_at
FROM posts
WHERE id < 83
ORDER BY id DESC
LIMIT 20;

-- Kursor '83'-dən ƏVVƏL 20 qeyd əldə et (geri)
SELECT id, title, created_at
FROM posts
WHERE id > 83
ORDER BY id ASC
LIMIT 20;

Kursor formatı

Kursor sadə (ID dəyəri) və ya mürəkkəb (bir neçə sahədən ibarət) ola bilər. Sadə kursorlar qeydin əsas açarıdır, məsələn autoinkrement id və ya UUID. Mürəkkəb kursorlar qeyri-unikal sahələr üzrə çeşidləmə üçün istifadə olunur, məsələn (created_at, id), burada id eyni zaman damğalarında unikallığı təmin edir.

Tipik API formatı — kursor base64 kodlaşdırılmış sətir şəklindədir. Server kursoru dekodlaşdırır, dəyəri çıxarır və SQL sorğusu qurur. Base64 kodlaşdırılması kursorun daxili strukturunu müştəridən gizlədir və geriyə uyğunluğu itirmədən formatı dəyişməyə imkan verir. Müştəri kursorları cavabın endCursor sahəsində alır və onları növbəti sorğuda sətir kimi ötürür.

İrəli və geri naviqasiya

Kursor səhifələnməsi ikitərəfli naviqasiyanı dəstəkləyir. İrəli hərəkət (next) üçün cari səhifənin son elementinin kursoru, geri (previous) üçün — ilk elementin kursoru istifadə olunur. Sorğuda after və before parametrləri istiqaməti müəyyənləşdirir: after kursor daxil olduqdan sonra qeydləri götürür, before — kursor daxil olana qədər.

Cursor vs Offset pagination

Kursor və Offset səhifələnməsi arasında seçim API dizayn edərkən əsas memarlıq məsələlərindən biridir. Hər metodun güclü və zəif tərəfləri var. Cursor-pagination dinamik məlumat ssenarilərində, Offset — ixtiyari naviqasiya ssenarilərində üstünlük təşkil edir.

XüsusiyyətCursorOffset
Əlavələr zamanı sabitlikYüksək (dublikatsız)Aşağı (səhifə yerdəyişməsi)
Böyük dəstlərdə performansO(log n) — sabitO(n) — artdıqca azalır
Səhifə nömrəsi ilə naviqasiyaYoxBəli (page=5)
Tətbiq mürəkkəbliyiOrtaAşağı
REST dəstəyicursor/before/afterpage/offset
GraphQL dəstəyiRelay standartıTövsiyə edilmir

Offset nə üçün böyük miqyasda uduzur

Offset səhifələnməsi OFFSET mövqeyinə qədər tam cədvəl skan etməsini yerinə yetirir. Offset=100000 olduqda, LIMIT 20 olsa belə, verilənlər bazası 100000 sətri oxuyur və atlayır. MySQL və PostgreSQL OFFSET-i optimallaşdıra bilmir — bu SQL-də LIMIT/OFFSET tətbiqinin xüsusiyyətidir. Cursor-pagination B-tree indeksindən istifadə edir, mövqeyi O(log n) müddətində tapır.

Offset-in əlavə problemi — geri səhifələnmə zamanı qeydlərin „atlanması”. İstifadəçi 5-ci səhifəni yükləyibsə və bu anda yeni qeydlər əlavə olunubsa, 6-cı səhifəni sorğulayarkən 5-ci səhifədən qeydi yenidən görəcək və ya yeni qeydləri atlayacaq. Cursor-pagination bu ssenarini tamamilə aradan qaldırır: kursor dəstdə konkret yeri göstərir və əlavələr mövqeyi dəyişmir.

Cursor Pagination-ın tətbiqi

Kursor səhifələnməsinin backenddə (Kotlin + Spring) və müştəridə (Android + Retrofit) tətbiqini nəzərdən keçirək. Server after, before, limit parametrlərini qəbul edir və kursorlar və pageInfo ilə qeyd siyahısını qaytarır. Tipik cavab səhifələnmə UI-ni idarə etmək üçün hasNextPage və hasPreviousPage ehtiva edir.

kotlin
@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
        )
    )
}

Android-də müştəri tətbiqi

Müştəri tərəfdə kursor səhifələnməsi Paging 3-dən PagingSource vasitəsilə tətbiq olunur, burada açar kursor (Long) rolunu oynayır. PagingSource.load LoadParams.key — son yüklənmiş qeydin kursorunu alır. LoadResult.Page məlumatları və nextKey — növbəti səhifə üçün kursoru qaytarır. NextKey = null olduqda — səhifələnmə tamamlanır.

kotlin
// Retrofit API
interface PostApi {
    @GET("posts")
    suspend fun getPosts(
        @Query("after") after: Long?,
        @Query("limit") limit: Int = 20
    ): CursorResponse<Post>
}

// PagingSource kursor açarı ilə
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)
    }
}

Relay vasitəsilə GraphQL tətbiqi

GraphQL-də kursor səhifələnməsi Relay Connection nümunəsi vasitəsilə tətbiq olunur. Hər tip Connection (pageInfo və edges ilə) və Edge (node + cursor) var. Sorğu first, after, last, before parametrlərini ötürür. Server qaytarır kursorlarla edges massivi və hasNextPage/hasPreviousPage ilə pageInfo.

Cursor Pagination nə vaxt istifadə edilməlidir

Cursor-pagination qeydlərin tez-tez əlavə olunduğu və ya silindiyi dinamik məlumatlarla işləyən API-lər üçün tövsiyə olunur. Klassik nümunələr: sosial şəbəkədə xəbər lentləri, söhbət mesajları, əməliyyat tarixçəsi, post şərhləri. Bütün bu ssenarilərdə ardıcıllıq və dublikatların olmaması vacibdir.

  • Söhbətlər və messencerlər — hər yeni mesaj siyahının yuxarısına əlavə olunur. Offset səhifələnməsi hər yeni mesajda pozulur.
  • Sosial şəbəkələr və lentlər — postlar fasiləsiz dərc olunur. Kursor səhifələnməsi istifadəçinin heç bir postu qaçırmayacağına zəmanət verir.
  • Sifariş və əməliyyat tarixçəsi — məlumatlar daha az dəyişir, lakin maliyyə hesabatları üçün ardıcıllıq kritikdir.
  • Böyük həcmli məlumatlı API-lər — milyonlarla qeyd. Cursor-pagination Offset-in yavaşlamağa başladığı yerdə performansı qoruyur.
  • GraphQL API — Relay standartı spesifikasiyaya uyğun olaraq cursor-based pagination istifadəsini tələb edir.
  • Sonsuz sürüşdürmə ilə mobil tətbiqlər — istifadəçi aşağı sürüşdürərək yeni hissələri yükləyir. Kursor yanaşması dublikatsız hamar UX təmin edir.

Cursor-pagination nə vaxt uyğun deyil

Offset səhifələnməsinin daha əlverişli olduğu ssenarilər mövcuddur: səhifə nömrəsi ilə naviqasiyanın tələb olunduğu inzibati panellər; nəticələrin dəyişə biləcəyi səhifələmə ilə axtarış; 5-ci səhifəyə sabit keçidin lazım olduğu hesabatlar və analitika. Bu hallarda kursorun üstünlükləri tətbiq mürəkkəbliyindən üstün deyil.

Kursor səhifələnməsi ixtiyari səhifəyə „atlanmanı” dəstəkləmir — istifadəçi “Səhifə 5” düyməsini sıxıb ora keçə bilməz. Bu memarlıq məhdudiyyətidir: ümumi səhifə sayını hesablamaq üçün ayrıca COUNT sorğusu tələb olunur, bu da böyük cədvəllər üçün baha başa gələ bilər. Belə hallarda hibrid yanaşma: məlumatlar üçün cursor + səhifələmə üçün count.

Tez-tez verilən suallar

Cursor Pagination-da kursor nədir?

Kursor — məlumat dəstində mövqeyi göstərən qeydin unikal identifikatorudur. Sadə (qeyd ID-si) və ya mürəkkəb (bir neçə sahə) ola bilər. Müştəri səhifənin son qeydinin kursorunu alır və növbəti hissəni əldə etmək üçün onu növbəti sorğuda ötürür.

Cursor Pagination Offset-dən nə ilə yaxşıdır?

Cursor-pagination yeni qeydlər əlavə edildikdə yerdəyişməyə məruz qalmır — hər element dəqiq bir səfihəyə düşür. İlk n sətirləri skan etmək əvəzinə indekslərdən istifadə etməklə böyük həcmlərdə sürəti qoruyur. Offset daha sadədir, lakin dinamik məlumatlar üçün qeyri-sabitdir.

Kursor səhifələnməsini GraphQL olmadan tətbiq etmək mümkündürmü?

Bəli, kursor səhifələnməsi GraphQL-ə bağlı deyil. İstənilən REST API-də kursoru sorğu parametri kimi ötürməklə tətbiq edilə bilər ?after=83&limit=20. Cavabda endCursor və hasNextPage ilə pageInfo olmalıdır — bu, müştəriyə kursorun daxili strukturunu bilmədən yükləməni idarə etməyə imkan verir.

Hansı kursoru istifadə etməli — ID, UUID yoxsa timestamp?

Autoinkrement ID — optimal seçim: monotonik artır, dəyişmir, səmərəli indekslənir. UUID v7 (zaman sıralı) da uyğundur. Timestamp eyni vaxtda dublikatlar verə bilər, buna görə də ID ilə birləşdirilir: (created_at, id) kursorun unikallığını təmin etmək üçün.

Kursor səhifələnməsində ümumi səhifə sayını necə öyrənmək olar?

Kursor səhifələnməsi ümumi səhifə sayını təqdim etmir — bu onun məhdudiyyətidir. Total haqqında məlumat lazımdırsa, eyni filtrlərlə ayrıca COUNT sorğusu yerinə yetirin. Böyük cədvəllər üçün EXPLAIN vasitəsilə təxmini hesablama və ya analitikadan keşlənmiş total istifadə edin.

Nəticə

  • Cursor Pagination — yerdəyişmə əvəzinə qeydin unikal identifikatoru ilə naviqasiya edən səhifələnmə üsulu.
  • Kursor əlavələr zamanı dəstin sabitliyinə zəmanət verir: yeni qeydlər artıq yüklənmiş səhifələri dəyişdirmir.
  • Performans B-tree indekslərindən istifadə sayəsində böyük həcmli məlumatlarda yüksək (O(log n)) qalır.
  • Cursor-pagination dinamik məlumatlar üçün uyğundur: söhbətlər, xəbər lentləri, əməliyyatlar, şərhlər.
  • Əsas məhdudiyyət — səhifə nömrəsi ilə naviqasiyanın olmaması və ixtiyari səhifəyə atlama imkanının olmaması.
  • Tətbiq WHERE id kursor daxil olduqdan sonra, after/before parametrləri və cavabda pageInfo istifadə edir.
  • Standart — Relay Connection GraphQL, lakin kursor parametrləri ilə REST API də geniş yayılmışdır.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun