הפניה למאפיין ישות

‫Firestore במצב Datastore ‏ (Datastore) תומך במגוון סוגי נתונים של ערכי מאפיינים. לדוגמה:

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

רשימה מלאה של הסוגים זמינה במאמר מאפיינים וסוגי ערכים.

מאפיינים וסוגי ערכים

ערכי הנתונים שמשויכים לישות מורכבים ממאפיין אחד או יותר. לכל מאפיין יש שם וערך אחד או יותר. למאפיין יכולים להיות ערכים מכמה סוגים, ולשתי ישויות יכולים להיות ערכים מסוגים שונים לאותו מאפיין. אפשר להוסיף מאפיינים לאינדקס או להסיר אותם מהאינדקס (שאילתות שממיינות או מסננות לפי מאפיין P יתעלמו מישויות שבהן P לא מופיע באינדקס). לישות יכולים להיות לכל היותר 20,000 מאפיינים שעברו אינדוקס.

סוגי הערכים הנתמכים:

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

  1. ערכי Null
  2. מספרים עם נקודה קבועה
    • מספרים שלמים
    • תאריכים ושעות
  3. ערכים בוליאניים
  4. רצפים של בייטים
    • מחרוזת Unicode
    • מפתחות Blobstore
    • מפתחות של Datastore

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

סוגי נכסים

‫NDB תומך בסוגי המאפיינים הבאים:

סוג הנכס תיאור
IntegerProperty מספר שלם עם סימן ב-64 ביט
FloatProperty מספר בשיטת נקודה צפה עם דיוק כפול
BooleanProperty בוליאני
StringProperty מחרוזת Unicode; עד 1,500 בייט, באינדקס
TextProperty מחרוזת Unicode; אורך בלתי מוגבל, לא נוסף לאינדקס
BlobProperty מחרוזת בייטים לא מפוענחת:
אם מגדירים את indexed=True, עד 1,500 בייטים, באינדקס;
אם indexed הוא False (ברירת המחדל), אורך בלתי מוגבל, לא באינדקס.
ארגומנט אופציונלי של מילת מפתח: compressed.
DateTimeProperty תאריך ושעה (ראו מאפייני תאריך ושעה)
DateProperty תאריך (ראו מאפייני תאריך ושעה)
TimeProperty זמן (ראו מאפייני תאריך ושעה)
GeoPtProperty מיקום גיאוגרפי. זהו אובייקט ndb.GeoPt. לאובייקט יש מאפיינים lat ו-lon, שניהם מסוג float. אפשר ליצור אותו עם שני ערכים מספריים כמו ndb.GeoPt(52.37, 4.88) או עם מחרוזת ndb.GeoPt("52.37, 4.88"). (זו למעשה אותה כיתה כמו db.GeoPt)
KeyProperty מפתח במאגר הנתונים
ארגומנט אופציונלי של מילת מפתח: kind=kind, כדי לדרוש שהמפתחות שמוקצים למאפיין הזה תמיד יהיו מהסוג שצוין. יכול להיות מחרוזת או מחלקה משנית של Model.
BlobKeyProperty מפתח Blobstore
מקביל ל-BlobReferenceProperty ב-db API הישן, אבל ערך המאפיין הוא BlobKey ולא BlobInfo. אפשר ליצור BlobInfo ממנו באמצעות BlobInfo(blobkey)
UserProperty אובייקט המשתמש.
StructuredProperty כולל סוג אחד של מודל בתוך סוג אחר, לפי ערך (ראו מאפיינים מובנים)
LocalStructuredProperty בדומה ל-StructuredProperty, אבל הייצוג בדיסק הוא blob אטום ולא מתבצע אינדוקס (ראו מאפיינים מובנים).
ארגומנט אופציונלי של מילת מפתח: compressed.
JsonProperty הערך הוא אובייקט Python (כמו רשימה, מילון או מחרוזת) שאפשר לבצע לו סריאליזציה באמצעות המודול json של Python.‏ Datastore מאחסן את הסריאליזציה של ה-JSON כ-blob. לא מתבצע אינדוקס כברירת מחדל.
ארגומנט מילת מפתח אופציונלי: compressed.
PickleProperty הערך הוא אובייקט Python (כמו רשימה, מילון או מחרוזת) שאפשר לבצע לו סריאליזציה באמצעות פרוטוקול ה-pickle של Python. מאגר הנתונים מאחסן את הסריאליזציה של ה-pickle כ-blob. לא נכלל באינדקס כברירת מחדל.
ארגומנט אופציונלי של מילת מפתח: compressed.
GenericProperty ערך כללי
בשימוש בעיקר במחלקה Expando, אבל אפשר להשתמש בו גם באופן מפורש. הסוג יכול להיות כל אחד מהסוגים הבאים: int, ‏ long, ‏ float, ‏ bool, ‏ str, ‏ unicode, ‏ datetime, ‏ Key, ‏ BlobKey, ‏ GeoPt, ‏ User, ‏ None.
ComputedProperty ערך שמחושב מנכסים אחרים על ידי פונקציה בהגדרת המשתמש (UDF). (ראו נכסים מחושבים).

