שאילתת Couchbase עם MapReduce Views

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

במדריך זה, נציג כמה תצוגות פשוטות של MapReduce ונדגים כיצד לשאול אותם באמצעות Couchbase Java SDK.

2. תלות של Maven

לעבודה עם Couchbase בפרויקט Maven, ייבא את ה- SDK של Couchbase אל ה- pom.xml:

 com.couchbase.client java-client 2.4.0 

תוכל למצוא את הגרסה האחרונה ב- Maven Central.

3. תצוגות MapReduce

ב- Couchbase, תצוגת MapReduce היא סוג של אינדקס שניתן להשתמש בו לשאילתת דלי נתונים. זה מוגדר באמצעות JavaScript מַפָּה פונקציה ואופציונלי לְהַפחִית פוּנקצִיָה.

3.1. ה מַפָּה פוּנקצִיָה

ה מַפָּה הפונקציה מופעלת כנגד כל מסמך פעם אחת. כאשר הנוף נוצר, ה- מַפָּה הפונקציה מופעלת פעם אחת נגד כל מסמך בדלי, והתוצאות נשמרות בדלי.

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

בגלל ה מַפָּה תוצאות הפונקציה מאוחסנות בדלי הנתונים, שאילתות מול נוף מציגות זמן אחזור נמוך.

בואו נסתכל על דוגמא של מַפָּה פונקציה שיוצרת אינדקס על שֵׁם שדה של כל המסמכים בדלי אשר סוּג שדה שווה ל- "StudentGrade":

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.name) {emit (doc.name, null); }}

ה לִפְלוֹט הפונקציה מספרת ל- Couchbase איזה שדות נתונים לאחסן במפתח האינדקס (פרמטר ראשון) ואיזה ערך (פרמטר שני) לשייך למסמך האינדקס.

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

כאשר Couchbase מעבד את התצוגה, הוא יוצר אינדקס של המקשים הנפלטים על ידי ה- מַפָּה פונקציה, תוך שיוך כל מפתח לכל המסמכים שבגינם נפלט מפתח זה.

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

3.2. ה לְהַפחִית פוּנקצִיָה

ה לְהַפחִית פונקציה משמשת לביצוע חישובים מצטברים באמצעות תוצאות a מַפָּה פוּנקצִיָה. ממשק המשתמש של Couchbase Admin מספק דרך קלה ליישם את המובנה לְהַפחִית פונקציות "_ Count", "_sum", ו “_ סטטיסטיקות”, לשלך מַפָּה פוּנקצִיָה.

אתה יכול גם לכתוב בעצמך לְהַפחִית פונקציות לצבירות מורכבות יותר. נראה דוגמאות לשימוש במובנה לְהַפחִית מתפקד בהמשך במדריך.

4. עבודה עם צפיות ושאילתות

4.1. ארגון הצפיות

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

כאשר אתה יוצר תצוגה ראשונה בתוך מסמך עיצוב, Couchbase מייעד אותו כ- התפתחות נוף. אתה יכול להפעיל שאילתות כנגד a התפתחות להציג כדי לבדוק את הפונקציונליות שלו. ברגע שאתה מרוצה מהנוף, היית עושה זאת לְפַרְסֵם את מסמך העיצוב, והנוף הופך ל- הפקה נוף.

4.2. בניית שאילתות

על מנת לבנות שאילתה מול תצוגת Couchbase, עליך לספק את שם מסמך העיצוב ושם התצוגה שלה כדי ליצור ViewQuery לְהִתְנַגֵד:

שאילתת ViewQuery = ViewQuery.from ("שם מסמך העיצוב", "שם התצוגה");

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

כדי לבנות שאילתה על רקע תצוגת פיתוח, באפשרותך להחיל את ה- התפתחות() שיטה בעת יצירת השאילתה:

שאילתת ViewQuery = ViewQuery.from ("שם עיצוב דוק", "שם תצוגה"). פיתוח ();

4.3. ביצוע השאילתה

