יצירה וניהול של אינדקס RUM

במסמך הזה מוסבר איך ליצור את התוסף RUM ואינדקסים כדי לבצע אופטימיזציה של חיפוש טקסט מלא ב-AlloyDB ל-PostgreSQL. הוא כולל דוגמאות לתרחישי שימוש נפוצים, כולל דירוג, חיפוש של צירופי מילים ומיון לפי חותמת זמן.

לפני שמתחילים

כדי ליצור את התוסף RUM, צריך להיות לכם תפקיד מסד הנתונים alloydbsuperuser.

תפקיד האדמין ב-AlloyDB‏ (roles/alloydb.admin) ב-IAM מעניק שליטה מלאה במשאבי AlloyDB, אבל לא מעניק את תפקיד מסד הנתונים alloydbsuperuser. כדי ליצור את התוסף, אדמין צריך להעניק לכם באופן מפורש את תפקיד מסד הנתונים alloydbsuperuser.

מידע נוסף על הענקת תפקידים זמין במאמר הוספת חשבון משתמש או חשבון שירות ב-IAM לאשכול.

יצירת תוסף RUM

צריך ליצור את תוסף ה-RUM פעם אחת לכל מסד נתונים.

  1. מתחברים למסד הנתונים של AlloyDB באמצעות psql או לקוח אחר. מידע נוסף זמין במאמר בנושא חיבור למופע של אשכול.
  2. מריצים את פקודת ה-SQL הבאה כדי ליצור את התוסף:

    CREATE EXTENSION IF NOT EXISTS rum;
    

יצירת אינדקס RUM

כדי לבצע אופטימיזציה של שאילתות חיפוש טקסט מלא, צריך ליצור אינדקס RUM על הנתונים. ‫RUM מציע כמה מחלקות אופרטורים לתרחישי שימוש שונים.

סוגים של מחלקות אופרטורים של RUM

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

סוג המפעיל תרחיש השימוש העיקרי מגבלות
rum_tsvector_ops חיפוש טקסט מלא רגיל עם דירוג וחיפוש ביטויים. לא רלוונטי
rum_tsvector_hash_ops אינדקס קטן יותר ועדכונים מהירים יותר לחיפוש טקסט מלא. לא תומך בחיפוש לפי קידומת.
rum_tsvector_addon_ops חיפוש טקסט מלא שממוין לפי עמודה אחרת. לא רלוונטי
rum_anyarray_ops חיפוש בעמודות של מערכים. לא רלוונטי
rum_<TYPE>_ops יצירת אינדקס לסוגים סקלריים של שאילתות שמבוססות על מרחק. לא רלוונטי
rum_tsvector_hash_addon_ops חיפוש טקסט מלא מבוסס-גיבוב שממוין לפי עמודה אחרת. אין תמיכה בהתאמה של קידומות.
rum_tsquery_ops יצירת אינדקס של ערכי tsquery מאוחסנים לחיפוש הפוך. לא רלוונטי
rum_anyarray_addon_ops חיפוש במערך ממוין לפי עמודה אחרת. לא רלוונטי

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

  1. צור טבלה בשם documents:

    CREATE TABLE documents (
      id SERIAL PRIMARY KEY,
      title TEXT NOT NULL,
      content TEXT NOT NULL,
      published_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
    );
    
  2. מאכלסים את הטבלה documents בנתונים לדוגמה:

    INSERT INTO documents (title, content) VALUES
      ('Title', 'This search engine is working as intended');
    
  3. מוסיפים לטבלה עמודה שנוצרה על ידי tsvector. בעמודה הזו מאוחסן באופן אוטומטי הטקסט שעבר עיבוד, והיא משפרת את ביצועי השאילתות:

    ALTER TABLE documents
    ADD COLUMN search_vector tsvector
    GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;
    
  4. יוצרים את אינדקס ה-RUM בעמודה החדשה search_vector:

    CREATE INDEX idx_docs_rum
    ON documents
    USING rum (search_vector rum_tsvector_ops);
    
  5. שולחים שאילתה לטבלה באמצעות האינדקס. האופרטור <=> מחשב את ציון הרלוונטיות או המרחק בין המסמך לבין השאילתה ישירות מהאינדקס, וכך מאפשר מיון מהיר:

    SELECT title, content
    FROM documents
    WHERE search_vector @@ to_tsquery('english', 'search <-> engine')
    ORDER BY search_vector <=> to_tsquery('english', 'search <-> engine');
    
  6. מילוי טבלת documents בנתונים נוספים:

    INSERT INTO documents (title, content) VALUES ('Title1', 'English is my primary language.');
    INSERT INTO documents (title, content) VALUES ('Title2', 'Google has a great engineering culture');
    
  7. מריצים שאילתת חיפוש של קידומת. כך מוצאים מסמכים שמכילים מילים שמתחילות ב-eng, כמו engineer או english:

    SELECT title, content
    FROM documents
    WHERE search_vector @@ to_tsquery('english', 'eng:*');
    

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

  1. יוצרים את אינדקס ה-RUM באמצעות מחלקת האופרטורים של הגיבוב:

    CREATE INDEX idx_docs_rum_hash
    ON documents
    USING rum (search_vector rum_tsvector_hash_ops);
    
  2. מילוי טבלת documents בנתונים נוספים:

    INSERT INTO documents (title, content) VALUES ('Title3', 'That person was driving incredibly fast, however the routing was not very efficient');
    
  3. הפעלת שאילתת התאמה רגילה:

    SELECT * FROM documents WHERE search_vector @@ to_tsquery('english', 'fast & efficient');
    
    

