סקירה כללית של השאילתה

בדף הזה מתוארת הפונקציה SEARCH ומצב השאילתה המשופר, שמשמשים לביצוע שאילתות של חיפוש טקסט מלא בטבלאות של Spanner.

שאילתה של אינדקס חיפוש

‫Spanner מספק את הפונקציה SEARCH לשימוש בשאילתות של אינדקס חיפוש. דוגמה לתרחיש שימוש: אפליקציה שבה המשתמשים מזינים טקסט בתיבת חיפוש, והאפליקציה שולחת את קלט של משתמשים ישירות אל הפונקציה SEARCH. הפונקציה SEARCH תשתמש באינדקס חיפוש כדי למצוא את הטקסט הזה.

הפונקציה SEARCH דורשת שני ארגומנטים:

  • שם של אינדקס חיפוש
  • שאילתת חיפוש

הפונקציה SEARCH פועלת רק כשמוגדר אינדקס חיפוש. אפשר לשלב את הפונקציה SEARCH עם כל מבנה SQL שרירותי, כמו מסננים, צבירות או שאילתות איחוד (join).

אי אפשר להשתמש בפונקציה SEARCH בשאילתות של עסקאות.

השאילתה הבאה משתמשת בפונקציה SEARCH כדי להחזיר את כל האלבומים שכוללים את friday או monday בשם:

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, 'friday OR monday')

PostgreSQL

בדוגמה הזו נעשה שימוש ב-spanner.search.

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'friday OR monday')

שאילתת חיפוש

שאילתות החיפוש משתמשות בתחביר של שאילתת החיפוש הגולמית כברירת מחדל. אפשר לציין תחביר חלופי באמצעות הארגומנט SEARCH dialect.

ניב rquery

ניב ברירת המחדל הוא שאילתת חיפוש גולמית. ‫Spanner משתמש בשפה ספציפית לדומיין (DSL) שנקראת rquery.

השפה של rquery פועלת לפי אותם כללים כמו מפצל המילים של טקסט פשוט כשמפצלים את שאילתת החיפוש לקבוצות של מילים. הוא כולל פילוח של שפות אסייתיות.

מידע על השימוש ב-rquery זמין במאמר בנושא תחביר rquery.

מילים בניב

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

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

מידע על השימוש במילה dialect מופיע במאמר תחביר של מילים.

words_phrase dialect

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

בדומה ל-rquery, הניב words_phrase פועל לפי אותם כללים כמו מנתח הטוקנים של טקסט פשוט כשמפצלים את שאילתת החיפוש של הקלט למונחים.

מידע על השימוש בניב words_phrase זמין במאמר תחביר של words phrase.

מצב שאילתה משופר

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

כדי להפעיל את האפשרות הזו, מגדירים את הארגומנט האופציונלי enhance_query=>true בפונקציה SEARCH. לדוגמה, שאילתת החיפוש hotl cal תואמת לאלבום Hotel California.

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, 'hotl cal', enhance_query=>true)

PostgreSQL

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'hotl cal', enhance_query=>true)

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

‫Google כל הזמן משפרת את האלגוריתמים של שיפור השאילתות. כתוצאה מכך, שאילתה עם enhance_query == true עשויה להניב תוצאות שונות מעט לאורך זמן.

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

דרישות לגבי שאילתות SQL

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

