חיפוש טקסט מלא: יישום PostgreSQL, Elasticsearch, Meilisearch

כשקטלוג גדל, החיפוש הסטנדרטי מתחיל להיכשל: משתמשים לא מוצאים את המוצרים שהם צריכים, וזמני התגובה מתארכים. אנחנו מיישמים חיפוש טקסט מלא באמצעות PostgreSQL, Elasticsearch או Meilisearch, ובוחרים את הפתרון האופטימלי למשימות שלכם. הצוות שלנו מספק את הפרויקט במפתח מלא—מבדיקת היתכנות ועד קונפיגורציה ותמיכה שוטפת—ומבטיח חיפוש מהיר ורלוונטי שגדל עם העסק שלכם.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
חיפוש טקסט מלא: יישום PostgreSQL, Elasticsearch, Meilisearch
בינוני
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

תארו לעצמכם חנות מסחר אלקטרוני עם 50,000 מוצרים שבה משתמשים לא מוצאים "אוזניות אלחוטיות" כי רק התאמות מדויקות בכותרת מוחזרות. אנחנו פותרים זאת עם חיפוש טקסט מלא (FTS). עבור קטלוג אלקטרוניקה של 120,000 פריטים (SKU), יישמנו Elasticsearch עם מנתח (stemmer) רוסי ופאקטות (facets). זמן החיפוש הממוצע ירד מ-3 שניות ל-0.1 שניות, וההמרה עלתה ב-30%. במשך 5 שנים, השלמנו מעל 15 פרויקטי FTS, הפחתנו את זמן החיפוש פי 10 והגדלנו את ההמרה ב-25%. חיסכון בעלויות תשתית מגיע ל-30% על ידי הורדת עומס ממסד הנתונים. להלן, נפרט כיצד לבחור וליישם חיפוש טקסט מלא—מ-PostgreSQL FTS ועד מנועים חיצוניים.

אילו בעיות פותר חיפוש טקסט מלא?

על נפחים גדולים, שאילתות LIKE גורמות לסריקת טבלאות מלאה. עם 100,000 מוצרים, זמן התגובה עולה על 5 שניות, מה שגורם לאובדן של עד 40% מהמשתמשים. FTS משתמש באינדקסים הפוכים (inverted indexes) כדי למצוא רשומות במילישניות. בנוסף, FTS מדרג תוצאות לפי רלוונטיות: כותרות מקבלות משקל גדול יותר מתיאורים, ורשומות חדשות מדורגות גבוה יותר. זה מגדיל את המרת החיפוש ב-25% ומפחית את העומס על מסד הנתונים.

באיזו ארכיטקטורה לבחור: FTS מובנה או מנוע חיצוני?

הבחירה תלויה בנפח הנתונים ובדרישות התכונות. עבור קטלוג טיפוסי של עד 100,000 רשומות, PostgreSQL FTS מספיק. עבור תרחישים מורכבים (חיפוש מטושטש, פאקטות, מילים נרדפות), יש צורך ב-Elasticsearch או Meilisearch.

PostgreSQL FTS: אפשרות מובנית

הגדרת סכמה:

ALTER TABLE products ADD COLUMN search_vector TSVECTOR GENERATED ALWAYS AS ( to_tsvector('russian', coalesce(title, '') || ' ' || coalesce(description, '') || ' ' || coalesce(brand, '') ) ) STORED;
CREATE INDEX idx_products_fts ON products USING GIN (search_vector);

ALTER TABLE products ADD COLUMN search_vector TSVECTOR GENERATED ALWAYS AS ( to_tsvector('russian', coalesce(title, '') || ' ' || coalesce(description, '') || ' ' || coalesce(brand, '') ) ) STORED; CREATE INDEX idx_products_fts ON products USING GIN (search_vector); — העמודה מתעדכנת אוטומטית ב-INSERT/UPDATE, אין צורך בטריגר.

חיפוש עם דירוג ותקצירים:

-- Простой запрос
SELECT id, title, ts_rank(search_vector, query) AS rank,
       ts_headline('russian', description, query, 'MaxWords=30, MinWords=15, StartSel=<b>, StopSel=</b>') AS excerpt
FROM products,
     plainto_tsquery('russian', 'беспроводные наушники') AS query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

-- websearch_to_tsquery: поддерживает "фразы", -исключения, OR
SELECT id, title
FROM products
WHERE search_vector @@ websearch_to_tsquery('russian', '"беспроводные наушники" -проводные')
ORDER BY ts_rank(search_vector, websearch_to_tsquery('russian', '"беспроводные наушники" -проводные')) DESC;

GENERATED ALWAYS AS ... STORED יוצר תקציר עם התאמות מודגשות. פרטים נוספים ב-PostgreSQL.

Elasticsearch: מתי יש צורך במנוע חיצוני

ל-PostgreSQL FTS יש מגבלות: אין חיפוש מטושטש, אין מילים נרדפות מובנות, אין אגרגציות פאקטות. אם אלה נדרשות, השתמשו ב-Elasticsearch או OpenSearch.

סכמת אינדקס עם מנתח רוסי:

PUT /products {
  "settings": {
    "analysis": {
      "analyzer": {
        "russian_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "russian_stop", "russian_stemmer"]
        }
      },
      "filter": {
        "russian_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "russian_stemmer": {
          "type": "stemmer",
          "language": "russian"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "russian_analyzer",
        "boost": 3
      },
      "brand": {
        "type": "text",
        "analyzer": "russian_analyzer",
        "boost": 2
      },
      "description": {
        "type": "text",
        "analyzer": "russian_analyzer"
      },
      "category_id": {
        "type": "keyword"
      },
      "price": {
        "type": "double"
      },
      "status": {
        "type": "keyword"
      },
      "created_at": {
        "type": "date"
      }
    }
  }
}

