Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Для чого використовується пошук?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Пошук** використовується для швидкого і точного знаходження потрібних даних серед великих обсягів інформації. Він поліпшує користувацький досвід, підвищує конверсію, пришвидшує роботу з продуктом, допомагає аналізувати дані та ухвалювати рішення, а також знижує навантаження на підтримку. **Ключове:** технічно пошук спирається на індексацію (токенізація, нормалізація, побудова інвертованого індексу) і ранжування результатів за релевантністю (наприклад, TF-IDF чи BM25).Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Коротка відповідь Пошук використовується для швидкого і точного знаходження потрібних даних серед великих обсягів інформації. Він поліпшує користувацький досвід, підвищує конверсію, пришвидшує роботу з продуктом, допомагає аналізувати дані та ухвалювати рішення, а також знижує навантаження на підтримку. ## Детальна відповідь ### Основні цілі пошуку - Швидкий доступ до даних: знаходити документи, товари, користувачів, логи, метрики. - Навігація та discovery: допомогти користувачу зрозуміти асортимент і структуру контенту. - Аналітика та моніторинг: оперативно знаходити аномалії та інциденти в потоках подій і логах. - Автоматизація: знаходити сутності у фонових задачах, тригерах, інтеграціях. - Персоналізація та рекомендації: враховувати інтереси користувача при видачі й ранжуванні результатів. - Зниження навантаження на підтримку: користувачі самі знаходять відповіді та контент. ### Види пошуку - Точний збіг: за ключем/ID/полем. - Підрядковий: LIKE/ILIKE, префіксний пошук, автодоповнення. - Повнотекстовий: за словами, з урахуванням морфології та релевантності. - Фразовий: "пошук за точною фразою" з порядком слів. - Фільтраційний/фасетний: поля-грані (категорія, ціна), агрегації. - Нечіткий (fuzzy): з урахуванням описок (відстань Левенштейна, BK-дерева). - Синоніми та нормалізація: врахування форм слова та альтернативних термінів. - Геопошук: за координатами і радіусом, сортування за відстанню. - Векторний/семантичний: пошук за змістом з ембеддингами. ### Як це працює під капотом - Індексація: токенізація, нормалізація, стоп-слова, стемінг/лематизація, n-грами; побудова інвертованого індексу. - Структури даних: хеш-таблиці, B-/B+ дерева, Trie, інвертований індекс, BK-дерево. - Релевантність і ранжування: TF-IDF, BM25, буст важливих полів, свіжість, популярність, персоналізація. - Обробка запитів: парсинг операторів (AND/OR/NOT), фільтри, сортування, пагінація, підсвічування збігів, фасети. - Архітектура: окремий рушій (спеціалізовані системи) або вбудований FTS у БД; асинхронна індексація з черг; eventual consistency; шардування, реплікація; кешування. ### UX-практики - Автодоповнення і підказки (suggestions), популярні запити. - Дебаунс уведення і скасування застарілих запитів, стан завантаження і порожні стани. - Підсвічування збігів, синоніми, виправлення описок, "можливо, ви мали на увазі". - Фільтри, фасети, збережені запити, історія пошуку, гарячі клавіші. ### Метрики якості - Precision / Recall, F1 - точність і повнота. - MRR, NDCG - якість ранжування. - CTR, конверсія після кліку, час до першого результату, швидкість відповіді. ### Продуктивність і масштабування - Індекси в БД: GIN/GIST для повнотексту, btree для точних збігів, покривні індекси. - Кешування: відповідей, підказок, фасетів; прогрів та інвалідація. - Пагінація: keyset замість OFFSET/LIMIT на великих даних, таймаути запитів. - Асинхронна індексація, контроль відставання індексу, планові реіндексації та бекапи. ### Безпека та відповідність - Фільтрація за правами доступу (row-level security), багатоорендність (tenant_id). - Маскування і мінімізація PII, аудит запитів, rate limiting. ### Інтернаціоналізація - Unicode-сумісність, коректна колація і регістронезалежність. - Морфологія для різних мов, транслітерація (наприклад, укр/лат). ### Приклади коду ### SQL: повнотекстовий пошук у Postgres ``` -- Створення індексу повнотекстового пошуку за колонками title і body CREATE INDEX idx_posts_fts ON posts USING GIN ( to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')) ); -- Пошук з урахуванням морфології (plainto_tsquery зручний для користувацького вводу) SELECT id, title, ts_headline('english', body, plainto_tsquery('english', $1)) AS snippet, ts_rank_cd( to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')), plainto_tsquery('english', $1) ) AS rank FROM posts WHERE to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')) @@ plainto_tsquery('english', $1) ORDER BY rank DESC LIMIT 20 OFFSET $2; -- Приклад simple LIKE (підрядковий пошук; менш ефективний) SELECT id, title FROM posts WHERE title ILIKE '%' || $1 || '%' LIMIT 20; ``` ### Backend: Node.js Express endpoint з пагінацією і фільтрами ``` import express from 'express'; import { Pool } from 'pg'; const app = express(); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); app.get('/search', async (req, res) => { const q = String(req.query.q || '').trim(); const limit = Math.min(parseInt(String(req.query.limit || '20'), 10), 100); const cursor = req.query.cursor ? Number(req.query.cursor) : null; // keyset пагінація const category = req.query.category ? String(req.query.category) : null; const params = [] as any[]; let where = [] as string[]; if (q) { params.push(q); where.push("to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')) @@ plainto_tsquery('english', $" + params.length + ")"); } if (category) { params.push(category); where.push('category = $' + params.length); } if (cursor) { params.push(cursor); where.push('id > $' + params.length); // keyset: сортування за id ASC } const sql = ` SELECT id, title, ts_rank_cd( to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,'')), COALESCE(plainto_tsquery('english', $1), to_tsquery('english', '')) ) AS rank FROM posts ${where.length ? 'WHERE ' + where.join(' AND ') : ''} ORDER BY id ASC LIMIT ${limit + 1} `; try { const { rows } = await pool.query(sql, params); const hasMore = rows.length > limit; const items = hasMore ? rows.slice(0, limit) : rows; res.json({ items, nextCursor: hasMore ? items[items.length - 1].id : null }); } catch (e) { res.status(500).json({ error: 'search_failed' }); } }); app.listen(3000); ``` ### Frontend: React, дебаунс і скасування застарілих запитів ``` import { useEffect, useMemo, useState } from 'react'; function useDebouncedValue(value, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { const id = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(id); }, [value, delay]); return debounced; } export default function SearchBox() { const [q, setQ] = useState(''); const debouncedQ = useDebouncedValue(q, 300); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const controller = useMemo(() => new AbortController(), [debouncedQ]); useEffect(() => { if (!debouncedQ) { setItems([]); return; } setLoading(true); setError(null); fetch(`/search?q=${encodeURIComponent(debouncedQ)}`, { signal: controller.signal }) .then(r => { if (!r.ok) throw new Error('Network error'); return r.json(); }) .then(data => setItems(data.items || [])) .catch(err => { if (err.name !== 'AbortError') setError(err.message); }) .finally(() => setLoading(false)); return () => controller.abort(); }, [debouncedQ]); return ( <div> <input placeholder="Пошук..." value={q} onChange={e => setQ(e.target.value)} /> {loading && <div>Завантаження...</div>} {error && <div>Помилка: {error}</div>} {!loading && !error && items.length === 0 && debouncedQ && <div>Нічого не знайдено</div>} <ul> {items.map(i => <li key={i.id}>{i.title}</li>)} </ul> </div> ); } ``` ### Алгоритмічний аспект: бінарний пошук ``` function binarySearch(arr, x) { let l = 0, r = arr.length - 1; while (l <= r) { const m = l + ((r - l) >> 1); if (arr[m] === x) return m; if (arr[m] < x) l = m + 1; else r = m - 1; } return -1; } // Використання: масив має бути відсортований console.log(binarySearch([1,3,5,7,9], 7)); // 3 ``` ### Чек-лист впровадження пошуку 1. Зібрати вимоги: типи контенту, поля, мова, навантаження, SLA. 2. Обрати технологію: БД з FTS або спеціалізований рушій; оцінити вартість і підтримку. 3. Схема даних та індекси: які поля індексуються, морфологія, синоніми, токенізація. 4. Пайплайн індексації: джерела, черги, дедуплікація, оновлення, контроль відставання. 5. API: фільтри, сортування, пагінація, підсвічування, безпека на рівні запитів. 6. UI/UX: підказки, автодоповнення, фасети, порожні стани, доступність (a11y). 7. Спостережуваність: логування запитів, метрики, трейсинг, дашборди, алерти. 8. Якість і релевантність: офлайн/онлайн оцінки, A/B-тести, збір кліків для навчання. 9. Експлуатація: бекапи, реіндексація без даунтайму, шардування/реплікація, план оновлень.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.