השאילתות צריכות לעמוד בתנאים הבאים:

  • הפונקציות SEARCH ו-SEARCH_SUBSTRING דורשות אינדקס חיפוש. ‫Spanner לא תומך בפונקציות האלה בשאילתות שמופעלות מול טבלת הבסיס או אינדקסים משניים.

  • אינדקסים עם חלוקה למחיצות צריכים לכלול את כל עמודות החלוקה למחיצות שמוגבלות על ידי תנאי שוויון בסעיף WHERE של השאילתה.

    לדוגמה, אם אינדקס החיפוש מוגדר כ-PARTITION BY x, y, השאילתה חייבת לכלול צירוף בלשון WHERE של x = <parameter or constant> AND y = <parameter or constant>. אם תנאי כזה חסר, האופטימיזציה של השאילתה לא מתבססת על אינדקס החיפוש הזה.

  • כל העמודות TOKENLIST שאליהן מתייחסים האופרטורים SEARCH ו-SEARCH_SUBSTRING חייבות להיות באינדקס באותו אינדקס חיפוש.

    לדוגמה, נניח שיש לכם את הטבלה הבאה ואת הגדרת האינדקס:

    GoogleSQL

    CREATE TABLE Albums (
        AlbumId STRING(MAX) NOT NULL,
        AlbumTitle STRING(MAX),
        AlbumStudio STRING(MAX),
        AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
        AlbumStudio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumStudio)) HIDDEN
    ) PRIMARY KEY(AlbumId);
    
    CREATE SEARCH INDEX AlbumsTitleIndex ON Albums(AlbumTitle_Tokens);
    CREATE SEARCH INDEX AlbumsStudioIndex ON Albums(AlbumStudio_Tokens);
    

    PostgreSQL

    CREATE TABLE albums (
        albumid character varying NOT NULL,
        albumtitle character varying,
        albumstudio character varying,
        albumtitle_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumtitle)) VIRTUAL HIDDEN,
        albumstudio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumstudio)) VIRTUAL HIDDEN,
    PRIMARY KEY(albumid));
    
    CREATE SEARCH INDEX albumstitleindex ON albums(albumtitle_tokens);
    CREATE SEARCH INDEX albumsstudioindex ON albums(albumstudio_tokens);
    

    השאילתה הבאה נכשלת כי אין אינדקס חיפוש יחיד שמכיל את AlbumTitle_Tokens וגם את AlbumStudio_Tokens:

    GoogleSQL

    SELECT AlbumId
    FROM Albums
    WHERE SEARCH(AlbumTitle_Tokens, @p1)
        AND SEARCH(AlbumStudio_Tokens, @p2)
    

    PostgreSQL

    בדוגמה הזו נעשה שימוש בפרמטרים של השאילתה $1 ו-$2 שמשויכים לערכים fast car ו-blue note, בהתאמה.

    SELECT albumid
    FROM albums
    WHERE spanner.search(albumtitle_tokens, $1)
        AND spanner.search(albumstudio_tokens, $2)
    
  • אם העמודה של סדר המיון יכולה להכיל ערך NULL, גם הסכימה וגם השאילתה צריכות להחריג שורות שבהן העמודה של סדר המיון היא NULL. פרטים נוספים מופיעים במאמר בנושא סדר המיון של אינדקס החיפוש.

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

  • Search indexes וsearch functions לא נתמכים ב-DML, ב-partitioned DML או ב-partitioned queries.

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

לא מומלץ להשתמש באינדקסים של חיפוש ובפונקציות חיפוש בעסקאות של קריאה וכתיבה. במהלך ההרצה, שאילתות חיפוש נועלות מחיצה שלמה של אינדקס. כתוצאה מכך, שיעור גבוה של שאילתות חיפוש בעסקאות קריאה-כתיבה עלול לגרום להתנגשויות נעילה שיובילו לעליות פתאומיות בזמן האחזור. כברירת מחדל, אינדקסים של חיפוש לא נבחרים אוטומטית בעסקאות של קריאה וכתיבה. אם שאילתה נאלצת להשתמש באינדקס חיפוש בעסקת קריאה-כתיבה, היא נכשלת כברירת מחדל. הבדיקה תיכשל גם אם השאילתה מכילה אחת מפונקציות החיפוש. אפשר לבטל את ההתנהגות הזו באמצעות רמז ברמת ההצהרה של GoogleSQL‏ (@{ALLOW_SEARCH_INDEXES_IN_TRANSACTION=TRUE}), אבל עדיין יש סיכון להתנגשויות נעילה בשאילתות.

אחרי שהתנאים לשימוש באינדקס מתקיימים, הכלי לאופטימיזציה של שאילתות מנסה להאיץ תנאים של שאילתות שאינן טקסטואליות (כמו Rating > 4). אם אינדקס החיפוש לא כולל את העמודה המתאימה TOKENLIST, התנאי לא מואץ ונשאר תנאי שיורי.

פרמטרים של שאילתה

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

בחירת אינדקס

בדרך כלל, Spanner בוחר את האינדקס היעיל ביותר לשאילתה באמצעות מודלים מבוססי-עלות. עם זאת, הרמז FORCE_INDEX מורה במפורש ל-Spanner להשתמש באינדקס חיפוש ספציפי. לדוגמה, בדוגמה הבאה אפשר לראות איך מאלצים את Spanner להשתמש ב-AlbumsIndex:

GoogleSQL

SELECT AlbumId
FROM Albums @{FORCE_INDEX=AlbumsIndex}
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")

PostgreSQL

SELECT albumid
FROM albums/*@force_index=albumsindex*/
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')

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

תקצירים בתוצאות החיפוש

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

לדוגמה, Gmail משתמש בתקצירים כדי לציין את החלק באימייל שתואם לשאילתת החיפוש:

רשימת קטעי טקסט

יש כמה יתרונות ליצירת קטע קוד על ידי מסד הנתונים:

  1. נוחות: לא צריך להטמיע לוגיקה כדי ליצור תקצירים משאילתת חיפוש.
  2. יעילות: קטעי קוד מקטינים את גודל הפלט מהשרת.

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

לדוגמה, הפקודה הבאה משתמשת ב-SNIPPET כדי לאחזר טקסט מ-AlbumTitle:

GoogleSQL

SELECT AlbumId, SNIPPET(AlbumTitle, "Fast Car")
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "Fast Car")

PostgreSQL

בדוגמה הזו נעשה שימוש ב-spanner.snippet.

SELECT albumid, spanner.snippet(albumtitle, 'Fast Car')
FROM albums
WHERE spanner.search(albumtitle_tokens, 'Fast Car')

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