9-bo‘lim

Kursor, saralash va sahifalash

Kursor qanday ishlaydi, sort va limit, skip nima uchun sekin, kursor bo'yicha sahifalash hamda countDocuments va estimatedDocumentCount farqi.

🕑 11 daqiqa o‘qish 📄 733 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Kursor nima
  2. sort
  3. limit va skip
  4. Kursor bo'yicha sahifalash
  5. Sanash
  6. Kursorni aylanib chiqish
  7. Kursor metodlari
  8. Xulosa

find() hujjatlarni qaytarmaydi - u kursor qaytaradi. Bu farqni tushunish katta to'plamlar bilan ishlashda hal qiluvchi ahamiyatga ega.

JavaScript
db.kitob.insertMany([
  { _id: 1, nom: "Otkan kunlar", yil: 1926, narx: 85000, janr: "tarixiy" },
  { _id: 2, nom: "Mehrobdan chayon", yil: 1929, narx: 72000,
    janr: "tarixiy" },
  { _id: 3, nom: "Sarob", yil: 1935, narx: 59000, janr: "ijtimoiy" },
  { _id: 4, nom: "Kecha va kunduz", yil: 1936, narx: 68000,
    janr: "ijtimoiy" },
  { _id: 5, nom: "Navoiy", yil: 1944, narx: 95000, janr: "tarixiy" },
  { _id: 6, nom: "Shum bola", yil: 1936, narx: 47000, janr: "bolalar" },
  { _id: 7, nom: "Ufq", yil: 1964, narx: 64000, janr: "zamonaviy" },
  { _id: 8, nom: "Yulduzli tunlar", yil: 1978, narx: 78000, janr: "tarixiy" }
])

Kursor nima #

Kursor - serverdagi ochiq natija oqimi. U hamma hujjatni bir vaqtda yubormaydi, balki to'plam-to'plam (odatda 101 ta, keyin ~4 MB lik bloklarda) uzatadi.

Kursor natijani bo'lib-bo'lib uzatadi Server 1 000 000 hujjat natijaga mos keladi kursor ochiq turadi 1-blok 2-blok so'ralganda Mijoz Xotirada faqat bir blok turadi ~101 hujjat toArray() chaqirilsa Hamma blok ketma-ket so'raladi va xotiraga to'planadi - 1 000 000 ta Shuning uchun limit() kursorga, toArray() esa xotiraga ta'sir qiladi find().limit(20).toArray() - xavfsiz: faqat 20 ta hujjat keladi find().toArray() - xavfli: hammasi keladi Kursor 10 daqiqadan keyin serverda o'zi yopiladi
Kursor ochiq turgan paytda server uning holatini xotirada saqlaydi

sort #

JavaScript
db.kitob.find({}, { nom: 1, narx: 1, _id: 0 }).sort({ narx: -1 }).limit(3)
Natija
[
  { nom: 'Navoiy', narx: 95000 },
  { nom: 'Otkan kunlar', narx: 85000 },
  { nom: 'Yulduzli tunlar', narx: 78000 }
]
QiymatTartib
1O'sish bo'yicha
-1Kamayish bo'yicha

Bir nechta maydon bo'yicha saralash - tartib muhim:

JavaScript
const p = { _id: 0, nom: 1, janr: 1, yil: 1 }
db.kitob.find({}, p).sort({ janr: 1, yil: -1 })
Natija
[
  { nom: 'Shum bola', yil: 1936, janr: 'bolalar' },
  { nom: 'Kecha va kunduz', yil: 1936, janr: 'ijtimoiy' },
  { nom: 'Sarob', yil: 1935, janr: 'ijtimoiy' },
  { nom: 'Yulduzli tunlar', yil: 1978, janr: 'tarixiy' },
  { nom: 'Navoiy', yil: 1944, janr: 'tarixiy' },
  { nom: 'Mehrobdan chayon', yil: 1929, janr: 'tarixiy' },
  { nom: 'Otkan kunlar', yil: 1926, janr: 'tarixiy' },
  { nom: 'Ufq', yil: 1964, janr: 'zamonaviy' }
]

