אפליקציה יכולה להשתמש בשאילתות כדי לחפש ב-Datastore ישויות שתואמות לקריטריונים ספציפיים לחיפוש, שנקראים מסננים.
סקירה כללית
אפליקציה יכולה להשתמש בשאילתות כדי לחפש ב-Datastore ישויות שתואמות לקריטריונים ספציפיים לחיפוש, שנקראים מסננים. לדוגמה, אפליקציה שעוקבת אחרי כמה ספרי אורחים יכולה להשתמש בשאילתה כדי לאחזר הודעות מספר אורחים אחד, לפי תאריך:
...
חלק מהשאילתות מורכבות יותר מאחרות, ולכן מאגר הנתונים צריך אינדקסים שנבנו מראש.
האינדקסים המוגדרים מראש האלה מצוינים בקובץ תצורה, index.yaml.
בשרת הפיתוח, אם מריצים שאילתה שצריכה אינדקס שלא ציינתם, שרת הפיתוח מוסיף אותו באופן אוטומטי לקובץ index.yaml שלו.
אבל באתר שלכם, שאילתה שצריכה אינדקס שעדיין לא צוין נכשלת.
לכן, מחזור הפיתוח הטיפוסי הוא לנסות שאילתה חדשה בשרת הפיתוח, ואז לעדכן את האתר כך שישתמש בקובץ index.yaml שהשתנה באופן אוטומטי.
אפשר לעדכן את index.yaml בנפרד מהעלאת האפליקציה על ידי הפעלת gcloud app deploy index.yaml.
אם במאגר הנתונים יש הרבה ישויות, לוקח הרבה זמן ליצור להן אינדקס חדש. במקרה כזה, כדאי לעדכן את הגדרות האינדקס לפני שמעלים קוד שמשתמש באינדקס החדש.
אפשר להשתמש במסוף הניהול כדי לברר מתי הסתיימה בניית האינדקסים.
ב-App Engine Datastore יש תמיכה מובנית במסננים להתאמות מדויקות (האופרטור ==) ולהשוואות (האופרטורים <, <=, > ו->=).
אפשר לשלב כמה מסננים באמצעות פעולה בוליאנית AND, עם כמה מגבלות (ראו בהמשך).
בנוסף לאופרטורים המקוריים, ה-API תומך באופרטור !=, שמשלב קבוצות של מסננים באמצעות הפעולה הבוליאנית OR, ובפעולה IN, שבודקת שוויון לאחד מהערכים האפשריים ברשימה (כמו האופרטור in ב-Python).
הפעולות האלה לא ממופות ביחס של 1:1 לפעולות המקוריות של Datastore, ולכן הן קצת מוזרות ואיטיות יחסית.
הן מיושמות באמצעות מיזוג בזיכרון של זרמי תוצאות. שימו לב שהערך p != v מיושם בתור "p < v OR p > v".
(זה חשוב לגבי מאפיינים חוזרים).
מגבלות: Datastore אוכף כמה הגבלות על שאילתות. הפרה של הכללים האלה תגרום להחרגות. לדוגמה, כרגע אסור לשלב יותר מדי מסננים, להשתמש באי-שוויונים בכמה מאפיינים או לשלב אי-שוויון עם סדר מיון במאפיין אחר. בנוסף, לפעמים צריך להגדיר אינדקסים משניים למסננים שמפנים לכמה מאפיינים.
לא נתמך: Datastore לא תומך ישירות בהתאמות של מחרוזות משנה, בהתאמות לא תלויות-רישיות או בחיפוש טקסט מלא. יש דרכים להטמיע התאמות לא תלויות-רישיות ואפילו חיפוש טקסט מלא באמצעות מאפיינים מחושבים.
סינון לפי ערכי נכסים
נזכיר את המחלקה Account ממאפייני NDB:
בדרך כלל לא רוצים לאחזר את כל הישויות מסוג מסוים, אלא רק את אלה שיש להן ערך ספציפי או טווח ערכים ספציפי במאפיין מסוים.
אובייקטים של מאפיינים מעמיסים על אופרטורים מסוימים להחזיר ביטויי סינון שניתן להשתמש בהם כדי לשלוט בשאילתה: לדוגמה, כדי למצוא את כל ישויות החשבון שהמאפיין userid שלהן הוא בדיוק 42, אפשר להשתמש בביטוי
(אם אתם בטוחים שהיה רק Account אחד
עם userid, יכול להיות שעדיף להשתמש ב-userid
כמפתח.
Account.get_by_id(...)
מהיר יותר מ-Account.query(...).get().)
NDB תומך בפעולות הבאות:
property == value
property < value
property <= value
property > value
property >= value
property != value
property.IN([value1, value2])
כדי לסנן לפי אי-שוויון, אפשר להשתמש בתחביר כמו זה:
הפונקציה הזו מוצאת את כל ישויות החשבון שהמאפיין userid שלהן גדול מ-40 או שווה לו.
שתיים מהפעולות האלה, != ו-IN, מיושמות כשילובים של הפעולות האחרות, והן קצת מוזרות, כפי שמתואר בקטע != ו-IN.
אפשר לציין כמה מסננים:
הפעולה הזו משלבת את הארגומנטים שצוינו במסנן, ומחזירה את כל הישויות של Account שערך ה-userid שלהן גדול מ-40 או שווה לו, וקטן מ-50.
הערה: כפי שצוין קודם, Datastore דוחה שאילתות שמשתמשות בסינון אי-שוויון ביותר ממאפיין אחד.
במקום לציין מסנן שאילתה שלם בביטוי אחד, יכול להיות שיהיה לכם נוח יותר לבנות אותו בשלבים. לדוגמה:
query3 מקביל למשתנה query מהדוגמה הקודמת. חשוב לזכור שאובייקטים של שאילתות הם לא ניתנים לשינוי, ולכן הבנייה של query2 לא משפיעה על query1 והבנייה של query3 לא משפיעה על query1 או על query2.
הפעולות != ו-IN
זוכרים את המחלקה Article ממאפייני NDB?
הפעולות != (לא שווה) ו-IN (חברות) מיושמות על ידי שילוב של מסננים אחרים באמצעות הפעולה OR. הראשון,
property != value
מיושם בתור
(property < value) OR (property > value)
לדוגמה,
שווה ל-
הערה:
אולי זה מפתיע, אבל השאילתה הזו לא מחפשת
ישויות של Article שלא כוללות את התג "perl".
במקום זאת, הוא מוצא את כל הישויות עם תג אחד לפחות שלא שווה ל-'perl'.
לדוגמה, הישות הבאה תיכלל בתוצאות, למרות שאחד מהתגים שלה הוא "perl":
אבל הכתובת הזו לא תיכלל:
אין דרך לשלוח שאילתה לגבי ישויות שלא כוללות תג ששווה ל-"perl".
באופן דומה, הפעולה IN
property IN [value1, value2, ...]
שבודק אם ערך מסוים נמצא ברשימה של ערכים אפשריים, מיושם כך:
(property == value1) OR (property == value2) OR ...
לדוגמה,
שווה ל-
הערה:
שאילתות שמשתמשות ב-OR
מסירות כפילויות מהתוצאות: זרם התוצאות לא כולל ישות יותר מפעם אחת, גם אם ישות תואמת לשתי שאילתות משנה או יותר.
שאילתות לגבי מאפיינים חוזרים
הכיתה Article שהוגדרה בקטע הקודם משמשת גם כדוגמה לשאילתה של מאפיינים חוזרים. לדוגמה, מסנן כמו
משתמש בערך יחיד, למרות ש-Article.tags הוא מאפיין חוזר. אי אפשר להשוות בין מאפיינים חוזרים לבין אובייקטים של רשימה (Datastore לא יבין את זה), ומסנן כמו
מבצעת פעולה שונה לחלוטין מחיפוש ישויות מסוג Article שהערך של התגים שלהן הוא הרשימה ['python', 'ruby', 'php']: היא מחפשת ישויות שהערך של tags שלהן (שנחשב לרשימה) מכיל לפחות אחד מהערכים האלה.
שאילתה לערך של None במאפיין חוזר
גורמת להתנהגות לא מוגדרת, אז אל תעשו את זה.
שילוב של פעולות AND ו-OR
אפשר להוסיף פעולות AND ו-OR באופן שרירותי.
לדוגמה:
עם זאת, בגלל אופן ההטמעה של OR, שאילתה מהסוג הזה שהיא מורכבת מדי עלולה להיכשל עם חריגה. כדי לשפר את האבטחה, מומלץ לנרמל את המסננים האלה כך שתהיה (לכל היותר) פעולת OR אחת בחלק העליון של עץ הביטויים, ורמה אחת של פעולות AND מתחתיה.
כדי לבצע את הנורמליזציה הזו, צריך לזכור את כללי הלוגיקה הבוליאנית,
ואת אופן ההטמעה של המסננים != ו-IN:
- מרחיבים את האופרטורים
!=ו-INלצורה הפרימיטיבית שלהם, שבה!=הופך לבדיקה אם המאפיין קטן או גדול מהערך, ו-INהופך לבדיקה אם המאפיין שווה לערך הראשון או לערך השני או...עד לערך האחרון ברשימה. -
ANDעםORבתוכו שווה ערך ל-ORשל כמהANDשמוחלים על האופרנדים המקוריים שלAND, עם אופרנדORיחיד שמוחלף ב-ORהמקורי. לדוגמה,AND(a, b, OR(c, d))שווה ערך ל-OR(AND(a, b, c), AND(a, b, d)) - פעולת
ANDשיש לה אופרנד שהוא בעצמו פעולתANDיכולה לשלב את האופרנדים של פעולת ה-ANDהמוטמעת בפעולת ה-ANDשמקיפה אותה. לדוגמה,AND(a, b, AND(c, d))שווה ערך ל-AND(a, b, c, d) - פעולת
ORשיש לה אופרנד שהוא בעצמו פעולתORיכולה לשלב את האופרנדים של פעולת ה-ORהמוטמעת בתוך פעולת ה-ORשמקיפה אותה. לדוגמה,OR(a, b, OR(c, d))שווה ערך ל-OR(a, b, c, d)
אם נחיל את הטרנספורמציות האלה בשלבים על המסנן לדוגמה, באמצעות סימון פשוט יותר מ-Python, נקבל:
- שימוש בכלל מס' 1 באופרטורים
INו-!=:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', AND(tags == 'php', OR(tags < 'perl', tags > 'perl')))) - שימוש בכלל מס' 2 על
ORהפנימי ביותר שמוטמע בתוךAND:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', OR(AND(tags == 'php', tags < 'perl'), AND(tags == 'php', tags > 'perl')))) - שימוש בכלל מס' 4 על
ORשמוטמע בתוךORאחר:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', AND(tags == 'php', tags < 'perl'), AND(tags == 'php', tags > 'perl'))) - שימוש בכלל מס' 2 על ה-
ORשנותר, שמוטמע בתוךAND:OR(AND(tags == 'python', tags == 'ruby'), AND(tags == 'python', tags == 'jruby'), AND(tags == 'python', AND(tags == 'php', tags < 'perl')), AND(tags == 'python', AND(tags == 'php', tags > 'perl')))
- שימוש בכלל מס' 3 כדי לכווץ את שאר ה-
ANDהמקוננים:OR(AND(tags == 'python', tags == 'ruby'), AND(tags == 'python', tags == 'jruby'), AND(tags == 'python', tags == 'php', tags < 'perl'), AND(tags == 'python', tags == 'php', tags > 'perl'))
זהירות:
במסננים מסוימים, הנורמליזציה הזו עלולה לגרום לפיצוץ קומבינטורי. נניח שיש לנו AND של 3 פסוקיות OR, שבכל אחת מהן יש 2 פסוקיות בסיסיות.
אחרי נרמול, מתקבל OR של 8 AND
פסוקיות עם 3 פסוקיות בסיסיות בכל אחת: כלומר, 6 מונחים הופכים ל-24.
הגדרת סדר המיון
אפשר להשתמש ב-order() כדי לציין את הסדר שבו השאילתה מחזירה את התוצאות. השיטה הזו מקבלת רשימה של ארגומנטים, שכל אחד מהם הוא אובייקט מאפיין (למיון בסדר עולה) או השלילה שלו (למיון בסדר יורד). לדוגמה:
הפעולה הזו מאחזרת את כל הישויות מסוג Greeting, ממוינות בסדר עולה לפי הערך של המאפיין content שלהן.
רצפים של ישויות עוקבות עם אותו מאפיין תוכן ימוינו לפי ערך יורד של המאפיין date שלהן.
אפשר להשתמש בכמה קריאות ל-order() כדי להשיג את אותה תוצאה:
הערה: כשמשלבים מסננים עם order(), Datastore דוחה שילובים מסוימים.
במיוחד כשמשתמשים במסנן אי-שוויון, סדר המיון הראשון (אם יש כזה) חייב לציין את אותו מאפיין כמו המסנן.
בנוסף, לפעמים צריך להגדיר אינדקס משני.
שאילתות לגבי ישויות אם
שאילתות לגבי ישות אם מאפשרות לבצע שאילתות עם מודל עקביות חזק במאגר הנתונים, אבל ישויות עם אותה ישות אם מוגבלות לכתיבה אחת בשנייה. כאן מוצגת השוואה פשוטה של היתרונות והחסרונות והמבנה של שאילתה לגבי ישות אם ושאילתה שלא לגבי ישות אם, באמצעות לקוחות והרכישות המשויכות שלהם במאגר הנתונים.
בדוגמה הבאה, שבה אין ישות אב, יש ישות אחת במאגר הנתונים לכל Customer, וישות אחת במאגר הנתונים לכל Purchase, עם KeyProperty שמצביע על הלקוח.
כדי למצוא את כל הרכישות ששייכות ללקוח, אפשר להשתמש בשאילתה הבאה:
במקרה כזה, מאגר הנתונים מציע תפוקת כתיבה גבוהה, אבל רק מודל עקביות הדרגתי. אם נוספה רכישה חדשה, יכול להיות שיוצגו נתונים לא עדכניים. אפשר למנוע את ההתנהגות הזו באמצעות שאילתות לגבי ישות אם.
ללקוחות ולרכישות עם שאילתות לגבי ישות אם, עדיין יש את אותו מבנה עם שתי ישויות נפרדות. החלק של הלקוח זהה. עם זאת, כשיוצרים רכישות, לא צריך יותר לציין את KeyProperty() עבור רכישות. הסיבה לכך היא שכאשר משתמשים בשאילתות לגבי ישות אם, קוראים למפתח של ישות הלקוח כשיוצרים ישות רכישה.
לכל רכישה יש מפתח, וגם ללקוח יש מפתח משלו. עם זאת, כל מפתח רכישה יכלול את המפתח של customer_entity. חשוב לזכור: הפעולה הזו תוגבל לכתיבה אחת לכל ישות אב בשנייה. הקוד הבא יוצר ישות עם ישות אב:
כדי לשלוח שאילתה על הרכישות של לקוח מסוים, משתמשים בשאילתה הבאה.
מאפייני שאילתה
לאובייקטים של שאילתות יש את מאפייני הנתונים הבאים לקריאה בלבד:
| מאפיין | סוג | ברירת מחדל | תיאור |
|---|---|---|---|
| סיווג | str | None | שם הסוג (בדרך כלל שם המחלקה) |
| אב | Key | None | ישות האם שצוינה בשאילתה |
| מסננים | FilterNode | None | ביטוי סינון |
| הזמנות | Order | None | סדר המיון |
הדפסה של אובייקט שאילתה (או קריאה ל-str() או ל-repr()) יוצרת ייצוג של מחרוזת בפורמט נוח:
סינון לפי ערכי מאפיינים מובְנים
שאילתה יכולה לסנן ישירות לפי ערכי השדות של מאפיינים מובְנים.
לדוגמה, שאילתה לכל אנשי הקשר עם כתובת שהעיר שלה היא 'Amsterdam' תיראה כך:
אם משלבים כמה מסננים כאלה, המסננים עשויים להתאים לישויות משנה שונות של Address באותה ישות של איש קשר.
לדוגמה:
יכול להיות שיוצגו אנשי קשר עם כתובת שהעיר שלה היא 'Amsterdam' וכתובת אחרת (שונה) שהרחוב שלה הוא 'Spear St'. עם זאת, לפחות עבור מסנני שוויון, אפשר ליצור שאילתה שמחזירה רק תוצאות עם כמה ערכים בישות משנה אחת:
אם משתמשים בשיטה הזו, המאפיינים של ישות המשנה ששווים ל-None לא נכללים בשאילתה.
אם למאפיין יש ערך ברירת מחדל, צריך להגדיר אותו באופן מפורש ל-None כדי להתעלם ממנו בשאילתה. אחרת, השאילתה תכלול מסנן שדורש שערך המאפיין יהיה שווה לערך ברירת המחדל.
לדוגמה, אם למודל Address היה מאפיין country עם default='us', הדוגמה שלמעלה הייתה מחזירה רק אנשי קשר שהמדינה שלהם היא 'us'. כדי להביא בחשבון אנשי קשר עם ערכי מדינה אחרים, צריך לסנן לפי Address(city='San Francisco', street='Spear St',
country=None).
אם לישות משנה יש ערכי מאפיינים ששווים ל-None, המערכת מתעלמת מהם. לכן, אין טעם לסנן לפי ערך מאפיין של ישות משנה של None.
שימוש במאפיינים שנקראים לפי מחרוזת
לפעמים רוצים לסנן או לסדר שאילתה על סמך מאפיין שהשם שלו מצוין באמצעות מחרוזת. לדוגמה, אם מאפשרים למשתמש להזין שאילתות חיפוש כמו tags:python, יהיה נוח להפוך אותן איכשהו לשאילתה כמו
Article.query(Article."tags" == "python") # does NOT work
אם המודל הוא Expando, אפשר להשתמש במסנן ב-GenericProperty, המחלקה שבה Expando משתמשת למאפיינים דינמיים:
אפשר להשתמש ב-GenericProperty גם אם המודל הוא לא Expando, אבל אם רוצים להבטיח שמשתמשים רק בשמות מאפיינים מוגדרים, אפשר להשתמש גם במאפיין המחלקה _properties
או להשתמש ב-getattr() כדי לקבל אותו מהכיתה:
ההבדל הוא שב-getattr() נעשה שימוש ב-"Python name" של המאפיין, וב-_properties האינדקס הוא לפי ה-"datastore name" של המאפיין. הם שונים רק כשהנכס הוגדר עם משהו כמו
כאן השם ב-Python הוא title אבל השם במאגר הנתונים הוא t.
הגישות האלה פועלות גם להזמנת תוצאות של שאילתות:
איטרטורים של שאילתות
בזמן שהשאילתה מתבצעת, המצב שלה נשמר באובייקט איטרטור. (רוב האפליקציות לא ישתמשו בהם ישירות. בדרך כלל קל יותר לקרוא ל-fetch(20) מאשר לשנות את אובייקט האיטרטור).
יש שתי דרכים בסיסיות לקבל אובייקט כזה:
- באמצעות הפונקציה המובנית
iter()של Python באובייקטQuery - קריאה ל-method
iter()של האובייקטQuery
הראשונה תומכת בשימוש בלולאת for של Python (שקוראת באופן משתמע לפונקציה iter()) כדי ליצור לולאה על שאילתה.
הדרך השנייה, באמצעות ה-method iter() של האובייקט Query, מאפשרת להעביר אפשרויות לאיטרטור כדי להשפיע על ההתנהגות שלו. לדוגמה, כדי להשתמש בשאילתת מפתחות בלבד בלולאת for, אפשר לכתוב את הקוד הבא:
לאיטרטורים של שאילתות יש שיטות שימושיות אחרות:
| Method | תיאור |
|---|---|
__iter__()
| חלק מפרוטוקול האיטרטור של Python. |
next()
| הפונקציה מחזירה את התוצאה הבאה או מקפיצה את החריגה StopIteration אם אין תוצאה. |
has_next()
| מחזירה True אם קריאה עוקבת ל-next() תחזיר תוצאה, ו-False אם היא תעלה את StopIteration.הפונקציה חוסמת עד שהתשובה לשאלה הזו ידועה, ומאחסנת את התוצאה במאגר (אם יש כזו) עד שמאחזרים אותה באמצעות next().
|
probably_has_next()
| כמו has_next(), אבל משתמש בקיצור דרך מהיר יותר
(ולפעמים לא מדויק).יכול להיות שהפונקציה תחזיר תוצאה חיובית כוזבת ( True כש-next() למעשה יחזיר StopIteration), אבל אף פעם לא תחזיר תוצאה שלילית כוזבת (False כש-next() למעשה יחזיר תוצאה).
|
cursor_before()
| הפונקציה מחזירה סמן שאילתות (query cursor) שמייצג נקודה ממש לפני התוצאה האחרונה שהתקבלה. אם אין סמן זמין, תתקבל חריגה (במיוחד אם לא הועברה אפשרות השאילתה produce_cursors).
|
cursor_after()
| הפונקציה מחזירה סמן שאילתות שמייצג נקודה מיד אחרי התוצאה האחרונה שהתקבלה. אם אין סמן זמין, תופיע חריגה (במיוחד אם אפשרות השאילתה produce_cursors לא הועברה).
|
index_list()
| הפונקציה מחזירה רשימה של אינדקסים שבהם השתמשה שאילתה שהופעלה, כולל אינדקסים ראשיים, מורכבים, מסוג kind ושל מאפיין יחיד. |
סמני שאילתות (query cursor)
סמן שאילתות (query cursor) הוא מבנה נתונים קטן ואטום שמייצג נקודת המשך בשאילתה. האפשרות הזו שימושית כדי להציג למשתמש דף תוצאות אחד בכל פעם, וגם כדי לטפל במשימות ארוכות שאולי צריך לעצור ולהמשיך.
דרך נפוצה להשתמש בהם היא באמצעות השיטה fetch_page() של שאילתה.
היא פועלת באופן דומה ל-fetch(), אבל מחזירה שלשה (results, cursor, more).
הדגל more שמוחזר מציין שכנראה יש עוד תוצאות. אפשר להשתמש בו בממשק המשתמש, למשל כדי להסתיר כפתור או קישור של "הדף הבא".
כדי לבקש דפים נוספים, מעבירים את הסמן שמוחזר על ידי קריאה אחת של fetch_page() לקריאה הבאה. אם מעבירים סמן לא חוקי, נוצרת השגיאה BadArgumentError. שימו לב שהאימות רק בודק אם הערך מקודד ב-base64. תצטרכו לבצע כל אימות נוסף שנדרש.
כדי לאפשר למשתמש לראות את כל הישויות שתואמות לשאילתה, ולאחזר אותן דף אחד בכל פעם, הקוד יכול להיראות כך:
...
שימו לב לשימוש ב-urlsafe() וב-Cursor(urlsafe=s) כדי לבצע סריאליזציה ודה-סריאליזציה של הסמן.
כך אפשר להעביר סמן ללקוח באינטרנט בתגובה לבקשה אחת, ולקבל אותו בחזרה מהלקוח בבקשה מאוחרת יותר.
הערה:
השיטה fetch_page() בדרך כלל מחזירה סמן גם אם אין יותר תוצאות, אבל זה לא מובטח: ערך הסמן שמוחזר יכול להיות None. חשוב גם לזכור שהדגל more מוטמע באמצעות השיטה probably_has_next() של האיטרטור, ולכן במקרים נדירים הוא עשוי להחזיר את הערך True גם אם הדף הבא ריק.
חלק מהשאילתות ב-NDB לא תומכות בסמני שאילתות, אבל אפשר לתקן אותן.
אם בשאילתה נעשה שימוש ב-IN, ב-OR או ב-!=, תוצאות השאילתה לא יפעלו עם סמנים אלא אם הן מסודרות לפי מפתח.
אם אפליקציה לא מסדרת את התוצאות לפי מפתח וקוראת ל-fetch_page(), היא מקבלת BadArgumentError.
אם מופיעה שגיאה ב-
User.query(User.name.IN(['Joe', 'Jane'])).order(User.name).fetch_page(N)
, צריך לשנות אותה ל-
User.query(User.name.IN(['Joe', 'Jane'])).order(User.name, User.key).fetch_page(N)
במקום "לדפדף" בתוצאות השאילתה, אתם יכולים להשתמש ב-method iter() של השאילתה כדי להציב סמן בנקודה מדויקת.
כדי לעשות את זה, מעבירים את produce_cursors=True אל iter().
כשהאיטרטור נמצא במקום הנכון, מפעילים את cursor_after()
כדי לקבל סמן שנמצא מיד אחריו. (או, באופן דומה, קוראים ל-cursor_before() כדי לקבל סמן ממש לפני).
חשוב לזכור שקריאה ל-cursor_after() או ל-cursor_before() עשויה לבצע קריאה חוסמת ל-Datastore, ולהריץ מחדש חלק מהשאילתה כדי לחלץ סמן שמצביע לאמצע של אצווה.
כדי להשתמש בסמן כדי לעבור אחורה בין דפים של תוצאות שאילתה, צריך ליצור שאילתה הפוכה:
קריאה לפונקציה לכל ישות ("מיפוי")
נניח שאתם צריכים לקבל את הישויות מסוג Account שתואמות לישויות מסוג Message שהוחזרו על ידי שאילתה.
אפשר לכתוב משהו כמו:
אבל זה לא יעיל: צריך לחכות לאחזור של ישות, להשתמש בה, לחכות לישות הבאה, להשתמש בה וכן הלאה. יש הרבה זמן המתנה. דרך נוספת היא לכתוב פונקציית קריאה חוזרת (callback) שממופה על תוצאות השאילתה:
הגרסה הזו תפעל קצת יותר מהר מהלולאה הפשוטה של for שלמעלה, כי אפשר להפעיל כמה תהליכים בו-זמנית.
עם זאת, מכיוון שהקריאה ל-get() ב-callback() עדיין סינכרונית, השיפור לא עצום.
זה מקום טוב להשתמש בבקשות GET אסינכרוניות.
GQL
GQL היא שפה דמוית-SQL לאחזור ישויות או מפתחות מ-App Engine Datastore. למרות שהתכונות של GQL שונות מאלה של שפת שאילתות למסד נתונים רלציוני מסורתי, התחביר של GQL דומה לזה של SQL. התחביר של GQL מתואר בחומר העזר בנושא GQL.
אתם יכולים להשתמש ב-GQL כדי ליצור שאילתות. הפעולה הזו דומה ליצירת שאילתה באמצעות Model.query(), אבל משתמשת בתחביר GQL כדי להגדיר את המסנן והסדר של השאילתה. כדי להשתמש בו:
ndb.gql(querystring)מחזירה אובייקטQuery(מאותו סוג שמוחזר על ידיModel.query()). כל השיטות הרגילות זמינות באובייקטים כאלה שלQuery:fetch(),map_async(),filter(), וכו'.-
Model.gql(querystring)הוא קיצור שלndb.gql("SELECT * FROM Model " + querystring). בדרך כלל, querystring הוא משהו כמו"WHERE prop1 > 0 AND prop2 = TRUE". - כדי לשלוח שאילתות למודלים שמכילים מאפיינים מובְנים, אפשר להשתמש ב-
foo.barבתחביר GQL כדי להפנות למאפייני משנה. - GQL תומכת בקישורי פרמטרים דמויי SQL. אפליקציה יכולה להגדיר שאילתה ואז לקשור אליה ערכים:
או
קריאה לפונקציה
bind()של שאילתה מחזירה שאילתה חדשה, היא לא משנה את השאילתה המקורית. - אם מחלקת המודל שלכם מבטלת את השיטה
_get_kind()של המחלקה, שאילתת ה-GQL צריכה להשתמש בסוג שהפונקציה הזו מחזירה, ולא בשם המחלקה. - אם מאפיין במודל שלכם משנה את השם שלו (למשל,
foo = StringProperty('bar')), שאילתת ה-GQL צריכה להשתמש בשם המאפיין ששונה (בדוגמה,bar).
אם חלק מהערכים בשאילתה הם משתנים שסופקו על ידי המשתמש, תמיד צריך להשתמש בתכונה של קישור פרמטרים. כך אפשר למנוע התקפות שמבוססות על פריצות תחביריות.
אם תשלחו שאילתה למודל שלא ייבאתם (או, באופן כללי יותר, שלא הגדרתם), תופיע שגיאה.
אם משתמשים בשם מאפיין שלא מוגדר על ידי מחלקת המודל, תופיע שגיאה, אלא אם המודל הוא Expando.
אם מציינים מגבלה או היסט ב-fetch() של השאילתה, הם מבטלים את המגבלה או ההיסט שהוגדרו בסעיפים OFFSET ו-LIMIT של GQL. אל תשלבו את OFFSET ו-LIMIT של GQL עם fetch_page(). שימו לב שהמגבלה של 1,000 תוצאות לכל היותר ש-App Engine מטיל על שאילתות חלה גם על ההיסט וגם על המגבלה.
אם אתם רגילים ל-SQL, היזהרו מהנחות שגויות כשאתם משתמשים ב-GQL. GQL מתורגם ל-API המקורי של NDB לשאילתות. זה שונה ממיפוי אובייקטים-יחסיים (ORM) טיפוסי (כמו SQLAlchemy או תמיכה במסד הנתונים של Django), שבו הקריאות ל-API מתורגמות ל-SQL לפני שהן מועברות לשרת של מסד הנתונים. GQL לא תומכת בשינויים ב-Datastore (הוספות, מחיקות או עדכונים), אלא רק בשאילתות.