חיפוש וקטורי סמנטי ונושאים נוספים לזכייה בלבבות ובאהבתם של אחרים
תמונת המצב המלאה של עולם החיפוש: חיפוש מדויק, מטושטש, סמנטי והיברידי — ומתי לשלב ביניהם.
בעמוד זה
- מפת האפשרויות לחיפוש וקטורי: השוואה בין 16 אפשרויות
- מונחים המשמשים במדריך הזה
- כיצד Embeddings מוצאים תוכן קשור
- מתי pgvector מנצח
- שלבו מילות מפתח ווקטורים עבור שאילתות מעורבות
- התאימו כל ממשק חיפוש לסוגי השאילתות שלו
סעיפי משנה
- מתי לעבור מעבר ל־pgvector
- אל תבצעו Embedding למזהים ותצפו להתאמות מדויקות
- בנו את החיפוש סביב מבנה השאילתה
חיפוש אינו דבר אחד, וחיפוש סמנטי אינו תחליף לכל השאר.
מפת האפשרויות לחיפוש וקטורי: השוואה בין 16 אפשרויות
השוו בין מודל הפריסה, הרישוי, יכולות החיפוש והתאמה לעומסי עבודה. הסברים על העמודות ודוגמאות SQL מופיעים בהמשך.
| מסד נתונים | פריסה | רישיון | חיפוש היברידי | וקטורים דלילים | ממשק שאילתות | Embedding מולטימודלי מובנה | אינדקס בדיסק | מגבלות ממד וקטור | מתאים במיוחד ל- |
|---|---|---|---|---|---|---|---|---|---|
| pgvector | אירוח עצמי / מנוהל (Supabase, Neon, RDS) | OSS (PostgreSQL) | ידני (RRF באמצעות SQL) | ❌ | ✅ SQL מלא | ❌ | ✅ HNSW בדיסק | 16,000 באחסון; 2,000 באינדקס vector | כבר עובדים עם Postgres; כמויות וקטורים בינוניות |
| Qdrant | אירוח עצמי / ענן | Apache 2.0 | ✅ BM25 מובנה | ✅ תמיכה בשלה | ❌ (REST/gRPC) | ❌ | ✅ | 65,535 | שאילתות מסוננות בקנה מידה גדול; מטא-דטה מורכב |
| Weaviate | אירוח עצמי / ענן | BSD 3 | ✅ BM25 + RRF מובנים | ✅ | ❌ (GraphQL / gRPC) | ✅ באמצעות מודולים | ✅ | 65,535 | דפוסי גישה של GraphQL; וקטוריזציה מובנית |
| Pinecone | ענן בלבד | קנייני | ✅ (נוסף ב-2024) | ✅ | ❌ | ❌ | ✅ (serverless) | 20,000 | פשטות מנוהלת; ללא צוות תפעול |
| Milvus / Zilliz | אירוח עצמי / ענן (Zilliz) | Apache 2.0 | ✅ מובנה | ✅ | ✅ דמוי SQL (Milvus Query Language) | ✅ | ✅ DiskANN | 32,768 | קנה מידה של מיליארדים; on-prem ארגוני |
| Chroma | Embedded / אירוח עצמי | Apache 2.0 | ❌ | ❌ | ❌ | ❌ | ❌ | 65,535 | פיתוח מקומי ואבות-טיפוס בלבד |
| LanceDB | Embedded / ענן | Apache 2.0 | ✅ | ❌ | ✅ SQL באמצעות DataFusion | ✅ מובנה | ✅ (פורמט Lance) | ללא הגבלה | Edge / serverless; lakehouse מולטימודלי |
| Orama | Embedded / ענן | Apache 2.0 | ✅ טקסט מלא + וקטור | ❌ | ❌ | ❌ | ❌ | משתנה | אפליקציות JS/edge; חיפוש קל לאתרים ולאפליקציות |
| Turbopuffer | ענן בלבד (serverless) | קנייני | ✅ BM25 + וקטור | ❌ | ❌ | ❌ | ✅ (אחסון אובייקטים) | 16,000 | SaaS רב-דיירים; מיליוני namespaces |
| Elasticsearch | אירוח עצמי / Elastic Cloud | SSPL / AGPLv3 | ✅ RRF + ELSER דליל | ✅ (ELSER) | ✅ Query DSL | ❌ | ✅ DiskBBQ | 4,096 | כבר משתמשים ב-Elastic stack; חיפוש היברידי ארגוני |
| OpenSearch | אירוח עצמי / מנוהל ב-AWS | Apache 2.0 | ✅ RRF + Neural Search | ✅ | ✅ Query DSL | ❌ | ✅ FAISS + HNSW | 16,000 | AWS-native; חלופת Elastic בקוד פתוח |
| Vespa | אירוח עצמי / ענן | Apache 2.0 | ✅ מובנה | ✅ טנסורים / דירוג לקסיקלי | ✅ YQL | ✅ טנסורים | ✅ | למעשה ללא הגבלה | מערכות חיפוש + דירוג + המלצות |
| ClickHouse | אירוח עצמי / ענן | Apache 2.0 | ידני | ❌ | ✅ SQL מלא | ❌ | ✅ עמודות + HNSW | משתנה | אנליטיקה ולוגים עם חיפוש וקטורי לצד OLAP |
| MongoDB Atlas | ענן / אירוח עצמי | SSPL | ✅ מובנה | ❌ | ✅ MQL + aggregation | ❌ | ✅ HNSW | 8,192 | כבר משתמשים ב-MongoDB; מסמכים + וקטורים במקום אחד |
| Redis (VSS) | אירוח עצמי / Redis Cloud | RSALv2 / SSPL | ✅ (RediSearch) | ✅ | ❌ | ❌ | ❌ RAM בלבד | 32,768 | השהיה נמוכה במיוחד; חיפוש וקטורי בשכבת המטמון |
| Marqo | ענן / אירוח עצמי | Apache 2.0 | ✅ | ❌ | ❌ | ✅ דגש מובנה | ✅ | משתנה | מולטימודלי מקצה לקצה: תמונה + טקסט + וידאו |
“מצא משתמש עם כתובת האימייל dan@example.com” ו“מצא לי מאמרים על דיבוג כמהנדס מתחיל” מתוארים שניהם כחיפוש, אבל כמעט שאין ביניהם דבר משותף מבחינת הבעיה ההנדסית. לראשון יש תשובה נכונה ואיתור באינדקס ב־O(log n). לשני אין תשובה נכונה — יש רק רלוונטיות — והוא דורש הבנה של שפה, כוונה ומשמעות.
המהנדסים שהכי משכנעים כשמדובר בהחלטות חיפוש — אלה שמנצחים בוויכוחים ומעלים לפרודקשן את המערכת הנכונה — מבינים את התמונה כולה. הם יודעים לאיזה כלי לפנות ולמה, ויכולים להסביר זאת בבירור.
המאמר הזה עוסק בשכבה הסמנטית: מה חיפוש וקטורי עושה בפועל, מתי הוא מנצח, ומתי עדיף שלא יפריע. הגרסה השימושית אינה “להטמיע הכול”. היא לדעת מתי הווקטורים צריכים לפעול לצד חיפוש לקסיקלי, fuzzy וחיפוש בהתאמה מדויקת בתוך ארכיטקטורה היברידית.
החצי הלקסיקלי וה־fuzzy של התמונה — tsvector, pg_trgm, pg_search — מופיע ב־מדריך לחיפוש טקסט ב-Postgres 2026.
מונחים המשמשים במדריך הזה
Embedding — רשימה צפופה של מספרים בנקודה צפה, שמופקת על ידי מודל ומייצגת קטע טקסט (או תמונה, אודיו וכדומה) כנקודה במרחב רב־ממדי. תוכן בעל משמעות דומה מגיע לנקודות סמוכות; תוכן שאינו קשור מגיע לנקודות רחוקות.
חיפוש לקסיקלי — חיפוש המבוסס על התאמה מדויקת של מילים וטוקנים. מהיר, דטרמיניסטי ונכון עבור מונחים ידועים. אינו מבין מילים נרדפות, ניסוחים חלופיים או מקבילות בין שפות.
חיפוש סמנטי — חיפוש המבוסס על משמעות ולא על טוקנים. שאילתה כמו “איך מטפלים ב-timeouts” יכולה למצוא מסמך שכותרתו “הגדרת מדיניות retry” בלי אף מילה משותפת, מפני שה־embeddings שלהם קרובים גאומטרית.
וקטור — רשימת מספרים. בהקשר של חיפוש, זהו הפלט של מודל embedding. “חיפוש וקטורי” מוצא את הווקטורים הקרובים ביותר לווקטור השאילתה לפי מרחק גאומטרי.
FTS (Full-Text Search) — החיפוש הלקסיקלי המובנה של Postgres, המופעל באמצעות tsvector / tsquery. הוא מפרק טקסט לטוקנים, מבצע stemming ומאנדקס אותו עבור שאילתות מילות מפתח. חזק עבור פרוזה ואיתור מונחים מדויקים; עיוור למשמעות.
BM25 — אלגוריתם דירוג לחיפוש לקסיקלי (בשימוש Elasticsearch, Qdrant ואחרים). הוא מדרג תוצאות לפי תדירות המונח, כשהיא משוקללת לפי מידת הנדירות של המונח בכלל הקורפוס. טוב יותר מהתאמת מילות מפתח גולמית; עדיין לקסיקלי.
HNSW (Hierarchical Navigable Small World) — אינדקס חיפוש השכנים הקרובים בקירוב, שהוא הסטנדרט לחיפוש וקטורי. הוא בונה גרף קירבה רב־שכבתי עבור שאילתות דמיון מהירות ובעלות recall גבוה. pgvector, Qdrant, Weaviate ורוב האחרים משתמשים בו.
RRF (Reciprocal Rank Fusion) — אלגוריתם למיזוג רשימות תוצאות מדורגות ממספר מערכות אחזור. הוא משתמש רק במיקום בדירוג — אין צורך בנרמול ציונים. תוצאה שמדורגת גבוה גם ברשימת ה־FTS וגם ברשימת הווקטורים תקבל ציון משולב חזק יותר מתוצאה ששולטת רק באחת מהן.
כיצד Embeddings מוצאים תוכן קשור
Embeddings וקטוריים ממירים טקסט (או תמונות, אודיו וכדומה) לרשימת מספרים — נקודה במרחב רב־ממדי. מודל embedding מאומן כך שטקסטים בעלי קשר סמנטי ימוקמו קרוב זה לזה במרחב הזה. "Dog" ו־"canine" יסתיימו קרוב זה לזה. "Running a marathon" ו־"running a Python script" יסתיימו רחוק זה מזה, אף על פי שהם חולקים מילה.
חיפוש דמיון במרחב הזה מוצא מסמכים שמשמעותם קרובה ביותר למשמעות השאילתה, בלי קשר לחפיפה המדויקת במילים.
המשמעות היא ש:
"How do I configure request timeouts?"יכול להתאים למאמר שכותרתו"Setting connection limits and retry policies"— ללא מילות מפתח חופפות, אך עם רלוונטיות רעיונית גבוהה"Something light for a summer evening"יכול להתאים להמלצת יין בלי שאף אחת ממילות המפתח תופיע בתיאור המוצר- שאילתה באנגלית יכולה להתאים למסמכים רלוונטיים בצרפתית, בספרדית או ביפנית, אם מודל ה־embedding אומן באופן רב־לשוני
חיפוש לקסיקלי (tsvector, pg_trgm) לא יכול לעשות דבר מזה. הוא פועל על מילים ותווים, לא על משמעות. הכלים אינם בני־החלפה — הם פותרים בעיות שונות.
מתי pgvector מנצח
בניית RAG. Retrieval-Augmented Generation מאחזר את מקטעי המסמכים שמשמעותם קרובה ביותר לשאלת המשתמש, ואז מעביר אותם למודל שפה כהקשר. שלב האחזור הזה הוא פעולה וקטורית. FTS יפספס ניסוחים מחדש, מילים נרדפות והתאמות רעיוניות שמקטע רלוונטי עשוי לבטא בצורה אחרת. היתרון של pgvector על פני מאגר וקטורים עצמאי: הוא רץ בתוך מופע ה־Postgres הקיים — בלי שירות נפרד לפריסה, לתפעול או לסנכרון נתונים אליו.
המשתמשים מתארים את מה שהם רוצים, לא את מה שצריך לחפש. ל־"Articles about building confidence as a new manager" אין מילות מפתח שסביר שיופיעו באופן עקבי בפוסטים הרלוונטיים. ״A lightweight framework for handling side effects״ עשוי שלא להשתמש באותן מילים בדיוק בתיעוד. חיפוש וקטורי מתאים לפי הכוונה, לא לפי האיות.
מציאת פריטים דומים. מוצרים קשורים, פניות תמיכה דומות, דיווחי באגים כפולים, מאמרים שאולי יעניינו אתכם גם. "Find issues similar to this one" הוא חיפוש שכנים קרובים — מטמיעים את הפריט ומוצאים את שכניו הגאומטריים. הסתייגות חשובה: חיפוש וקטורי תמיד מחזיר תוצאות, גם כששום דבר אינו באמת דומה. במקרי שימוש של מניעת כפילויות והמלצות, סננו לפי סף דמיון מינימלי (למשל, דמיון קוסינוס ≥ 0.80), כדי להימנע מהצגת התאמות ברמת ביטחון נמוכה כאילו הן משמעותיות.
מניעת כפילויות סמנטית. לפני שמאנדקסים תוכן עבור RAG או חיפוש, לעיתים קרובות צריך לזהות כפילויות־כמעט בקורפוס — מאמרים שעודכנו מספר פעמים, פניות תמיכה שנפתחו פעמיים, או רשומות בבסיס ידע שחופפות במידה משמעותית. הטמיעו את המסמכים וסננו לפי סף דמיון קוסינוס כדי לסמן או למזג כפילויות־כמעט לפני שהן מזהמות את האינדקס. כך נמנע מהאחזור להחזיר מספר מקטעים כמעט זהים ולבזבז את חלון ההקשר.
חיפוש רב־לשוני. מודלי embedding רב־לשוניים ממפים תוכן בעל משמעות שקולה בשפות שונות לווקטורים סמוכים. שאילתה בספרדית עבור "perder peso" יכולה להתאים למאמר באנגלית על "sustainable weight loss habits" — ללא טוקנים משותפים, אך עם אותה משמעות בסיסית. FTS דורש הגדרת מילון נפרדת לכל שפה ומתמודד poorly עם שאילתות חוצות־שפות. pg_trgm אינו תלוי בשפה, אך הוא אורטוגרפי, לא סמנטי.
הגדרת pgvector
מהתקנת ההרחבה ועד לשאילתת דמיון, ההגדרה מסתכמת בכמה פקודות SQL:
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- HNSW is usually the first index to try for moderate-size datasetsCREATE INDEX documents_embedding_idx ON documents USING hnsw (embedding vector_cosine_ops);
-- Semantic search querySELECT id, title, 1 - (embedding <=> $1::vector) AS similarityFROM documentsORDER BY embedding <=> $1::vectorLIMIT 10;<=> הוא מרחק קוסינוס. 1 - cosine_distance נותן דמיון קוסינוס (1.0 = זהה, 0.0 = אורתוגונלי). עבור ivfflat (החלופה הוותיקה ומהירה יותר לבנייה), השתמשו ב־lists = sqrt(row_count) כנקודת התחלה.
היכן חיפוש וקטורי מחזיר את התשובה הלא נכונה
- התאמת טוקנים מדויקת — מק”טים, קודי שגיאה, שמות פונקציות.
ORD-12345אינו דומה סמנטית לשום דבר. חיפוש מבוסס־embeddings עלול להחזיר אתORD-12344או דבר שאינו רלוונטי. השתמשו ב־FTS או באינדקס B-tree. - שמות ושמות פרטיים. מרחב ה־embeddings מתארגן לפי משמעות, לא לפי איות. הרשומה של המשתמש “Micheal Jordan” לא בהכרח תמוקם ליד “Michael Jordan” במרחב הווקטורי.
- מחרוזות קצרות שבהן דמיון ברמת התווים חשוב יותר מהמשמעות.
pg_trgmמתאים לכך. - שאילתות שבהן המונח המדויק חייב להופיע. BM25 ו־FTS אמינים יותר להתאמה לפי מונח ידוע.
שלבו מילות מפתח ווקטורים עבור שאילתות מעורבות
תיעוד טכני הוא הדוגמה הברורה ביותר למקרה שבו אף אחד מהכלים אינו מספיק לבדו.
משתמשים שמחפשים “how to configure timeouts” זקוקים להתאמה מושגית: מאמר שכותרתו “Setting retry policies and connection limits” אינו חולק מילות מפתח עם השאילתה, אבל הוא בדיוק מה שהם צריכים.
אותם משתמשים מחפשים גם את withRetry(), את ECONNRESET ואת ERR_SOCKET_TIMEOUT. המחרוזות המדויקות האלה חייבות להופיע — התאמה סמנטית עלולה שלא למצוא אותן באופן אמין, ותוצאה חיובית שגויה (דומה מבחינה מושגית, אבל לא ה־API הנכון) מטעה בפועל.
חיפוש וקטורי מטפל בשאילתות המושגיות. FTS מטפל במונחים המדויקים. אף אחד מהם אינו מטפל היטב בשני הסוגים לבדו.
הפתרון הוא חיפוש היברידי: מריצים את שניהם וממזגים את התוצאות.
מיזוג תוצאות מדורגות באמצעות RRF
Reciprocal Rank Fusion (RRF) הוא האלגוריתם המקובל לשילוב רשימות מדורגות ממערכות אחזור שונות. הוא אינו דורש לנרמל ציונים בין המערכות — הוא משתמש רק במיקומים בדירוג. תוצאה שמופיעה גבוה בשתי הרשימות מקבלת ציון משולב חזק יותר מתוצאה ששולטת רק באחת מהן.
WITH fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank FROM documents, to_tsquery('english', $1) query WHERE search_vector @@ query LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(f.id, v.id) AS id, COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM fts_results f FULL OUTER JOIN vector_results v ON f.id = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;ה־60 במכנה הוא קבוע ה־RRF. ערכים גבוהים יותר ממתנים את ההבדלים בין מיקומי הדירוג; ערכים נמוכים יותר מעצימים אותם. ברירת המחדל, 60, עובדת היטב עבור רוב סוגי התוכן.
RRF עוקף את הבעיה הקשה יותר של נרמול ts_rank (ציון המבוסס על תדירות לוגריתמית) מול מרחק קוסינוס (מדד גאומטרי). אלה מדדים שאי אפשר להשוות ישירות. RRF שואל רק: “כמה גבוה הופיעה התוצאה הזו בכל אחת מהרשימות?”
הוסיפו טריגרמות לשגיאות כתיב ולשמות
בחיפוש למשתמשים על תוכן מעורב — שבו משתמשים עשויים לחפש שם של אדם, מושג או מונח מדויק באותו סשן — מיזוג תלת־כיווני מטפל בכולם:
WITH trgm_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY similarity(title, $1) DESC) AS rank FROM documents WHERE title % $1 LIMIT 50),fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, to_tsquery('english', $1)) DESC) AS rank FROM documents WHERE search_vector @@ to_tsquery('english', $1) LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(t.id, f.id, v.id) AS id, COALESCE(1.0 / (60 + t.rank), 0) + COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM trgm_results t FULL OUTER JOIN fts_results f ON t.id = f.id FULL OUTER JOIN vector_results v ON COALESCE(t.id, f.id) = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;כך מתקבל טיפול בהתאמות שמות גמישות (טריגרמות), בהתאמות מדויקות של מילות מפתח (FTS) ובשאילתות מושגיות (וקטור). תיבת חיפוש יחידה יכולה לשרת את שלושת סוגי הכוונה האלה של המשתמשים.
התאימו כל ממשק חיפוש לסוגי השאילתות שלו
ביישומים אמיתיים כמעט אף פעם אין ממשק חיפוש יחיד. יש בהם כמה ממשקים, שלכל אחד מהם צורך אחר:
| ממשק | מה המשתמשים מחפשים | שכבות מומלצות |
|---|---|---|
| חיפוש בבלוג / בתיעוד | מילות מפתח + מושגים | FTS + pgvector (RRF) |
| חיפוש שמות משתמשים/לקוחות | שמות עם שגיאות כתיב | pg_trgm |
| חיפוש מוצרים | שמות, תיאורים, “דומה ל־“ | pg_trgm + FTS + pgvector |
| מניעת כפילויות בכרטיסי תמיכה | ”בעיות הדומות לזו” | pgvector בלבד |
| חיפוש פנימי לפי SKU/הזמנה | מזהים מדויקים | אינדקס B-tree |
| RAG על בסיס ידע גדול | שאלות בשפה טבעית | pgvector (מסמכים מחולקים למקטעים) |
| “אולי תאהבו גם” במסחר אלקטרוני | דמיון התנהגותי + סמנטי | pgvector |
| השלמה אוטומטית | קידומת, עמידות לשגיאות כתיב | pg_trgm |
אלה לא תרחישים היפותטיים. רוב היישומים עתירי התוכן זקוקים לפחות לשני ממשקי חיפוש נפרדים, עם צורות שאילתה שונות. הפיתוי הוא לבחור גישה אחת ולהשתמש בה בכל מקום — בדרך כלל חיפוש וקטורי כיום, כי זו הבחירה האופנתית. התוצאה היא embeddings יקרים עבור בעיות שבהן אינדקס טריגרמות היה מהיר יותר, זול יותר ונכון יותר.
הוסיפו שכבת חיפוש כשסוג שאילתה נכשל
הוסיפו שכבה כאשר מופיע מצב כשל שהשכבה הנוכחית אינה יכולה לתקן:
- משתמשים מתלוננים על כך ששגיאות כתיב אינן מוצאות התאמות ← הוסיפו
pg_trgm - משתמשים מחפשים לפי מושג ומפספסים תוצאות רלוונטיות ← הוסיפו pgvector
- משתמשים מחפשים סמלים או קודים מדויקים ומקבלים במקום זאת תוצאות מושגיות ← הוסיפו FTS או בדקו אם אתם מסתמכים יותר מדי על חיפוש וקטורי
- זמן האחזור הופך לבעיה ← בדקו סינון מוקדם, אינדקסים מקורבים או חנות ייעודית
מתי לעבור מעבר ל־pgvector
pgvector מטפל בחלק גדול מצורכי החיפוש של יישומים, עוד לפני שיש צורך במסד נתונים נוסף. נקודת החיתוך המשוערת תלויה במספר הווקטורים, בהגדרות האינדקס, בקצב הכתיבות, במסננים, בחומרה ובמקביליות. לכן יש להתייחס לכלל כמו “פחות מ־10 מיליון וקטורים” כהנחת פתיחה שצריך למדוד באמצעות בנצ’מרק, לא כמגבלת מוצר. כאשר באמת גדלתם מעבר ליכולותיו — מקביליות גבוהה מאוד, דרישות p99 נמוכות מאוד, מיליארדי וקטורים או צורך רציני בבידוד בין דיירים — השוק של מסדי נתונים וקטוריים ייעודיים רחב, וכדאי להבין אותו.
איך לקרוא את ההשוואה
חיפוש היברידי פירושו שחיפוש מילות מפתח באמצעות BM25 ודמיון וקטורי רצים בשאילתה אחת וממוזגים באמצעות RRF. בלעדיו, עליכם לבחור מצב חיפוש אחד או למזג בעצמכם שתי שאילתות.
וקטורים דלילים הולכים רחוק יותר מ־BM25. לווקטור SPLADE יש כ־30,000 ממדים (אחד לכל מונח באוצר המילים), וכ־98% מהם הם אפסים. המיקומים שאינם אפס מציינים אילו מונחים חשובים ובאיזו מידה. שאילתה עבור “dogs” תשקלל גם את “canine” ואת “pet” — דיוק ברמה של BM25, בתוספת הרחבת מונחים בתוך אינדקס וקטורי. אם העמודה הזו היא false, תצטרכו שכבת FTS נפרדת עבור שאילתות של מונחים מדויקים.
# SPLADE: ~30,000 dims, ~60 non-zero — only relevant vocabulary positions firedef encode_splade(text: str) -> dict: tokens = tokenizer(text, return_tensors="pt", truncation=True, max_length=512) with torch.no_grad(): output = model(**tokens) vec = torch.log1p(torch.relu(output.logits)).max(dim=1).values.squeeze() return {"indices": vec.nonzero().squeeze().tolist(), "values": vec[vec != 0].tolist()}SQL / דמוי־SQL עוסק למעשה בסינון. חיפוש וקטורי ללא סינון הוא דמו. עדיין צריך תחום של דייר, טווחי תאריכים, הרשאות ומסנני קטגוריות. SQL מלא (pgvector, LanceDB) מאפשר לבטא זאת לצד ה־joins הקיימים שלכם. מסדי נתונים ייעודיים משתמשים באובייקטי מסנני JSON (Qdrant, Pinecone), ב־DSL לשאילתות (Elasticsearch, Milvus) או ב־GraphQL (Weaviate). הם עובדים; SQL נעשה אטרקטיבי יותר ככל שלוגיקת הסינון מורכבת יותר.
-- pgvector: vector similarity is just another expressionSELECT id, title, 1 - (embedding <=> $1) AS scoreFROM documentsWHERE tenant_id = $2 AND category = ANY($3::text[]) AND created_at > NOW() - INTERVAL '90 days'ORDER BY embedding <=> $1LIMIT 10;# Qdrant: equivalent filter as a Python object — same result, more ceremonyresults = client.query_points( collection_name="documents", query=query_embedding, query_filter=models.Filter(must=[ models.FieldCondition(key="tenant_id", match=models.MatchValue(value=tenant_id)), models.FieldCondition(key="category", match=models.MatchAny(any=categories)), models.FieldCondition(key="created_at", range=models.DatetimeRange(gte=cutoff)), ]), limit=10,)תמיכה מולטימודלית מובנית פירושה שמסד הנתונים מגיע עם מודלי embedding עבור תוכן שאינו טקסט. אתם מוסרים לו URL גולמי של תמונה; הוא מטפל בוקטוריזציה. רוב מסדי הנתונים אינם תלויים בשיטת embedding — אתם אחראים לצינור ה־embedding. Marqo ו־Weaviate (באמצעות מודולי CLIP/ImageBind) סוגרים את הלולאה הזו.
# Marqo: POST raw images, query with text — no external embedding stepmq.index("products").add_documents( [{"id": "shoe-001", "image": "https://cdn.example.com/shoes/001.jpg"}], tensor_fields=["image"])results = mq.index("products").search(q="lightweight shoes for summer")# Returns shoe-001 despite zero keyword overlap — CLIP handles the cross-modal matchאינדקס מבוסס־דיסק הוא מנוף עלויות. אינדקסי HNSW השוכנים ב־RAM עשויים לדרוש כמה גיגה־בייטים של RAM לכל מיליון וקטורים בעלי 1536 ממדים, לאחר שסופרים את הווקטורים הגולמיים, תקורת הגרף והמטא־נתונים. חלופות שהן native לדיסק (Milvus DiskANN, Elasticsearch DiskBBQ, פורמט Lance של LanceDB ושכבת אחסון האובייקטים של Turbopuffer) מוותרות לעיתים על חלק מזמן האחזור בתמורה לעלות תשתית נמוכה יותר. בעומסי RAG, שבהם זמן האחזור של המודל כבר שולט בתמונה, הפשרה הזו ראויה לעיתים קרובות לבנצ’מרק.
מספר הממדים המרבי הוא הגירה שמסתתרת בתוך הארכיטקטורה שלכם. text-embedding-3-large משתמש ב־3072 ממדים, Jina v3 יכול להפיק embeddings גדולים יותר, ומודלי מחקר ממשיכים לדחוף לממדים גבוהים יותר. חלק מהשירותים המנוהלים מפרסמים תקרות קשיחות למספר הממדים; אחרים מתעדים תקרות גבוהות או שאין להם תקרה מעשית עבור מודלי embedding טיפוסיים. בדקו את התיעוד העדכני לפני התחייבות. בחרו משהו עם מרווח נשימה; הגירה של אינדקס וקטורי לאחר שנתקלים בתקרת ממדים היא ספרינט כואב.
פשרות תפעוליות שהטבלה אינה יכולה להציג
ריבוי הדיירים של Turbopuffer בנוי סביב מספרים גבוהים מאוד של namespaces. המיצוב הציבורי שלו וסיפורי הלקוחות מדגישים עומסי עבודה כמו הקורפוס הגדול ורב־ה־namespaces של Notion. אם כל משתמש או ארגון זקוק לחיפוש וקטורי מבודד, הארכיטקטורה הזו יכולה לשנות את הכלכלה, אך עדיין בצעו בנצ’מרק לפי צורת הדיירים שלכם.
המצב המשובץ של LanceDB הוא הדבר הקרוב ביותר ל־“SQLite לחיפוש וקטורי”. הוא רץ בתוך התהליך, אינו דורש שרת, ועובד ב־Lambda, ב־Cloudflare Workers ובסביבות edge. הפורמט העמודתי Lance הופך הפעלה משובצת למעשית גם בקנה מידה אמיתי.
Chroma חזקה ביותר בסביבות פיתוח/בדיקות ובפריסות של אפליקציות קטנות. אם אתם מכוונים לקורפוסים גדולים מאוד, ל־HA, לעבודה עתירת דיסק או לחיפוש היברידי מהשורה הראשונה, העריכו מאגר שמיועד לייצור לפני שאתם מקדמים את אב־הטיפוס לתשתית.
Vespa היא מה שבוחרים כששליפה היא רק חצי מהמוצר. היא משלבת שליפה לקסיקלית, חיפוש nearest-neighbor, טנסורים, ביטויי דירוג, קיבוץ והגשה מקוונת. העוצמה הזו אמיתית, וכך גם המורכבות התפעולית ומורכבות המידול. היא מתאימה יותר לצוותי חיפוש/המלצות מאשר ל־“להוסיף חיפוש סמנטי לאפליקציית ה־CRUD שלי”.
ClickHouse שייכת לשיחה כשחיפוש מחובר לאנליטיקה. אם מקור האמת שלכם הוא אירועים, לוגים, traces או מדדים, ClickHouse משאירה מרחק וקטורי, סינון, אגרגציה ואינדוקס full-text רציני בתוך מנוע SQL אחד. היא אינה מסד נתונים וקטורי ייעודי, אבל לעיתים קרובות זו התשובה המשעממת-אך-נכונה לשליפה אנליטית.
וקטורים דלילים הם הדרך לקבל התאמת מילות מפתח באיכות של BM25 בתוך אינדקס וקטורי — בלי להפעיל מנוע full-text נפרד. ל־Qdrant ול־Elasticsearch יש כאן מימושים בשלים במיוחד. אם חיפוש היברידי הוא קריטי וארכיטקטורה של שתי מערכות אינה באה בחשבון, תמיכה בווקטורים דלילים היא מה שצריך לחפש.
בחרו מאגר לפי עומס העבודה
- מוצר SaaS עם בידוד לפי דייר → Turbopuffer
- סינון מורכב של מטא־נתונים בקנה מידה גדול → Qdrant
- אתם כבר על מחסנית Elastic/ELK → Elasticsearch עם DiskBBQ
- חברת AWS שרוצה קוד פתוח → OpenSearch
- פלטפורמת חיפוש/המלצות עם דרישות דירוג רציניות → Vespa
- אנליטיקה, observability, חיפוש בלוגים/אירועים → ClickHouse
- On-prem / self-hosted בקנה מידה של מיליארדים → Milvus
- Edge / serverless / מולטימודלי → LanceDB
- אפליקציית JS קטנה, אתר תיעוד או חוויית חיפוש native ל־edge → Orama
- אפס תפעול, העלות משנית → Pinecone
- מולטימודלי בראש סדר העדיפויות (תמונות, וידאו, אודיו) → Marqo
- אתם כבר על MongoDB → Atlas Vector Search
- אתם כבר על Postgres וצריכים יותר מרווח נשימה → Supabase Vector או Neon (שניהם pgvector מנוהל, עם כלי עבודה טובים יותר)
אל תבצעו Embedding למזהים ותצפו להתאמות מדויקות
אל תשתמשו בחיפוש וקטורי כחיפוש טקסט מטושטש עבור דברים שיש להם תשובות נכונות.
“מצאו לי את המשתמש עם כתובת האימייל dan@example.com” אינו בעיית חיפוש וקטורי. “מצאו את ההזמנה עם המזהה ORD-12345” גם לא. ביצוע embedding ל־ORD-12345 וחיפוש לפי דמיון קוסינוס יחזיר משהו — אבל הוא עלול להיות שגוי. למזהה יש תשובה נכונה. התאמה מקורבת של מזהה היא באג.
חיפוש וקטורי מחזיר את הדבר הדומה ביותר במאגר הנתונים שלכם, גם כששום דבר אינו רלוונטי בפועל. הוא לא יודע מתי אין תשובה טובה. זה בסדר עבור מסמכים קשורים. זו בעיה רצינית בשליפה מדויקת של רשומות, שבה תשובה שגויה ובטוחה בעצמה גרועה יותר מתוצאה ריקה.
אותו דבר עובד גם בכיוון ההפוך: אל תשתמשו ב־FTS עבור שאילתות שבהן המשתמש מתאר מושג. “מאמרים על קבלת החלטות קשות בתנאי אי־ודאות” אינו מכיל מילות מפתח אמינות. FTS יחזיר רעש או לא יחזיר דבר. השתמשו בכלי שמתאים למבנה השאילתה.
בנו את החיפוש סביב מבנה השאילתה
רוב מערכות החיפוש בייצור זקוקות ליותר משכבה אחת:
pg_trgmעבור שמות, שגיאות כתיב והשלמה אוטומטית- FTS /
pg_searchעבור חיפוש פרוזה המבוסס על מילות מפתח - pgvector עבור שאילתות סמנטיות ומושגיות
- מיזוג RRF עבור ממשקים שבהם משתמשים מערבבים סוגי שאילתות
- אינדקסים רגילים עבור מזהים מדויקים, מסננים ורשימות ממוינות
אלה אינם כלים מתחרים. הם משלימים זה את זה. מערכת חיפוש בנויה היטב בוחרת את השכבה הנכונה לכל מבנה שאילתה — וכשמבני השאילתות חופפים, היא מפעילה כמה שכבות וממזגת את התוצאות.
הצוותים שמספקים יכולות חיפוש טובות מבינים את כל המחסנית. אלה שלא, שולפים מסד נתונים וקטורי, מבצעים embedding להכול ותוהים למה שליפות מדויקות מחזירות לפעמים את הרשומה הלא נכונה.
