מבוא לחיפוש שינה

1. סקירה כללית

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

2. יסודות חיפוש שינה

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

אם אנו כבר משתמשים ב- Hibernate ו- JPA עבור ORM, אנו נמצאים רק צעד אחד מחיפוש ה- Hibernate Search.

חיפוש שינה (Hibernate Search) משלב את אפאצ'י לוסין (Apache Lucene), ספריית מנוע חיפוש עם ביצועים גבוהים ומרחיבה בטקסט מלא שנכתבה ב- Java. זה משלב את העוצמה של לוסין עם הפשטות של Hibernate ו- JPA.

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

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

3. תצורות

3.1. תלות Maven

לפני שמתחילים, ראשית עלינו להוסיף את התלות הדרושה לנו pom.xml:

 org.hibernate hibernate-search-orm 5.8.2.Final 

לשם הפשטות, נשתמש ב- H2 כמסד הנתונים שלנו:

 com.h2database h2 1.4.196 

3.2. תצורות

עלינו לציין היכן לוסין צריכה לאחסן את האינדקס.

ניתן לעשות זאת דרך מקום האירוח hibernate.search.default.directory_provider.

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

עלינו גם להגדיר ספריית בסיס ברירת מחדל בה יאוחסנו אינדקסים:

hibernate.search.default.directory_provider = מערכת קבצים hibernate.search.default.indexBase = / data / index / default

4. שיעורי הדוגמניות

לאחר התצורה, אנו מוכנים כעת לציין את המודל שלנו.

על גבי ההערות של JPA @יֵשׁוּת ו @שולחן, עלינו להוסיף @Indexed ביאור. זה אומר לחיפוש שינה כי הישות מוצר יהיה באינדקס.

לאחר מכן, עלינו להגדיר את התכונות הנדרשות כניתנות לחיפוש על ידי הוספת a @שדה ביאור:

@Entity @Indexed @Table (שם = "מוצר") מוצר ציבורי {@Id פרטי פרטי מזהה; @Field (termVector = TermVector.YES) פרטי מחרוזת productName; @Field (termVector = TermVector.YES) תיאור מחרוזת פרטי; @Field זיכרון פרטי פרטי; // גטרים, סטרים ובונים}

ה termVector = TermVector.YES מאפיין יידרש לשאילתת "עוד כמו זה" בהמשך.

5. בניית מדד לוסין

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

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager (entityManager); fullTextEntityManager.createIndexer (). startAndWait ();

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

הערה: עלינו לוודא כי ישויות מחויבות במלואן למסד הנתונים לפני שניתן לגלות ולהוסיף אותן לאינדקס על ידי לוסין (אגב, זו גם הסיבה לכך שייבוא ​​נתוני הבדיקה הראשוניים במקרי בדיקת הקוד לדוגמא שלנו מגיע במקרה מבחן ייעודי של JUnit, עם הערה @לְבַצֵעַ).

6. בנייה וביצוע שאילתות

כעת אנו מוכנים ליצור את השאילתה הראשונה שלנו.

בחלק הבא, נראה את זרימת העבודה הכללית להכנה ולביצוע שאילתה.

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

6.1. זרימת עבודה כללית ליצירה וביצוע שאילתה

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

בשלב 1 עלינו להשיג JPA FullTextEntityManager ומתוך כך א QueryBuilder:

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager (entityManager); QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory () .buildQueryBuilder (). ForEntity (Product.class) .get ();

בשלב 2, ניצור שאילתת Lucene באמצעות שאילתת ה- DSL של Hibernate:

org.apache.lucene.search.Query query = queryBuilder .keyword () .onField ("productName") .matching ("iphone") .createQuery ();

בשלב 3, נעטוף את שאילתת לוסן לשאילתת שינה:

org.hibernate.search.jpa.FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery (שאילתה, Product.class);

לבסוף, בשלב 4 נבצע את השאילתה:

תוצאות רשימה = jpaQuery.getResultList ();

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

שלבים 1, 3 ו -4 זהים לכל סוגי השאילתות.

בהמשך, נתמקד בשלב 2, א. ה. כיצד ליצור סוגים שונים של שאילתות.

6.2. שאילתות מילות מפתח

מקרה השימוש הבסיסי ביותר הוא חיפוש מילה ספציפית.

זה מה שעשינו למעשה כבר בסעיף הקודם:

מילת שאילתה Query = queryBuilder .keyword () .onField ("productName") .matching ("iphone") .createQuery ();

פה, מילת מפתח () מציין שאנחנו מחפשים מילה ספציפית אחת, onField () אומר ללוסין לאן להסתכל תוֹאֵם() מה לחפש.

6.3. שאילתות מטושטשות

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

על ידי withEditDistanceUpTo (), אנו יכולים להגדיר עד כמה מונח עשוי לחרוג מהאחר. ניתן להגדיר אותו ל 0, 1 ו -2, לפיו ערך ברירת המחדל הוא 2 (הערה: מגבלה זו נובעת מהיישום של לוסין).

על ידי withPrefixLength (), אנו יכולים להגדיר את אורך הקידומת אשר יתעלם מהמטושטש:

Query fuzzyQuery = queryBuilder .keyword () .fuzzy () .withEditDistanceUpTo (2) .withPrefixLength (0) .onField ("productName") .matching ("iPhaen") .createQuery ();

6.4. שאילתות עם תווים כלליים

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

לשם כך אנו יכולים להשתמש ב"?” לדמות אחת, ו"*” לכל רצף תווים:

Query wildcardQuery = queryBuilder .keyword (). Wildcard () .onField ("productName") .matching ("Z *") .createQuery ();

6.5. שאילתות ביטוי

אם אנו רוצים לחפש יותר ממילה אחת, אנו יכולים להשתמש בשאילתות ביטוי. אנחנו יכולים להסתכל למשפטים מדויקים או למשפטים משוערים, באמצעות מִשׁפָּט() ו withSlop (), אם נחוץ. גורם הסלופ מגדיר את מספר המילים האחרות המותרות במשפט:

Query phraseQuery = queryBuilder .phrase () .withSlop (1) .onField ("תיאור"). Sentence ("עם טעינה אלחוטית") .createQuery ();

6.6. שאילתות מחרוזת פשוטות

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

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

סוגי השאילתות הבאים נתמכים:

  • בוליאני (AND באמצעות "+", או באמצעות "|", לא באמצעות "-")
  • קידומת (קידומת *)
  • ביטוי ("איזה ביטוי")
  • עדיפות (באמצעות סוגריים)
  • מטושטש (מטושטש ~ 2)
  • ליד אופרטור לשאילתות ביטוי ("ביטוי כלשהו" ~ 3)

הדוגמה הבאה תשלב שאילתות מטושטשות, ביטויים ובוליאניים:

שאילתה simpleQueryStringQuery = queryBuilder .simpleQueryString () .onFields ("productName", "תיאור") .matching ("Aple ~ 2 + \" iPhone X \ "+ (256 | 128)") .createQuery ();

6.7. שאילתות טווח

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

טווח שאילתות Query = queryBuilder .range () .onField ("זיכרון"). מ (64) .to (256) .createQuery ();

6.8. שאילתות דומות יותר

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

כאמור, termVector = TermVector.YES התכונה במחלקת המודל שלנו נדרשת במקרה זה: היא אומרת ללוסין לאחסן את התדירות לכל מונח במהלך האינדקס.

על סמך זה, הדמיון יחושב בזמן ביצוע השאילתה:

שאילתה moreLikeThisQuery = queryBuilder .moreLikeThis () .comparingField ("productName"). BoostedTo (10f) .andField ("תיאור"). BoostedTo (1f) .toEntity (ישות) .createQuery (); תוצאות רשימה = (רשימה) fullTextEntityManager .createFullTextQuery (moreLikeThisQuery, Product.class) .setProjection (ProjectionConstants.THIS, ProjectionConstants.SCORE) .getResultList ();

6.9. חיפוש יותר משדה אחד

עד עכשיו, יצרנו רק שאילתות לחיפוש תכונה אחת, באמצעות onField ().

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

שאילתה luceneQuery = queryBuilder .keyword () .onFields ("productName", "תיאור") .matching (טקסט) .createQuery ();

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

שאילתה moreLikeThisQuery = queryBuilder .moreLikeThis () .comparingField ("productName"). BoostedTo (10f) .andField ("תיאור"). BoostedTo (1f) .toEntity (ישות) .createQuery ();

6.10. שילוב שאילתות

לבסוף, חיפוש שינה תומך גם בשילוב שאילתות תוך שימוש באסטרטגיות שונות:

  • צריך: השאילתה צריכה להכיל את האלמנטים התואמים של שאילתת המשנה
  • צריך: השאילתה חייבת להכיל את האלמנטים התואמים של שאילתת המשנה
  • אסור: השאילתה לא יכולה להכיל את האלמנטים התואמים של שאילתת המשנה

הצבירות הן דומה לבוליאניים AND, OR ו לֹא. עם זאת, השמות שונים כדי להדגיש כי יש להם גם השפעה על הרלוונטיות.

לדוגמא, א צריך בין שתי שאילתות דומה לבוליאני אוֹ: אם לאחת משתי השאילתות יש התאמה, התאמה זו תוחזר.

עם זאת, אם שתי השאילתות תואמות, ההתאמה תהיה רלוונטית גבוהה יותר בהשוואה לשאילתה אחת בלבד:

Query combinedQuery = queryBuilder .bool () .must (queryBuilder.keyword () .onField ("productName"). התאמה ("תפוח") .createQuery ()) .must (queryBuilder.range () .onField ("זיכרון") . מ (64). עד (256) .createQuery ()). צריך (queryBuilder.phrase (). onField ("תיאור"). משפט ("מזהה פנים") .createQuery ()) .must (queryBuilder.keyword ( ). onField ("productName"). התאמה ("samsung") .createQuery ()) .not () .createQuery ();

7. מסקנה

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

כמו תמיד, קוד המקור המלא של הדוגמאות זמין באתר GitHub.