ברגע שיש לנו ViewQuery אובייקט, אנו יכולים לבצע את השאילתה כדי להשיג ViewResult:

תוצאת ViewResult = bucket.query (שאילתה);

4.4. עיבוד תוצאות שאילתות

ועכשיו כשיש לנו ViewResult, נוכל לחזור על השורות כדי לקבל את מזהי המסמך ו / או התוכן:

עבור (שורה שורה ViewRow: result.allRows ()) {JsonDocument doc = row.document (); מחרוזת id = doc.id (); מחרוזת json = doc.content (). ToString (); }

5. יישום לדוגמא

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

{"type": "StudentGrade", "name": "John Doe", "course": "History", "hours": 3, "grade": 95}

נאחסן את המסמכים הללו בסעיף "baeldung-tutorial"דלי וכל התצוגות במסמך עיצוב בשם"סטודנטים ציונים. ” בואו נסתכל על הקוד הדרוש לפתיחת הדלי בכדי שנוכל לשאול אותו:

דלי דלי = CouchbaseCluster.create ("127.0.0.1") .openBucket ("מדריך baeldung");

6. שאילתות התאמה מדויקות

נניח שתרצה למצוא את כל ציוני הסטודנטים לקורס מסוים או לקבוצת קורסים מסוימת. בוא נכתוב תצוגה שנקראת “findByCourse"באמצעות הדברים הבאים מַפָּה פוּנקצִיָה:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.grade) {emit (doc.course, null); }}

שים לב שבתצוגה פשוטה זו, אנו צריכים רק לפלוט את ה- קוּרס שדה.

6.1. התאמה על מפתח יחיד

כדי למצוא את כל הציונים לקורס היסטוריה, אנו מיישמים את מַפְתֵחַ שיטה לשאילתת הבסיס שלנו:

שאילתת ViewQuery = ViewQuery.from ("studentGrades", "findByCourse"). מפתח ("היסטוריה");

6.2. התאמה במספר מקשים

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

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "findByCourse"). מקשים (JsonArray.from ("מתמטיקה", "מדע"));

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

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

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

7.1. שאילתות הכוללות שדה יחיד

כדי למצוא את כל המסמכים עם מגוון רחב של כיתה ערכים ללא קשר לערך קוּרס שדה, אנו זקוקים לתצוגה הפולטת רק את כיתה שדה. בוא נכתוב את מַפָּה פונקציה עבור "findByGrade" נוף:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.grade) {emit (doc.grade, null); }}

בוא נכתוב שאילתה ב- Java באמצעות תצוגה זו כדי למצוא את כל הציונים המקבילים לציון אות "B" (כולל 80 עד 89):

שאילתת ViewQuery = ViewQuery.from ("studentGrades", "findByGrade") .startKey (80). EndKey (89) .inclusiveEnd (true);

שים לב שערך מפתח ההתחלה בשאילתת טווח מתייחס תמיד ככולל.

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

שאילתת ViewQuery = ViewQuery.from ("studentGrades", "findByGrade"). StartKey (80). EndKey (90) .inclusiveEnd (false);

כדי למצוא את כל ציוני "A" (90 ומעלה), עלינו רק לציין את הגבול התחתון:

ViewQuery query = ViewQuery .from ("studentGrades", "findByGrade") .startKey (90);

וכדי למצוא את כל הציונים הנכשלים (מתחת ל -60), עלינו רק לציין את הגבול העליון:

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "findByGrade") .endKey (60) .inclusiveEnd (false);

7.2. שאילתות הכוללות מספר שדות

נניח שברצוננו למצוא את כל התלמידים בקורס ספציפי שכיתתו נופלת לטווח מסוים. שאילתה זו מחייבת תצוגה חדשה המפיצה את ה- קוּרס ו כיתה שדות.

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

בואו נסתכל על מַפָּה פונקציה לתצוגה "findByCourseAndGrade“:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.grade) {emit ([doc.course, doc.grade], null); }}