לחלק מהמאפיינים האלה יש ארגומנט אופציונלי של מילת מפתח, compressed. אם הנכס כולל compressed=True, הנתונים שלו דחוסים באמצעות gzip בדיסק. הוא תופס פחות מקום אבל צריך CPU כדי לבצע קידוד/פענוח בפעולות כתיבה וקריאה.

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

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

אפשרויות הנכס

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

class Employee(ndb.Model):
    full_name = ndb.StringProperty('n')
    retirement_age = ndb.IntegerProperty('r')

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

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

ארגומנט סוג ברירת מחדל תיאור
indexed bool בדרך כלל True הכללת מאפיין באינדקסים של Datastore. אם הערך הוא False, אי אפשר לבצע שאילתות על הערכים, אבל פעולות הכתיבה מהירות יותר. לא כל סוגי המאפיינים תומכים באינדקסים. אם הערך של indexed הוא True, הפעולה תיכשל.
מאפיינים שלא נכללים באינדקסים דורשים פחות פעולות כתיבה מאשר מאפיינים שכן נכללים באינדקסים.
repeated bool False ערך המאפיין הוא רשימת Python שמכילה ערכים מהסוג הבסיסי (ראו מאפיינים חוזרים).
אי אפשר לשלב אותו עם required=True או default=True.
required bool False חובה לציין ערך למאפיין.
default סוג הבסיס של הנכס ללא ערך ברירת המחדל של המאפיין אם לא צוין ערך מפורש.
choices רשימת הערכים של סוג הבסיס None רשימה אופציונלית של ערכים מותרים.
validator תפקיד None פונקציה אופציונלית לאימות הערך ואולי להמרת הערך. הפונקציה תופעל עם הארגומנטים (prop, value), והיא צריכה להחזיר את הערך (אחרי ההמרה, אם בוצעה) או להעלות חריגה. הפעלה חוזרת של הפונקציה על ערך שהומר לא צריכה לשנות את הערך עוד יותר. (לדוגמה, החזרה של value.strip() או value.lower() היא בסדר, אבל לא החזרה של value + '$'). הפונקציה יכולה גם להחזיר None, שפירושו 'ללא שינוי'. ראו גם כתיבת מחלקות משנה של מאפיינים
verbose_name מחרוזת None תווית HTML אופציונלית לשימוש במסגרות של טפסים באינטרנט כמו jinja2.

מאפיינים חוזרים

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

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

בדוגמה הזו נעשה שימוש במאפיין שחוזר על עצמו:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)
...
article = Article(
    title='Python versus Ruby',
    stars=3,
    tags=['python', 'ruby'])
article.put()

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

assert article.title == 'Python versus Ruby'
assert article.stars == 3
assert sorted(article.tags) == sorted(['python', 'ruby'])

כשמבצעים שאילתה לגבי המאפיין tags, הישות הזו תעמוד בדרישות של שאילתה לגבי 'python' או 'ruby'.