אינדקס לחיפוש ממוין לפי חותמת זמן

משתמשים במחלקת האופרטורים rum_tsvector_addon_ops כדי לבצע אופטימיזציה של שאילתות שמסננות לפי טקסט וממיינות לפי שדה אחר, כמו חותמת זמן. בדפוס הזה, הערך של השדה הנוסף מאוחסן ישירות באינדקס, וכך נמנעת פעולת מיון איטית אחרי החיפוש. בדוגמה הבאה מניחים שיש לכם טבלה בשם documents עם עמודה search_vector ועמודה published_at.

  1. יוצרים אינדקס שכולל את חותמת הזמן published_at:

    CREATE INDEX idx_docs_rum_timestamp
    ON documents
    USING rum (search_vector rum_tsvector_addon_ops, published_at)
    WITH (attach = 'published_at', to = 'search_vector');
    
  2. מריצים שאילתה שמחפשת מסמכים שמכילים את המילה engine וממיינת אותם לפי תאריך הוצאה לאור. האינדקס מטפל ביעילות גם בחיפוש וגם במיון:

    SELECT title, published_at
    FROM documents
    WHERE search_vector @@ to_tsquery('english', 'engine')
    ORDER BY published_at DESC;
    

אפשר להשתמש במחלקת האופרטורים rum_anyarray_ops כדי ליצור אינדקס לעמודות של מערכים, כמו רשימה של תגים. כך אפשר לבצע שאילתות יעילות לגבי מערכים שחופפים (&&), מכילים (@>) או מוכלים על ידי (<@) מערכים אחרים. בדוגמה הבאה מוסיפים עמודה tags לטבלה documents.

  1. מוסיפים עמודה tags ומאכלסים אותה בנתונים:

    ALTER TABLE documents 
    ADD COLUMN tags TEXT[];
    
    INSERT INTO documents (title, content, tags) VALUES ( 'Title4', 'Sample Text', ARRAY['ai', 'ml'] );
    
  2. יוצרים את אינדקס ה-RUM בעמודה TEXT[] בשם tags:

    CREATE INDEX idx_tags_rum
    ON documents
    USING rum (tags rum_anyarray_ops);
    
  3. מריצים שאילתה כדי למצוא מסמכים שיש להם את התגים ai או ml:

    SELECT * FROM documents WHERE tags && '{"ai", "ml"}';
    

אינדקס של סוגים סקלריים