Avval janr bo'yicha guruhlandi, har guruh ichida esa yil kamayish tartibida.

Barqaror bo'lmagan saralash

Ikkita hujjatning saralash maydoni teng bo'lsa, ular orasidagi tartib kafolatlanmagan. Xuddi shu so'rovni ikki marta bajarib, ikki xil natija olish mumkin.

Bu, ayniqsa, sahifalashda zarar keltiradi: birinchi sahifada ko'rgan hujjat ikkinchi sahifada yana chiqishi mumkin.

Yechim oddiy: saralashga yagona maydonni qo'shing:

Natija
.sort({ yil: -1, _id: 1 })

_id har doim yagona, shuning uchun tartib endi to'liq aniqlangan bo'ladi. Bu odat tarzida qilinishi kerak - foydali, lekin hech qanday qo'shimcha xarajat keltirmaydi.

limit va skip #

JavaScript
db.kitob.find({}, { nom: 1, _id: 0 }).sort({ yil: 1 }).limit(3)
db.kitob.find({}, { nom: 1, _id: 0 }).sort({ yil: 1 }).skip(3).limit(3)
Natija
[
  { nom: 'Otkan kunlar' },
  { nom: 'Mehrobdan chayon' },
  { nom: 'Sarob' }
]
[ { nom: 'Kecha va kunduz' }, { nom: 'Shum bola' }, { nom: 'Navoiy' } ]

Bu - klassik sahifalash: skip((sahifa - 1) * hajm).

skip katta sonlarda juda sekin

skip(100000) "100 000-hujjatga sakra" degani emas. Baza birinchi 100 000 hujjatni haqiqatan o'qiydi va ularni tashlab yuboradi.

SahifaO'qiladigan hujjatlar
120
10200
1002 000
5000100 000

Ya'ni sahifa raqami oshgan sari so'rov sekinlashadi. Bu "chuqur sahifalash muammosi" deb ataladi va u faqat MongoDB ga xos emas - SQL dagi OFFSET ham xuddi shunday ishlaydi.

Yechim - kursor bo'yicha sahifalash: oxirgi ko'rilgan qiymatdan keyingisini so'rash. Uni quyida ko'ramiz.

Kursor bo'yicha sahifalash #

skip o'rniga "oxirgi ko'rgan qiymatdan keyin" shartini yozamiz:

JavaScript
const tartib = { yil: 1, _id: 1 }
const p = { _id: 0, nom: 1, yil: 1 }
const birinchi = db.kitob.find({}, p).sort(tartib).limit(3).toArray()
birinchi.map(k => k.nom)
const oxirgi = db.kitob.findOne({ nom: birinchi[2].nom })
const keyin = { $or: [
  { yil: { $gt: oxirgi.yil } },
  { yil: oxirgi.yil, _id: { $gt: oxirgi._id } }
] }
db.kitob.find(keyin, p).sort(tartib).limit(3)
Natija
[ 'Otkan kunlar', 'Mehrobdan chayon', 'Sarob' ]


[
  { nom: 'Kecha va kunduz', yil: 1936 },
  { nom: 'Shum bola', yil: 1936 },
  { nom: 'Navoiy', yil: 1944 }
]

Bu so'rov necha-nchi sahifada bo'lishidan qat'i nazar bir xil tezlikda ishlaydi - chunki indeks kerakli joyga darhol sakraydi.

skipKursor bo'yicha
TezlikSahifa oshgani sari sekinlashadiDoim bir xil
"5-sahifaga o't"MumkinMumkin emas
Yangi yozuv qo'shilsaHujjat takrorlanishi mumkinTakrorlanmaydi
Qayerda ishlatiladiAdmin panel, kichik ro'yxatCheksiz ro'yxat, API

Sanash #