כשמעדכנים מאפיין חוזר, אפשר להקצות לו רשימה חדשה או לשנות את הרשימה הקיימת במקום. כשמקצים רשימה חדשה, סוגי הפריטים ברשימה נבדקים באופן מיידי. סוגי פריטים לא תקינים (לדוגמה, הקצאת הערך [1, 2] למאפיין art.tags שמופיע למעלה) מעלים חריגה. כשמשנים את הרשימה, השינוי לא מאומת באופן מיידי. במקום זאת, הערך יאומת כשכותבים את הישות ל-Datastore.

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

מאפייני תאריך ושעה

יש שלושה סוגים של מאפיינים שבהם אפשר לאחסן ערכים שקשורים לתאריך ולשעה:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

הערכים של המשתנים האלה שייכים למחלקות המתאימות (date, ‏ time,‏ datetime) של מודול datetime הסטנדרטי של Python. המשתנה הכללי ביותר מבין השלושה הוא DateTimeProperty, שמציין גם תאריך קלנדרי וגם שעה ביום. המשתנים האחרים שימושיים מדי פעם למטרות מיוחדות שדורשות רק תאריך (כמו תאריך לידה) או רק שעה (כמו שעת פגישה). מסיבות טכניות, DateProperty ו-TimeProperty הם מחלקות משנה של DateTimeProperty, אבל לא כדאי להסתמך על יחסי ההורשה האלה (שימו לב שהם שונים מיחסי ההורשה בין המחלקות הבסיסיות שמוגדרות על ידי מודול datetime עצמו).

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

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

אי אפשר לשלב את האפשרויות האלה עם repeated=True. ברירת המחדל של שניהם היא False. אם שניהם מוגדרים כ-True, המדיניות auto_now מקבלת עדיפות. אפשר לשנות את הערך של מאפיין באמצעות auto_now_add=True, אבל לא באמצעות auto_now=True. הערך האוטומטי לא נוצר עד שהישות נכתבת, כלומר האפשרויות האלה לא מספקות ערכי ברירת מחדל דינמיים. (הפרטים האלה שונים מאלה של ה-API הישן של מסד הנתונים).

מאפיינים מובנים

אפשר ליצור מבנה למאפיינים של מודל. לדוגמה, אפשר להגדיר מחלקה של מודל בשם Contact שמכילה רשימה של כתובות, שלכל אחת מהן יש מבנה פנימי. מאפיינים מובנים (סוג StructuredProperty``) מאפשרים לעשות זאת. לדוגמה:

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()

...

class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)

...

guido = Contact(
    name='Guido',
    addresses=[
        Address(
            type='home',
            city='Amsterdam'),
        Address(
            type='work',
            street='Spear St',
            city='SF')])

guido.put()

כך נוצרת ישות Datastore אחת עם המאפיינים הבאים:

assert guido.name == 'Guido'
addresses = guido.addresses
assert addresses[0].type == 'home'
assert addresses[1].type == 'work'
assert addresses[0].street is None
assert addresses[1].street == 'Spear St'
assert addresses[0].city == 'Amsterdam'
assert addresses[1].city == 'SF'

קריאה חוזרת של ישות כזו משחזרת בדיוק את הישות המקורית Contact. למרות שהמופעים של Address מוגדרים באמצעות אותה תחביר כמו עבור מחלקות מודלים, הם לא ישויות מלאות. אין להם מפתחות משלהם ב-Datastore. אי אפשר לאחזר אותם בנפרד מהיישות Contact שאליה הם שייכים. עם זאת, אפליקציה יכולה לשלוח שאילתה כדי לקבל את הערכים של השדות האישיים שלה. מידע נוסף זמין במאמר בנושא סינון ערכים של מאפיינים מובנים. שימו לב: address.type, address.street ו-address.city נחשבים למערכים מקבילים מנקודת המבט של Datastore, אבל ספריית NDB מסתירה את ההיבט הזה ובונה את הרשימה המתאימה של מופעי Address.

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