כאשר תצוגה זו מאוכלסת ב- Couchbase, ערכי האינדקס ממוינים לפי קוּרס ו כיתה. הנה קבוצת משנה של מקשים בסימן "findByCourseAndGradeתצוגה המוצגת בסדר המיון הטבעי שלהם:

["היסטוריה", 80] ["היסטוריה", 90] ["היסטוריה", 94] ["מתמטיקה", 82] ["מתמטיקה", 88] ["מתמטיקה", 97] ["מדע", 78] [ "מדע", 86] ["מדע", 92]

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

המשמעות היא שכדי למצוא את כל התלמידים שקיבלו ציון "B" (80 עד 89) בקורס מתמטיקה, היית מגדיר את הגבול התחתון ל:

["" מתמטיקה, 80]

והגבול העליון ל:

["" מתמטיקה ", 89]

בואו נכתוב את שאילתת הטווח ב- Java:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade"). StartKey (JsonArray.from ("Math", 80)). EndKey (JsonArray.from ("Math", 89)) .inclusiveEnd (true);

אם נרצה למצוא עבור כל התלמידים שקיבלו ציון "A" (90 ומעלה) במתמטיקה, היינו כותבים:

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .startKey (JsonArray.from ("מתמטיקה", 90)). EndKey (JsonArray.from ("מתמטיקה", 100));

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

ולמצוא את כל ציוני המתמטיקה הכושלים (מתחת ל 60):

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .startKey (JsonArray.from ("Math", 0)). EndKey (JsonArray.from ("Math", 60)) .inclusiveEnd (false);

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

לבסוף, כדי למצוא את חמש הציונים הגבוהים ביותר במתמטיקה (חסום קשרים כלשהם), אתה יכול להורות ל- Couchbase לבצע מיון יורד ולהגביל את גודל מערך התוצאות:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade"). Descending () .startKey (JsonArray.from ("Math", 100)). EndKey (JsonArray.from ("Math", 0)) .inclusiveEnd ( נכון () נכון (5);

שים לב שכאשר מבצעים מיון יורד, ה- startKey ו endKey הערכים הפוכים מכיוון ש- Couchbase מחיל את המיון לפני שהוא מחיל את ה- לְהַגבִּיל.

8. שאילתות מצטברות

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

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

בואו לבנות תצוגה ושאילתה לכל אחד מחישובים אלה באמצעות מובנה לְהַפחִית פונקציות.

8.1. משתמש ב לספור() פוּנקצִיָה

ראשית, בוא נכתוב את מַפָּה פונקציה לתצוגה לספור מספר התלמידים בכל קורס:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.name) {emit ([doc.course, doc.name], null); }}

נקרא לתצוגה זו "countStudentsByCourse”וקבעו שזה יהיה שימוש במובנה "_לספור" פוּנקצִיָה. ומכיוון שאנחנו מבצעים רק ספירה פשוטה, אנחנו עדיין יכולים לפלוט ריק כערך עבור כל ערך.

כדי לספור את מספר התלמידים בכל קורס:

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "countStudentsByCourse") .reduce () .groupLevel (1);

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

בואו נריץ את השאילתה ונחלץ את הספירות ל- a java.util.Map:

תוצאת ViewResult = bucket.query (שאילתה); מפה numStudentsByCourse = HashMap חדש (); עבור (שורת ViewRow: result.allRows ()) {JsonArray keyArray = (JsonArray) row.key (); קורס מחרוזת = keyArray.getString (0); ספירה ארוכה = Long.valueOf (row.value (). toString ()); numStudentsByCourse.put (קורס, ספירה); }

8.2. משתמש ב סְכוּם() פוּנקצִיָה

לאחר מכן, בוא נכתוב תצוגה המחשבת את סכום שעות הזיכוי של כל תלמיד. נקרא לתצוגה זו "sumHoursByStudent”וקבעו שזה יהיה שימוש במובנה "_סְכוּם" פוּנקצִיָה:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.course && doc.hours) {emit ([doc.name, doc.course], doc.hours); }}