JavaScript
db.kitob.countDocuments()
db.kitob.countDocuments({ janr: "tarixiy" })
db.kitob.estimatedDocumentCount()
Natija
8
4
8
countDocuments va estimatedDocumentCount

Ikkalasi ham son qaytaradi, lekin butunlay boshqacha ishlaydi:

countDocumentsestimatedDocumentCount
Filtr qabul qiladimiHaYo'q
Qanday hisoblaydiHujjatlarni sanaydiMetama'lumotdan o'qiydi
AniqmiHaTaxminiy
TezligiTo'plam hajmiga bog'liqDoimiy
Tranzaksiyada ishlaydimiHaYo'q

Qachon qaysi biri:

  • Sahifalash uchun "jami nechta" - aniq son kerak, countDocuments.
  • Bosh sahifada "bizda 2 000 000+ mahsulot" - taxminiy yetarli, estimatedDocumentCount.

Uchinchi variant ham bor: agar son tez-tez kerak bo'lsa, uni alohida hujjatda saqlab, $inc bilan yangilab boring. Million hujjatli to'plamda countDocuments har safar sekundlarga cho'zilishi mumkin.

Kursorni aylanib chiqish #

JavaScript
let jami = 0
db.kitob.find({ janr: "tarixiy" }).forEach(k => { jami += k.narx })
jami
Natija
330000

forEach hujjatlarni birma-bir oladi - xotirada bir vaqtda faqat bitta blok turadi. Katta to'plamda toArray() o'rniga aynan shu ishlatiladi.

Kursor metodlari #

MetodNima qiladi
.sort({ ... })Saralaydi
.limit(n)Eng ko'pi bilan n ta
.skip(n)Birinchi n tasini tashlaydi
.toArray()Hammasini massivga yig'adi
.forEach(f)Birma-bir aylanadi
.hasNext() / .next()Qo'lda boshqarish
.count()Eskirgan - countDocuments ishlating
JavaScript
const kursor = db.kitob.find({ janr: "tarixiy" }).sort({ yil: 1 })
kursor.hasNext()
kursor.next().nom
kursor.next().nom
Natija
true
Otkan kunlar
Mehrobdan chayon
Amaliy topshiriq
  1. sort bilan narx bo'yicha kamayish tartibida saralang.
  2. Ikkita maydon bo'yicha saralang va tartib ta'sirini ko'ring.
  3. Saralashga nega _id qo'shish kerakligini yozing.
  4. limit va skip bilan ikkinchi sahifani oling.
  5. skip(100000) nega sekin ekanini tushuntiring.
  6. Kursor bo'yicha sahifalashni yozing.
  7. Ikki usulning farqini jadval qilib solishtiring.
  8. countDocuments va estimatedDocumentCount ni chaqiring.
  9. Qaysi biri qachon to'g'ri kelishini yozing.
  10. forEach bilan yig'indi hisoblang.

Xulosa #

  • find() hujjatlarni emas, kursor qaytaradi.
  • Kursor natijani bloklar bilan uzatadi - xotirada hammasi turmaydi.
  • sort da 1 o'sish, -1 kamayish; bir nechta maydonda tartib muhim.
  • Teng qiymatlarda tartib kafolatlanmagan - saralashga _id qo'shing.
  • limit kursorni cheklaydi, toArray() esa xotiraga yig'adi.
  • skip katta sonlarda sekin: u tashlanadigan hujjatlarni ham o'qiydi.
  • Kursor bo'yicha sahifalash sahifa raqamidan qat'i nazar bir xil tez.
  • Uning narxi: "5-sahifaga sakrash" imkoni yo'q.
  • countDocuments aniq va filtr oladi; estimatedDocumentCount tez, lekin taxminiy.
  • Katta to'plamni aylanish uchun toArray() emas, forEach ishlating.

Keyingi bo'limda sxema dizaynini - ma'lumotni ichiga joylash yoki havola qilish masalasini ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.