אם לא צריך לשלוח שאילתה לגבי מאפיינים פנימיים של מבנה משנה, אפשר להשתמש במאפיין מקומי מובנה (LocalStructuredProperty) במקום זאת. אם מחליפים את StructuredProperty ב-LocalStructuredProperty בדוגמה שלמעלה, ההתנהגות של קוד Python נשארת זהה, אבל Datastore רואה רק blob אטום לכל כתובת. הישות guido שנוצרה בדוגמה תאוחסן באופן הבא:

name = 'Guido'
address = <opaque blob for {'type': 'home', 'city': 'Amsterdam'}>
address = <opaque blob for {'type': 'work', 'city': 'SF',
                              'street': 'Spear St'}>

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

נכסים מחושבים

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

class SomeEntity(ndb.Model):
    name = ndb.StringProperty()
    name_lower = ndb.ComputedProperty(lambda self: self.name.lower())

...

entity = SomeEntity(name='Nick')
entity.put()

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

assert entity.name == 'Nick'
assert entity.name_lower == 'nick'

אם נשנה את השם ל-Nickie ונבקש את הערך של name_lower, הפונקציה תחזיר את הערך nickie:

entity.name = 'Nick'
assert entity.name_lower == 'nick'
entity.name = 'Nickie'
assert entity.name_lower == 'nickie'

מאפייני הודעות של Google Protocol RPC

ספריית Google Protocol RPC משתמשת באובייקטים של Message לנתונים מובְנים. האובייקטים האלה יכולים לייצג בקשות RPC, תגובות או דברים אחרים. ‫NDB מספק API לאחסון אובייקטים של Google Protocol RPC‏ Messageכמאפייני ישות. נניח שאתם מגדירים מחלקת משנה Message:

from protorpc import messages
...
class Note(messages.Message):
    text = messages.StringField(1, required=True)
    when = messages.IntegerField(2)

אפשר לאחסן אובייקטים של Note ב-Datastore כערכי מאפייני ישות באמצעות API של msgprop של NDB.

from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
...
class NoteStore(ndb.Model):
    note = msgprop.MessageProperty(Note, indexed_fields=['when'])
    name = ndb.StringProperty()
...
my_note = Note(text='Excellent note', when=50)

ns = NoteStore(note=my_note, name='excellent')
key = ns.put()

new_notes = NoteStore.query(NoteStore.note.when >= 10).fetch()

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

MessageProperty תומך בהרבה אפשרויות של נכסים, אבל לא בכולן. הוא תומך ב:

  • name
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

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

אפשר גם להשתמש בהודעות מוטמעות (באמצעות MessageField):

class Notebook(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)
...
class SignedStorableNotebook(ndb.Model):
    author = ndb.StringProperty()
    nb = msgprop.MessageProperty(
        Notebook, indexed_fields=['notes.text', 'notes.when'])

MessageProperty כולל אפשרות מאפיין מיוחדת, protocol, שמציינת איך אובייקט ההודעה עובר סריאליזציה ל-Datastore. הערכים הם שמות פרוטוקולים כמו אלה שמשמשים את המחלקה protorpc.remote.Protocols. שמות הפרוטוקולים הנתמכים הם protobuf ו-protojson. ברירת המחדל היא protobuf.

בנוסף, msgprop מגדיר את EnumProperty, סוג מאפיין שאפשר להשתמש בו כדי לאחסן ערך protorpc.messages.Enum בישות. דוגמה:

class Color(messages.Enum):
    RED = 620
    GREEN = 495
    BLUE = 450
...
class Part(ndb.Model):
    name = ndb.StringProperty()
    color = msgprop.EnumProperty(Color, required=True)
...
p1 = Part(name='foo', color=Color.RED)
print p1.color  # prints "RED"

הערך של EnumProperty מאוחסן כמספר שלם. למעשה, EnumProperty הוא מחלקת משנה של IntegerProperty. המשמעות היא שאפשר לשנות את השם של ערכי ה-enum בלי לשנות ישויות שכבר אוחסנו, אבל אי אפשר לשנות את המספר שלהם.

המאפיין EnumProperty תומך באפשרויות המאפיין הבאות:

  • name
  • indexed
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

מידע על מודלים של ישויות ב-NDB

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

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


from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

כאן, username, userid ו-email הם מאפיינים של Account.

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

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

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