חיפוש עם פאקטות ופילטרים:

POST /products/_search
{
  "query": {
    "bool": {
      "must": {
        "multi_match": {
          "query": "беспроводные наушники",
          "fields": ["title^3", "brand^2", "description"],
          "type": "best_fields",
          "fuzziness": "AUTO"
        }
      },
      "filter": [
        {
          "term": {
            "status": "published"
          }
        },
        {
          "range": {
            "price": {
              "gte": 1000,
              "lte": 15000
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "by_brand": {
      "terms": {
        "field": "brand.keyword",
        "size": 20
      }
    },
    "price_stats": {
      "stats": {
        "field": "price"
      }
    }
  },
  "highlight": {
    "fields": {
      "title": {
        "number_of_fragments": 0
      },
      "description": {
        "fragment_size": 150,
        "number_of_fragments": 3
      }
    }
  },
  "from": 0,
  "size": 20
}

סנכרון עם PostgreSQL באמצעות CDC (Debezium + Kafka) מבטיח שהנתונים מגיעים ל-ES גם בזמן תקלות שירות. התיעוד ב-Elasticsearch מכסה את כל התכונות.

כיצד להבטיח סנכרון נתונים בין PostgreSQL ל-Elasticsearch?

לסנכרון שינויים, אנו משתמשים ב-Change Data Capture (CDC) עם Debezium ו-Kafka. Debezium עוקב אחר שינויי WAL של PostgreSQL ומפרסם אירועים ל-Kafka, שממנו Elasticsearch (באמצעות Logstash או צרכן מותאם אישית) מקבל עדכונים. לחלופין, שירות API ישיר קורא מעת לעת שינויים לפי חותמת זמן. CDC אמין יותר: הוא לא מאבד נתונים בזמן תקלות ואינו דורש שאילתות נוספות למסד הנתונים. כדי להגדיר CDC, הפעילו רישום WAL (-- Простой запрос SELECT id, title, ts_rank(search_vector, query) AS rank, ts_headline('russian', description, query, 'MaxWords=30, MinWords=15, StartSel=<b>, StopSel=</b>' ) AS excerpt FROM products, plainto_tsquery('russian', 'беспроводные наушники') AS query WHERE search_vector @@ query ORDER BY rank DESC LIMIT 20; -- websearch_to_tsquery: поддерживает "фразы", -исключения, OR SELECT id, title FROM products WHERE search_vector @@ websearch_to_tsquery('russian', '"беспроводные наушники" -проводные') ORDER BY ts_rank(search_vector, websearch_to_tsquery('russian', '"беспроводные наушники" -проводные')) DESC; ) וצרו פרסום (publication) עבור הטבלאות.

למה ליישם חיפוש טקסט מלא מיד?

משתמשים עוזבים אם הם לא מוצאים מוצר תוך 3 שניות. חיפוש טקסט מלא מעלה את המרת החיפוש ב-25% ומפחית את העומס על מסד הנתונים פי 10. המהנדסים המוסמכים של Elastic אצלנו עוזרים לבחור את הפתרון האופטימלי עבור המחסנית שלכם. צרו קשר—נעריך את הפרויקט שלכם ונמליץ על הגישה הטובה ביותר.

איך אנחנו עושים את זה: מקרה בוחן

יישמנו חיפוש עבור קטלוג של 120,000 מוצרים ב-3 ימים. השתמשנו ב-Elasticsearch עם מנתח רוסי ופאקטות לפי מותג, מחיר וסטטוס. תוצאה: זמן החיפוש הממוצע ירד מ-3 שניות ל-0.1 שניות, וההמרה עלתה ב-30%.

תהליך העבודה: תוכנית שלב אחר שלב

שלב משך תוצאה
אנליטיקה 0.5 ימים בדיקת סכמת DB, תרחישי חיפוש, שדות עדיפות
עיצוב 0.5 ימים בחירת מנוע (PostgreSQL FTS / Elasticsearch / Meilisearch) וסכמת אינדקס
יישום 1–2 ימים תצורה, קוד אינדוקס ושאילתות
בדיקות 0.5 ימים אימות רלוונטיות, ביצועים וכיסוי
פריסה 0.5 ימים הגדרת סנכרון (CDC או שירות) וניטור

טעויות נפוצות

  • שימוש ב-LIKE לחיפוש בטבלאות גדולות (איטי, ללא מורפולוגיה).
  • אי מתן משקל לשדות (כותרת ותיאור מדורגים שווה).
  • התעלמות מנורמליזציה (טיפול בסיומות מילים ורישיות).

השוואת מנועים

תכונה PostgreSQL FTS Elasticsearch/OpenSearch Meilisearch
זמן הגדרה דקות שעות–ימים דקות
חיפוש מטושטש באמצעות הרחבות מובנה מובנה
פאקטות קשה מובנה מובנה
סנכרון לא נדרש CDC או סנכרון CDC או סנכרון
תשתית כבר קיימת +שרת JVM +שרת Go
הגדרת מנתח נוסף ב-Meilisearch

ב-Meilisearch, פשוט הגדירו את שפת האינדקס בעת היצירה:

curl -X POST 'http://localhost:7700/indexes' \
  -H 'Content-Type: application/json' \
  -d '{ "uid": "products", "primaryKey": "id" }'

לאחר מכן הגדירו שדות חיפוש ומשקלים באמצעות הגדרות PATCH. צרו קשר—נעריך את הפרויקט שלכם ונמליץ על הפתרון האופטימלי.