שים לב כי בעת החלת ה- "_סְכוּם" לתפקד, אנחנו חייבים לִפְלוֹט הערך שיש לסכם - במקרה זה, מספר הזיכויים - עבור כל ערך.

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

שאילתת ViewQuery = ViewQuery .from ("studentGrades", "sumCreditsByStudent") .reduce () .groupLevel (1);

ועכשיו, בואו נריץ את השאילתה ונחלץ את הסכומים המצטברים ל- a java.util.Map:

תוצאת ViewResult = bucket.query (שאילתה); מפה שעותByStudent = HashMap חדש (); עבור (שורת ViewRow: result.allRows ()) {שם מחרוזת = (מחרוזת) row.key (); סכום ארוך = Long.valueOf (row.value (). toString ()); hoursByStudent.put (שם, סכום); }

8.3. חישוב ממוצעי ציון

נניח שאנחנו רוצים לחשב את ממוצע הציונים של כל סטודנט (GPA) על פני כל הקורסים, תוך שימוש בסולם הציונים המקובל על סמך הציונים שהושגו ומספר שעות הזיכוי שהקורס שווה (A = 4 נקודות לשעת אשראי, B = 3 נקודות לשעת אשראי, C = 2 נקודות לשעת אשראי, ו- D = נקודה לשעת אשראי).

אין מובנה לְהַפחִית פונקציה לחישוב ערכים ממוצעים, כך שנשלב את הפלט משתי תצוגות לחישוב ה- GPA.

יש לנו כבר את “SumHoursByStudent” תצוגה המסכמת את מספר שעות האשראי שכל סטודנט ניסה. כעת אנו זקוקים למספר הנקודות הכולל של כל תלמיד.

בואו ליצור תצוגה שנקראת “SumGradePointsByStudent” המחשב את מספר נקודות הציון שנצברו עבור כל קורס שנלמד. נשתמש במובנה "_סְכוּם" פונקציה להפחתת הדברים הבאים מַפָּה פוּנקצִיָה:

פונקציה (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.hours && doc.grade) {if (doc.grade> = 90) {emit (doc.name, 4 * doc .שעה (ות); } אחר אם (doc.grade> = 80) {emit (doc.name, 3 * doc.hours); } אחר אם (doc.grade> = 70) {emit (doc.name, 2 * doc.hours); } אחרת אם (doc.grade> = 60) {emit (doc.name, doc.hours); } אחר {emit (doc.name, 0); }}}

עכשיו בואו ונחקור תצוגה זו ונחלץ את הסכומים ל- a java.util.Map:

שאילתת ViewQuery = ViewQuery.from ("studentGrades", "sumGradePointsByStudent") .reduce () .groupLevel (1); תוצאת ViewResult = bucket.query (שאילתה); מפה gradePointsByStudent = HashMap חדש (); עבור (שורת ViewRow: result.allRows ()) {Course String = (String) row.key (); סכום ארוך = Long.valueOf (row.value (). toString ()); gradePointsByStudent.put (קורס, סכום); }

לסיום, בואו נשלב בין השניים מַפָּהs כדי לחשב את ממוצע הציונים לכל תלמיד:

תוצאת מפה = HashMap חדש (); עבור (Entry creditHoursEntry: hoursByStudent.entrySet ()) {שם מחרוזת = creditHoursEntry.getKey (); totalHours ארוך = creditHoursEntry.getValue (); totalGradePoints ארוך = gradePointsByStudent.get (שם); result.put (שם, ((float) totalGradePoints / totalHours)); }

9. מסקנה

הדגמנו כיצד לכתוב כמה תצוגות בסיסיות של MapReduce ב- Couchbase, וכיצד לבנות ולבצע שאילתות כנגד התצוגות ולחלץ את התוצאות.

את הקוד המוצג במדריך זה ניתן למצוא בפרויקט GitHub.

תוכל ללמוד עוד על תצוגות MapReduce וכיצד לבצע שאילתות עליהן ב- Java באתר התיעוד הרשמי של מפתחי Couchbase.


$config[zx-auto] not found$config[zx-overlay] not found