משתמשים במחלקות האופרטורים rum_<TYPE>_ops כדי ליצור אינדקס לעמודות שמכילות ערכים רציפים, כמו מספרים שלמים, חותמות זמן או מספרים עם נקודה צפה (floating-point). בעזרת המחלקות האלה של אופרטורים אפשר להשתמש באופרטור <=> כדי לחשב ביעילות את המרחק בין ערכים. בדוגמה הבאה אנחנו מניחים שיש לכם טבלה בשם documents.

  1. מוסיפים עמודה כללית של מספרים שלמים, כמו rating, לטבלה documents:

    ALTER TABLE documents
    ADD COLUMN rating INT;
    
    UPDATE documents 
    SET rating = floor(random() * 5 + 1);
    
  2. יוצרים אינדקס RUM בעמודה rating:

    CREATE INDEX idx_rating_rum
    ON documents
    USING rum (rating rum_int4_ops);
    
  3. מריצים שאילתה כדי למצוא מסמכים עם rating הכי קרוב לערך 5:

    SELECT title, rating
    FROM documents
    ORDER BY rating <=> 5;
    

אינדקס לחיפוש אופטימלי של גיבובים, ממוין לפי חותמת זמן

משתמשים במחלקת האופרטורים rum_tsvector_hash_addon_ops כדי לשלב את היתרונות של אינדקס גיבוב עם יכולות המיון של אינדקס תוסף. במחלקת הנתונים הזו מאוחסן גיבוב של כל לקסמה, יחד עם הערך של עמודה נוספת. ההגדרה הזו תומכת במיון יעיל לפי העמודה הנוספת, אבל לא תומכת בהתאמה של קידומת. בדוגמה הבאה מניחים שיש לכם טבלה בשם documents עם עמודה search_vector ועמודת חותמת זמן published_at.

  1. יוצרים אינדקס RUM שמשתמש במחלקת אופרטור הגיבוב וכולל את published_at חותמת הזמן:

    CREATE INDEX idx_docs_rum_hash_timestamp
    ON documents
    USING rum (search_vector rum_tsvector_hash_addon_ops, published_at)
    WITH (attach = 'published_at', to = 'search_vector');
    
  2. מריצים שאילתה שמוצאת מסמכים שמכילים את engine וממיינים אותם לפי תאריך הוצאה לאור:

    SELECT title, published_at
    FROM documents
    WHERE search_vector @@ to_tsquery('english', 'engine')
    ORDER BY published_at DESC;
    

אינדקס לשאילתות מאוחסנות

משתמשים במחלקת האופרטורים rum_tsquery_ops כדי ליצור אינדקס לערכים של tsquery. כך אפשר לבצע 'חיפוש הפוך' ולזהות אילו שאילתות מאוחסנות תואמות למסמך קלט נתון. בדוגמה הבאה נוצרת טבלה בשם queries.

  1. יוצרים טבלה לאחסון שאילתות:

    CREATE TABLE queries (
    query_text tsquery
    );
    INSERT INTO queries (query_text) VALUES (plainto_tsquery('AlloyDB is fast!'));
    
  2. יוצרים אינדקס RUM בעמודה query_text:

    CREATE INDEX idx_queries_rum
    ON queries
    USING rum (query_text rum_tsquery_ops);
    
  3. מריצים שאילתה כדי למצוא שאילתות שמורות שתואמות למסמך:

    SELECT *
    FROM queries
    WHERE to_tsvector('english', 'AlloyDB is fast') @@ query_text;
    

אינדקס לחיפוש במערך, ממוין לפי חותמת זמן

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

  1. יוצרים אינדקס RUM בעמודה tags שכוללת את חותמת הזמן published_at:

    CREATE INDEX idx_tags_rum_timestamp
    ON documents
    USING rum (tags rum_anyarray_addon_ops, published_at)
    WITH (attach = 'published_at', to = 'tags');
    
  2. מריצים שאילתה כדי למצוא מסמכים עם התג ai, ממוינים לפי תאריך הוצאה לאור:

    SELECT title, published_at
    FROM documents
    WHERE tags @> '{"ai"}'
    ORDER BY published_at DESC;
    

המאמרים הבאים