brief: | i18n.site תומך כעת בחיפוש טקסט מלא ללא שרת.
מאמר זה מציג את הטמעת טכנולוגיית חיפוש טקסט מלא חזיתית, כולל אינדקס הפוך שנבנה על ידי IndexedDB, חיפוש קידומת, אופטימיזציה של פילוח מילים ותמיכה בריבוי שפות.
בהשוואה לפתרונות קיימים, החיפוש הטהור של i18n.site בטקסט מלא הוא קטן בגודלו ומהיר, מתאים לאתרי אינטרנט קטנים ובינוניים כגון מסמכים ובלוגים, וזמין במצב לא מקוון.
לאחר מספר שבועות של פיתוח, i18n.site (כלי לבניית & markdown רב לשוני גרידא) תומך בחיפוש טהור בטקסט מלא.
מאמר זה ישתף את ההטמעה הטכנית של i18n.site
חיפוש בטקסט מלא. בקר i18n.site
קוד קוד : חפש ממשק / אינטראקטיבי
עבור אתרים קטנים ובינוניים סטטיים גרידא כגון מסמכים/בלוגים אישיים, בניית קצה עורפי לחיפוש בטקסט מלא בנוי בעצמו היא כבדה מדי, וחיפוש טקסט מלא ללא שירות הוא הבחירה הנפוצה יותר.
פתרונות חיפוש בטקסט מלא ללא שרת מתחלקים לשתי קטגוריות רחבות:
ראשית, ספקי שירותי חיפוש של צד שלישי algolia.com מספקים רכיבי קצה לחיפוש בטקסט מלא.
שירותים כאלה דורשים תשלום על סמך נפח החיפוש, ולעתים קרובות אינם זמינים למשתמשים בסין היבשתית עקב בעיות כגון תאימות לאתרים.
לא ניתן להשתמש בו במצב לא מקוון, לא ניתן להשתמש בו באינטראנט, ויש לו מגבלות גדולות. מאמר זה אינו דן הרבה.
השני הוא חיפוש טקסט מלא בחזית הקצה.
נכון לעכשיו, חיפושי טקסט מלא נפוצים בחזית כוללים lunrjs ו- ElasticLunr.js (מבוסס על lunrjs
פיתוח משני).
lunrjs
ישנן שתי דרכים לבנות אינדקסים, ולשתיהן יש בעיות משלהן.
קבצי אינדקס בנויים מראש
מכיוון שהאינדקס מכיל מילים מכל המסמכים, הוא גדול בגודלו. בכל פעם שמסמך מתווסף או משנה, יש לטעון קובץ אינדקס חדש. זה יגדיל את זמן ההמתנה של המשתמש ויצרוך הרבה רוחב פס.
טען מסמכים ובנה אינדקסים תוך כדי תנועה
בניית אינדקס היא משימה אינטנסיבית מבחינה חישובית בנייה מחדש של האינדקס בכל פעם שאתה ניגש אליו יגרום לפיגורים ברורים וחווית משתמש גרועה.
בנוסף ל lunrjs
, ישנם עוד כמה פתרונות חיפוש בטקסט מלא, כגון :
fusejs חשב את הדמיון בין מחרוזות לחיפוש.
הביצועים של פתרון זה גרועים ביותר ולא ניתן להשתמש בהם לחיפוש בטקסט מלא (ראה Fuse.js שאילתה ארוכה נמשכת יותר מ- 10 שניות, כיצד לייעל אותה? ).
TinySearch השתמש במסנן Bloom לחיפוש, לא ניתן להשתמש בו לחיפוש קידומת (לדוגמה, הזן goo
, חפש good
, google
), ואינו יכול להשיג אפקט השלמה אוטומטי דומה.
בגלל החסרונות של הפתרונות הקיימים, i18n.site
פיתחה פתרון חדש לחיפוש טקסט מלא פרונט-אנד טהור, בעל המאפיינים הבאים :
gzip
הוא 6.9KB
(לשם השוואה, הגודל של lunrjs
הוא 25KB
).indexedb
, שתופס פחות זיכרון ומהיר.להלן, i18n.site
פרטי יישום טכניים יוצגו בפירוט.
פילוח מילים משתמש בפילוח המילים המקורי של הדפדפן Intl.Segmenter
, וכל הדפדפנים המיינסטרים תומכים בממשק זה.
קוד המילה פילוח coffeescript
הוא כדלקמן
SEG = new Intl.Segmenter 0, granularity: "word"
seg = (txt) =>
r = []
for {segment} from SEG.segment(txt)
for i from segment.split('.')
i = i.trim()
if i and !'| `'.includes(i) and !/\p{P}/u.test(i)
r.push i
r
export default seg
export segqy = (q) =>
seg q.toLocaleLowerCase()
ב:
/\p{P}/
הוא ביטוי רגולרי המתאים לסימני פיסוק ספציפיים כוללים: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _
{ | } ~. .</p><ul style=";text-align:right;direction:rtl"><li style=";text-align:right;direction:rtl">
split('.')הוא משום שפילוח מילת דפדפן
Firefoxאינו מחלק
. ` .
5 טבלאות אחסון חפצים נוצרו ב IndexedDB
:
word
: id -doc
: id - מסמך url - מספר גרסת מסמךdocWord
מערך id מסמך id - מילה :prefix
: מערך של קידומת - מילה idrindex
: Word id - Document id : מערך מספרי שורותעברו במערך של מסמך url
וגרסה מספר ver
, וחפשו האם המסמך קיים בטבלה doc
אם הוא לא קיים, צור אינדקס הפוך. במקביל, הסר את האינדקס ההפוך עבור אותם מסמכים שלא הועברו.
בדרך זו ניתן להגיע לאינדקס מצטבר ולצמצם את כמות החישוב.
באינטראקציה קדמית, ניתן להציג את סרגל התקדמות הטעינה של האינדקס / להימנע מהפיגור בעת css בפעם progress + .
הפרויקט idb על בסיס אנקפסולציה אסינכרונית של IndexedDB
הקריאה והכתיבה של IndexedDB הן אסינכרוניות. בעת יצירת אינדקס, מסמכים ייטענו במקביל ליצירת האינדקס.
על מנת למנוע אובדן נתונים חלקי שנגרם על ידי כתיבה תחרותית, אתה יכול להתייחס לקוד coffeescript
למטה ולהוסיף מטמון ing
בין קריאה לכתיבה כדי ליירט כתיבה מתחרה.
pusher = =>
ing = new Map()
(table, id, val)=>
id_set = ing.get(id)
if id_set
id_set.add val
return
id_set = new Set([val])
ing.set id, id_set
pre = await table.get(id)
li = pre?.li or []
loop
to_add = [...id_set]
li.push(...to_add)
await table.put({id,li})
for i from to_add
id_set.delete i
if not id_set.size
ing.delete id
break
return
rindexPush = pusher()
prefixPush = pusher()
החיפוש יפלח תחילה את מילות המפתח שהזין המשתמש.
N-2
N-1
N
מילים אחרי פילוח המילה 1
תוצאות החיפוש המוצגות תחילה מבטיחות את דיוק השאילתה, והתוצאות שנטענו לאחר מכן (לחץ על כפתור הטען עוד) מבטיחות את שיעור ההיזכרות.
על מנת לשפר את מהירות התגובה, החיפוש משתמש במחולל yield
כדי ליישם טעינה לפי דרישה, וחוזר limit
פעם שנשאלת שאילתה לתוצאה.
שים לב שבכל פעם שאתה מחפש שוב אחרי yield
, אתה צריך לפתוח מחדש עסקת שאילתה של IndexedDB
.
על מנת להציג תוצאות חיפוש בזמן שהמשתמש מקליד, למשל, בהזנת wor
, מוצגות מילים עם קידומת wor
כגון words
ו work
.
ליבת החיפוש תשתמש בטבלת prefix
עבור המילה האחרונה לאחר פילוח מילים כדי למצוא את כל המילים עם הקדמת שלה, ולחפש ברצף.
פונקציית אנטי-טלטול debounce
משמשת גם באינטראקציה חזיתית (מיושם באופן הבא) כדי להפחית את תדירות קלט המשתמש המפעיל חיפושים ולהפחית את כמות החישובים.
export default (wait, func) => {
var timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(func.bind(this, ...args), wait);
};
}
טבלת האינדקס אינה מאחסנת את הטקסט המקורי, אלא רק את המילים, מה שמפחית את כמות האחסון.
הדגשת תוצאות החיפוש מחייבת טעינה מחדש של הטקסט המקורי, והתאמה service worker
יכולה למנוע בקשות רשת חוזרות ונשנות.
יחד עם זאת, מכיוון ש service worker
מאחסן את כל המאמרים במטמון, ברגע שהמשתמש מבצע חיפוש, האתר כולו, כולל החיפוש, זמין במצב לא מקוון.
פתרון החיפוש החזיתי הטהור של i18n.site
מותאם ל MarkDown
מסמכים.
בעת הצגת תוצאות החיפוש, שם הפרק יוצג והפרק ינווט בלחיצה.
חיפוש הפוך בטקסט מלא מיושם אך ורק בחזית הקצה, ללא צורך בשרת. הוא מתאים מאוד לאתרים קטנים ובינוניים כמו מסמכים ובלוגים אישיים.
i18n.site
חיפוש חזיתי טהור בפיתוח עצמי של קוד פתוח, קטן בגודלו ותגובה מהירה, פותר את החסרונות של החיפוש הנוכחי בטקסט מלא בחזית הקצה הטהור ומספק חווית משתמש טובה יותר.