GoDasturchi
Pagination: Cursor-Based

Pagination: Cursor-Based

Sahifalash: Cursor asosida

Kitobni "45-sahifa"dan o'qishni davom ettirasiz deylik. Agar kimdir kitobning BOSHIDAGI 3 betni OLIB TASHLASA, sizning "45-sahifa"ngiz ENDI boshqa matnga to'g'ri keladi — siz nimanidir O'TKAZIB YUBORASIZ yoki QAYTA o'qiysiz. Lekin agar siz sahifa RAQAMI o'rniga, kitobga JISMONIY ZAKLADKA (bookmark) qo'ysangiz — "...gacha o'qidim" — bu muammo BO'LMAYDI: zakladka HAR DOIM to'g'ri joyni ko'rsatadi, hatto oldingi betlar o'zgarsa ham. Cursor-based pagination — aynan shu "zakladka" g'oyasi: sahifa RAQAMI (offset) o'rniga, OXIRGI ko'rilgan yozuvning O'ZI ishlatiladi.

example.go
package main

import (
	"fmt"
	"strconv"
)

type Book struct {
	ID string
}

type Page struct {
	Books      []*Book
	NextCursor string
}

// listAfter — books ID bo'yicha o'sib boruvchi tartibda saralangan deb faraz qilinadi
func listAfter(books []*Book, cursor string, limit int) Page {
	start := 0
	if cursor != "" {
		cursorNum, _ := strconv.Atoi(cursor)
		for i, b := range books {
			idNum, _ := strconv.Atoi(b.ID)
			if idNum > cursorNum {
				start = i
				break
			}
			start = i + 1
		}
	}
	end := start + limit
	if end > len(books) {
		end = len(books)
	}
	page := books[start:end]
	next := ""
	if end < len(books) {
		next = page[len(page)-1].ID
	}
	return Page{Books: page, NextCursor: next}
}

func printPage(p Page) {
	var ids []string
	for _, b := range p.Books {
		ids = append(ids, b.ID)
	}
	fmt.Println(ids, "next:", p.NextCursor)
}

func main() {
	books := []*Book{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}

	page1 := listAfter(books, "", 2)
	printPage(page1)

	page2 := listAfter(books, page1.NextCursor, 2)
	printPage(page2)

	page3 := listAfter(books, page2.NextCursor, 2)
	printPage(page3)
}

cursor — oxirgi olingan yozuvning ID'si (bo'sh bo'lsa "boshidan" degani). listAfter HAR SAFAR "cursor'dan KATTA ID'lar"ni qidiradi — offset kabi "nechinchi ELEMENT"ni emas, BALKI "qaysi ELEMENTDAN keyin"ni so'raydi. NextCursor — agar YANA yozuv qolgan bo'lsa, KEYINGI so'rovda ishlatiladigan cursor'ni qaytaradi; bo'sh bo'lsa — bu OXIRGI sahifa degani.

