غالبًا ما تؤدي إضافة البحث إلى التطبيق الخاص بك إلى تمكين المطورين من الوصول إلى Elasticsearch – ولكن PostgreSQL يحتوي على بحث قوي ومدمج في النص الكامل يتعامل مع معظم الاحتياجات دون الحاجة إلى بنية تحتية إضافية. يوضح هذا الدليل كيفية إنشاء بحث إنتاجي باستخدام PostgreSQL، بدءًا من الأساسيات ووصولاً إلى التصنيف والتمييز.
📋 Table of Contents
- لماذا نستخدم PostgreSQL للبحث؟
- فهم tsvector و tsquery
- البحث الأساسي عن النص الكامل
- إضافة عمود tsvector محسوب مسبقًا مع الفهرس
- ترتيب النتائج حسب الصلة
- تمييز المصطلحات المتطابقة
- استخدامه من Node.js
- إضافة بحث غامض/متسامح مع الأخطاء المطبعية
- متى يتم استخدام Elasticsearch بدلاً من ذلك
- الأسئلة المتداولة
- الخلاصة
لماذا نستخدم PostgreSQL للبحث؟
- لا توجد بنية تحتية إضافية: استخدم قاعدة البيانات الموجودة لديك – لا يوجد محرك بحث منفصل للتشغيل والمزامنة
- جيد بما يكفي لمعظم التطبيقات: يتعامل مع ملايين المستندات بفهرسة مناسبة
- اتساق المعاملات: ابحث في تحديثات الفهرس باستخدام بياناتك، دون تأخير في المزامنة
- الميزات الغنية: الترتيب، والانطلاق، والتمييز، ولغات متعددة مدمجة في
فهم tsvector و tsquery
-- tsvector: a processed, searchable representation of text
-- tsquery: a search query
SELECT to_tsvector('english', 'The quick brown foxes are jumping');
-- 'brown':3 'fox':4 'jump':6 'quick':2
-- Note: stemming (foxes->fox, jumping->jump) and stopword removal (the, are)
SELECT to_tsvector('english', 'The quick brown foxes')
@@ to_tsquery('english', 'fox');
-- true - matches because 'foxes' stems to 'fox'
البحث الأساسي عن النص الكامل
-- Search articles by title and content
SELECT id, title
FROM articles
WHERE to_tsvector('english', title || ' ' || content)
@@ to_tsquery('english', 'postgresql & search');
-- & means AND, | means OR, ! means NOT
-- plainto_tsquery handles user input safely (treats as AND)
SELECT id, title
FROM articles
WHERE to_tsvector('english', title || ' ' || content)
@@ plainto_tsquery('english', 'postgresql search tutorial');
-- websearch_to_tsquery supports Google-like syntax (quotes, OR, -)
SELECT id, title
FROM articles
WHERE to_tsvector('english', content)
@@ websearch_to_tsquery('english', '"full text" search -elasticsearch');
إضافة عمود tsvector محسوب مسبقًا مع الفهرس
حساب tsvector على كل استعلام بطيء. قم بتخزينه في عمود يحتوي على فهرس GIN:
-- Add a generated tsvector column (auto-updates with the data)
ALTER TABLE articles ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
to_tsvector('english', coalesce(title, '') || ' ' || coalesce(content, ''))
) STORED;
-- Create a GIN index for fast searching
CREATE INDEX articles_search_idx ON articles USING GIN (search_vector);
-- Now searches are fast and use the index
SELECT id, title FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'postgresql search');
ترتيب النتائج حسب الصلة
-- ts_rank scores how well each row matches
SELECT id, title,
ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', 'postgresql search') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;
-- Weight title matches higher than content
ALTER TABLE articles ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(content, '')), 'B')
) STORED;
-- 'A' weight (title) ranks higher than 'B' weight (content)
تمييز المصطلحات المتطابقة
-- ts_headline returns snippets with matched terms highlighted
SELECT id, title,
ts_headline('english', content, query,
'StartSel=, StopSel=, MaxWords=35, MinWords=15') AS snippet
FROM articles, plainto_tsquery('english', 'postgresql') query
WHERE search_vector @@ query
ORDER BY ts_rank(search_vector, query) DESC;
-- snippet contains the relevant excerpt with around matches
استخدامه من Node.js
app.get('/search', async (req, res) => {
const q = req.query.q;
if (!q) return res.json({ results: [] });
const result = await db.query(`
SELECT id, title,
ts_headline('english', content, query,
'StartSel=,StopSel=,MaxWords=35') AS snippet,
ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', $1) query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20
`, [q]);
res.json({ results: result.rows });
});
إضافة بحث غامض/متسامح مع الأخطاء المطبعية
-- Enable the pg_trgm extension for fuzzy matching (handles typos)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Trigram index for similarity search
CREATE INDEX articles_title_trgm ON articles USING GIN (title gin_trgm_ops);
-- Find titles similar to a (possibly misspelled) query
SELECT title, similarity(title, 'postgres serch') AS sim
FROM articles
WHERE title % 'postgres serch' -- % is the similarity operator
ORDER BY sim DESC;
-- Matches 'PostgreSQL search' despite the typo
متى يتم استخدام Elasticsearch بدلاً من ذلك
يتعامل البحث عن النص الكامل لـ PostgreSQL مع معظم التطبيقات بشكل جيد. فكر في Elasticsearch عندما تحتاج إلى:
- نطاق واسع جدًا (مئات الملايين من المستندات) مع ضبط معقد للملاءمة
- الميزات المتقدمة: البحث متعدد الأوجه، والمجموعات المعقدة، والبحث الجغرافي على نطاق واسع
- البحث عبر العديد من مصادر البيانات خارج قاعدة البيانات الخاصة بك
- تحليلات في الوقت الحقيقي لبيانات البحث
بالنسبة لمعظم التطبيقات – المدونات، والتجارة الإلكترونية، ومواقع المحتوى، وSaaS – يعد البحث في PostgreSQL أبسط، ولا يتطلب بنية تحتية إضافية، وهو أكثر من كافي.
الأسئلة المتداولة
س: هل بحث PostgreSQL جيد بما فيه الكفاية، أم أنني بحاجة إلى Elasticsearch؟
ج: بالنسبة لمعظم التطبيقات، يعد البحث عن النص الكامل لـ PostgreSQL أكثر من كافي – فهو يتعامل مع ملايين المستندات من خلال التصنيف والتمييز والاشتقاق. استخدم Elasticsearch فقط للبحث واسع النطاق أو المتقدم متعدد الأوجه أو البحث عبر العديد من مصادر البيانات. لا تقم بإضافة البنية التحتية لـ Elasticsearch قبل الأوان.
س: كيف أتعامل مع الأخطاء المطبعية في البحث؟
ج: استخدم الامتداد pg_trgm لمطابقة تشابه المثلثات، والتي تتسامح مع الأخطاء المطبعية والأخطاء الإملائية. ادمجها مع البحث عن النص الكامل – استخدم tsvector للبحث الرئيسي وتشابه المثلثات كبديل أو للإكمال التلقائي/الاقتراحات.
س: لماذا يكون البحث عن النص الكامل بطيئًا؟
ج: من المحتمل أنك تقوم بحساب to_tsvector في كل استعلام بدون فهرس. قم بإضافة عمود tsvector مُخزن تم إنشاؤه باستخدام فهرس GIN. يؤدي هذا إلى حساب التمثيل القابل للبحث مسبقًا ويجعل عمليات البحث سريعة حتى على الجداول الكبيرة.
س: plainto_tsquery vs to_tsquery vs websearch_to_tsquery؟
A: to_tsquery يتطلب بناء جملة عامل التشغيل (&، |) – مناسب للاستعلامات البرمجية. plainto_tsquery يتعامل مع إدخال المستخدم العادي (يعامل الكلمات على أنها AND) – آمن لمربعات بحث المستخدم. websearch_to_tsquery يدعم بناء الجملة المشابه لـ Google (علامات الاقتباس، OR، -) – الأفضل للبحث الذي يواجه المستخدم.
س: هل يمكنني البحث بعدة لغات؟
ج: نعم — يحتوي PostgreSQL على تكوينات للبحث عن النص للعديد من اللغات (الإنجليزية، والإسبانية، والفرنسية، وما إلى ذلك) التي تتعامل مع الاشتقاق وكلمات التوقف لكل لغة. حدد اللغة في to_tsvector/to_tsquery. قم بتخزين عمود اللغة إذا كان المحتوى الخاص بك متعدد اللغات.
الخلاصة
يتعامل البحث عن النص الكامل المدمج في PostgreSQL مع معظم احتياجات البحث الخاصة بالتطبيقات دون تعقيد محرك بحث منفصل. استخدمعمود tsvector الذي تم إنشاؤه المخزن مع فهرس GIN للأداء، وts_rank لتصنيف الصلة، وts_headline للتمييز، وpg_trgm للتسامح مع الأخطاء المطبعية. قم بوزن الحقول المهمة (مثل العناوين) بدرجة أعلى باستخدام الوزن المحدد، واستخدم plainto_tsquery أو websearch_to_tsquery لإدخال المستخدم الآمن. حجز Elasticsearch للبحث واسع النطاق جدًا أو المتقدم متعدد الأوجه. بالنسبة للمدونات والتجارة الإلكترونية ومواقع المحتوى ومعظم تطبيقات SaaS، يعد بحث PostgreSQL أكثر بساطة، ولا يحتاج إلى بنية تحتية إضافية، ويظل متسقًا مع بياناتك، ويعمل بشكل ممتاز – وهو خيار افتراضي رائع قبل الوصول إلى بنية تحتية مخصصة للبحث.
🔗 Share this article
✍️ Leave a Comment