Atlas Search — full-text search, fuzzy, autocomplete

~25 daq · MongoDB · Atlas va Deployment

Muammodan boshlaylik

Do'kon qidiruvi $text (30-dars) bilan ishlaydi. Foydalanuvchilar shikoyat qiladi:

  • "samsnug" yozdim — hech narsa chiqmadi (xato yozilgan so'z)
  • "sams" yozayotganimda takliflar chiqmaydi (autocomplete yo'q)
  • "telefonlar" deb qidirsam, "telefon" nomli mahsulotlar topilmaydi (o'zbek tilidagi qo'shimchalar)
  • Natijalar orasida eng mosi qaysi — tartib noaniq

Alohida Elasticsearch o'rnatish — yana bir tizim, sinxronlash, monitoring.


Atlas Search nima?

Atlas Search — Atlas cluster ichiga o'rnatilgan, Apache Lucene asosidagi to'liq matnli qidiruv. Elasticsearch ni ham shu Lucene harakatlantiradi.

  • Ma'lumotlar avtomatik sinxronlanadi — alohida tizim yo'q
  • Aggregation pipeline dagi $search bosqichi orqali ishlatiladi
  • Fuzzy, autocomplete, sinonimlar, ajratib ko'rsatish (highlight), facet lar
$text Atlas Search
Xato yozilgan so'z ❌ ✅ fuzzy
Autocomplete ❌ ✅
Reyting sozlash Cheklangan To'liq (boost, function score)
Analizatorlar Bir nechta til Ko'p tillar, o'z analizatorlaringiz
Qayerda Har qanday MongoDB Atlas (va yangi versiyalarda lokal Community/Enterprise ham)

Hayotiy o'xshatma

Tajribali kutubxonachi. Siz "Qodiriyning o'tkir kunlari" desangiz — u xatoni tushunib, "O'tkan kunlar"ni beradi (fuzzy). "O'tk..." deyishingiz bilan — "O'tkan kunlarmi?" deb so'raydi (autocomplete). Va sizga eng mos kitobni birinchi qo'yadi.


Search index yaratish

Atlas → Database → cluster → Atlas Search → Create Search Index → JSON Editor:

{
  "mappings": {
    "dynamic": false,
    "fields": {
      "nomi": [
        { "type": "string", "analyzer": "lucene.standard" },
        { "type": "autocomplete", "tokenization": "edgeGram", "minGrams": 2, "maxGrams": 15 }
      ],
      "tavsif": { "type": "string" },
      "brend": { "type": "string" },
      "kategoriya": { "type": "token" },
      "narx": { "type": "number" }
    }
  }
}

Index nomi: mahsulotlar_search, collection: mahsulotlar.

  • dynamic: false — faqat ko'rsatilgan maydonlar indekslanadi (kichik va tez index)
  • string — so'zlarga ajratilgan matn qidiruvi
  • autocomplete — so'z boshlari bo'yicha
  • token — aniq qiymat bo'yicha filtr va facet uchun

Yoki koddan:

await Mahsulot.collection.createSearchIndex({ name: 'mahsulotlar_search', definition: { mappings: { ... } } })

Oddiy qidiruv

const natija = await Mahsulot.aggregate([
  { $search: {
      index: 'mahsulotlar_search',
      text: {
        query: req.query.q,
        path: ['nomi', 'tavsif', 'brend'],
      },
  } },
  { $limit: 20 },
  { $project: { nomi: 1, narx: 1, rasm: 1, ball: { $meta: 'searchScore' } } },
])

$search — pipeline ning birinchi bosqichi bo'lishi shart. Natijalar avtomatik moslik bo'yicha tartiblanadi.


Fuzzy — xatolarga chidamli

{ $search: {
    index: 'mahsulotlar_search',
    text: {
      query: 'samsnug galaxi',
      path: ['nomi', 'brend'],
      fuzzy: { maxEdits: 1, prefixLength: 2 },
    },
} }
  • maxEdits — nechta harf farqi kechiriladi (1 yoki 2)
  • prefixLength — so'zning birinchi nechta harfi aniq bo'lishi kerak (tezlik va aniqlik uchun)

"samsnug" → "Samsung" ✅


Autocomplete

router.get('/takliflar', async (req, res) => {
  const q = String(req.query.q ?? '').trim()
  if (q.length < 2) return res.json([])

  const takliflar = await Mahsulot.aggregate([
    { $search: {
        index: 'mahsulotlar_search',
        autocomplete: {
          query: q,
          path: 'nomi',
          fuzzy: { maxEdits: 1, prefixLength: 1 },
        },
    } },
    { $limit: 8 },
    { $project: { _id: 1, nomi: 1 } },
  ])

  res.json(takliflar)
})

Frontend da debounce (JS kursi, 83-dars) bilan har 300 ms da so'rov.


Murakkab so'rov — compound

{ $search: {
    index: 'mahsulotlar_search',
    compound: {
      must: [
        { text: { query: req.query.q, path: ['nomi', 'tavsif'], fuzzy: { maxEdits: 1 } } },
      ],
      should: [
        { text: { query: req.query.q, path: 'nomi', score: { boost: { value: 3 } } } },
      ],
      filter: [
        { equals: { path: 'kategoriya', value: 'telefon' } },
        { range: { path: 'narx', gte: 1000000, lte: 5000000 } },
      ],
    },
} }
Bo'lim Ma'nosi
must Albatta mos bo'lsin, ballga ta'sir qiladi
should Mos bo'lsa — ball oshadi (nomida bo'lsa — 3 barobar)
filter Albatta mos bo'lsin, ballga ta'sir qilmaydi
mustNot Mos bo'lmasin

Highlight — mos qismni ajratish

[
  { $search: {
      index: 'mahsulotlar_search',
      text: { query: 'simsiz quloqchin', path: 'tavsif' },
      highlight: { path: 'tavsif' },
  } },
  { $project: { nomi: 1, ajratilgan: { $meta: 'searchHighlights' } } },
]

Natijada tavsifning qaysi qismlari mos kelgani — frontend da <mark> bilan ko'rsatish uchun.

[!INFO] O'zbek tili uchun Atlas Search da maxsus analizator yo'q. lucene.standard so'zlarni to'g'ri ajratadi, lekin "telefonlar" va "telefon" ni bitta so'z deb bilmaydi. Yechimlar: autocomplete (edgeGram) so'z boshlari bo'yicha topadi; sinonimlar collection i bilan muhim shakllarni bog'lash; yoki custom analizator.


Tez-tez uchraydigan xatolar

$search ni birinchi bosqich qilmaslik

$match dan keyin $search — xato. Filtrlarni compound.filter ga qo'ying.

Index nomini ko'rsatmaslik

index ko'rsatilmasa — default nomli index qidiriladi. Topilmasa — natija jim bo'sh.

Har bir harf bosilganda so'rov

Autocomplete — debounce va minimal uzunlik (2 harf) bilan.


Xulosa

  • Atlas Search — Lucene asosidagi qidiruv, cluster ichida, avtomatik sinxron
  • Search index: string, autocomplete, token, number; dynamic: false
  • $search — pipeline ning birinchi bosqichi; searchScore — moslik bahosi
  • fuzzy — xato yozilgan so'zlar; autocomplete — takliflar
  • compound: must, should (boost), filter, mustNot
  • highlight — mos qismlarni ajratish; o'zbek tili — autocomplete va sinonimlar

Keyingi dars: 54-dars — Atlas Triggers va Functions — serverless logika