Bu naqshning ustunligi shunda: agar sahifalar ORASIDA (masalan foydalanuvchi 1-sahifani o'qib bo'lguncha) BIROR yozuv O'CHIRILSA yoki qo'shilsa, offset asosidagi sahifalash CHALKASHIB ketadi (ba'zi yozuvlar IKKI MARTA ko'rinadi yoki UMUMAN ko'rinmaydi), cursor asosidagi sahifalash esa BUNDAY MUAMMODAN XOLI — chunki u "nechinchi o'rinda" emas, "QAYSI yozuvdan keyin" deb so'raydi.

>_ Exercise

Offset asosidagi sahifalashning O'CHIRISHDAGI MUAMMOSINI, cursor bilan SOLISHTIRIB ko'ring.

  • offsetPage(books []*Book, limit, offset int) []*Book funksiyasini yozing ('Pagination: Limit & Offset' darsidagi paginate bilan bir xil mantiq)
  • original ro'yxat ({"1"}..{"5"}) uchun offsetPage(original, 2, 0) ni chaqirib, natijani chop eting
  • "2" ID'li kitob O'CHIRILGAN deb faraz qiling (afterDelete — {"1"},{"3"},{"4"},{"5"}), va offsetPage(afterDelete, 2, 2) natijasini chop eting — "3" O'TKAZIB YUBORILGANINI ko'rasiz
  • listAfter bilan xuddi shu ikki holatni (original va afterDelete) taqqoslang — cursor '3'ni O'TKAZIB YUBORMASLIGINI ko'rsating

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Cursor-based sahifalash — "nechinchi o'rin" (offset) emas, "QAYSI yozuvdan keyin" (cursor) so'raydi; shu sabab, sahifalar orasida ma'lumot o'chirilsa yoki qo'shilsa ham, natija BUZILMAYDI — bu katta, tez o'zgaruvchan ma'lumotlar uchun standart yechim.

NEXT UP

Filtering & Sorting Query Parameters

OUTPUT

$ go run main.go
Kodingizni ishga tushiring

Pagination: Cursor-Based

Sahifalash: Cursor asosida

Kitobni "45-sahifa"dan o'qishni davom ettirasiz deylik. Agar kimdir kitobning BOSHIDAGI 3 betni OLIB TASHLASA, sizning "45-sahifa"ngiz ENDI boshqa matnga to'g'ri keladi — siz nimanidir O'TKAZIB YUBORASIZ yoki QAYTA o'qiysiz. Lekin agar siz sahifa RAQAMI o'rniga, kitobga JISMONIY ZAKLADKA (bookmark) qo'ysangiz — "...gacha o'qidim" — bu muammo BO'LMAYDI: zakladka HAR DOIM to'g'ri joyni ko'rsatadi, hatto oldingi betlar o'zgarsa ham. Cursor-based pagination — aynan shu "zakladka" g'oyasi: sahifa RAQAMI (offset) o'rniga, OXIRGI ko'rilgan yozuvning O'ZI ishlatiladi.

example.go
package main

import (
	"fmt"
	"strconv"
)

type Book struct {
	ID string
}

type Page struct {
	Books      []*Book
	NextCursor string
}

// listAfter — books ID bo'yicha o'sib boruvchi tartibda saralangan deb faraz qilinadi
func listAfter(books []*Book, cursor string, limit int) Page {
	start := 0
	if cursor != "" {
		cursorNum, _ := strconv.Atoi(cursor)
		for i, b := range books {
			idNum, _ := strconv.Atoi(b.ID)
			if idNum > cursorNum {
				start = i
				break
			}
			start = i + 1
		}
	}
	end := start + limit
	if end > len(books) {
		end = len(books)
	}
	page := books[start:end]
	next := ""
	if end < len(books) {
		next = page[len(page)-1].ID
	}
	return Page{Books: page, NextCursor: next}
}

func printPage(p Page) {
	var ids []string
	for _, b := range p.Books {
		ids = append(ids, b.ID)
	}
	fmt.Println(ids, "next:", p.NextCursor)
}

func main() {
	books := []*Book{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}

	page1 := listAfter(books, "", 2)
	printPage(page1)

	page2 := listAfter(books, page1.NextCursor, 2)
	printPage(page2)

	page3 := listAfter(books, page2.NextCursor, 2)
	printPage(page3)
}

cursor — oxirgi olingan yozuvning ID'si (bo'sh bo'lsa "boshidan" degani). listAfter HAR SAFAR "cursor'dan KATTA ID'lar"ni qidiradi — offset kabi "nechinchi ELEMENT"ni emas, BALKI "qaysi ELEMENTDAN keyin"ni so'raydi. NextCursor — agar YANA yozuv qolgan bo'lsa, KEYINGI so'rovda ishlatiladigan cursor'ni qaytaradi; bo'sh bo'lsa — bu OXIRGI sahifa degani.

Bu naqshning ustunligi shunda: agar sahifalar ORASIDA (masalan foydalanuvchi 1-sahifani o'qib bo'lguncha) BIROR yozuv O'CHIRILSA yoki qo'shilsa, offset asosidagi sahifalash CHALKASHIB ketadi (ba'zi yozuvlar IKKI MARTA ko'rinadi yoki UMUMAN ko'rinmaydi), cursor asosidagi sahifalash esa BUNDAY MUAMMODAN XOLI — chunki u "nechinchi o'rinda" emas, "QAYSI yozuvdan keyin" deb so'raydi.

>_ Exercise

Offset asosidagi sahifalashning O'CHIRISHDAGI MUAMMOSINI, cursor bilan SOLISHTIRIB ko'ring.

  • offsetPage(books []*Book, limit, offset int) []*Book funksiyasini yozing ('Pagination: Limit & Offset' darsidagi paginate bilan bir xil mantiq)
  • original ro'yxat ({"1"}..{"5"}) uchun offsetPage(original, 2, 0) ni chaqirib, natijani chop eting
  • "2" ID'li kitob O'CHIRILGAN deb faraz qiling (afterDelete — {"1"},{"3"},{"4"},{"5"}), va offsetPage(afterDelete, 2, 2) natijasini chop eting — "3" O'TKAZIB YUBORILGANINI ko'rasiz
  • listAfter bilan xuddi shu ikki holatni (original va afterDelete) taqqoslang — cursor '3'ni O'TKAZIB YUBORMASLIGINI ko'rsating

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Cursor-based sahifalash — "nechinchi o'rin" (offset) emas, "QAYSI yozuvdan keyin" (cursor) so'raydi; shu sabab, sahifalar orasida ma'lumot o'chirilsa yoki qo'shilsa ham, natija BUZILMAYDI — bu katta, tez o'zgaruvchan ma'lumotlar uchun standart yechim.

NEXT UP

Filtering & Sorting Query Parameters