實體屬性參考資料

Datastore 模式的 Firestore (Datastore) 支援各種屬性值的資料類型。包括但不限於:

  • 整數
  • 浮點數
  • 字串
  • 日期
  • 二進位檔資料

如需這些類型的完整清單,請參閱 屬性和值類型

屬性和值類型

與實體相關聯的資料值由一或多個「屬性」組成。 每個屬性都有一個名稱及一或多個值。一個屬性可能會有多個類型的值,而兩個實體的相同屬性可能會有不同類型的值。屬性可能已建立索引或未建立索引 (排序或篩選屬性「P」的查詢將忽略「P」未建立索引的實體)。一個實體最多可有 20,000 個已建立索引的屬性。

支援的值類型如下:

當查詢的屬性具有混合類型的值時,Datastore 會根據內部表示法來決定排序:

  1. 空值
  2. 定點數
    • 整數
    • 日期和時間
  3. 布林值
  4. 位元組序列
    • Unicode 字串
    • Blobstore 索引鍵
    • Datastore 鍵

因為 長文字字串和長位元組字串 不會建立索引,因此未定義排序。

屬性類型

NDB 支援以下屬性類型:

物業類型 說明
IntegerProperty 64 位元帶正負號整數
FloatProperty 雙精度浮點數
BooleanProperty 布林值
StringProperty Unicode 字串;最多 1500 個位元組,已建立索引
TextProperty Unicode 字串;長度不限,不會建立索引
BlobProperty 未解譯的位元組字串:
如果您設定 indexed=True,最多 1500 個位元組,已建立索引;
如果 indexedFalse (預設值),長度不限,未建立索引。
選用關鍵字引數:compressed
DateTimeProperty 日期和時間 (請參閱「日期和時間屬性」)
DateProperty 日期 (請參閱「日期和時間屬性」)
TimeProperty 時間 (請參閱「日期和時間屬性」)
GeoPtProperty 地理位置。這是 ndb.GeoPt 物件。這個物件有 latlon 屬性,兩者都是浮點數。您可以兩個浮點數建構此物件,例如 ndb.GeoPt(52.37, 4.88),也可以使用字串 ndb.GeoPt("52.37, 4.88")。(這實際上與 db.GeoPt 是同一個類別)
KeyProperty Datastore 鍵
選用關鍵字引數:kind=kind,要求指派給這個屬性的鍵一律具有指定種類。可以是字串或 Model 子類別。
BlobKeyProperty Blobstore 鍵
對應舊版 DB API 中的 BlobReferenceProperty,但屬性值是 BlobKey,而非 BlobInfo;您可以使用 BlobInfo(blobkey) 從中建構 BlobInfo
UserProperty 使用者物件。
StructuredProperty 在另一個模型中包含一個模型 (依值) (請參閱「結構化屬性」)
LocalStructuredProperty 類似 StructuredProperty,但在磁碟上會顯示為不透明 blob,不會建立索引 (請參閱結構化屬性)。
選用的關鍵字引數:compressed
JsonProperty Value 是可使用 Python 的 json 模組序列化的 Python 物件 (例如清單、字典或字串);Datastore 會將 JSON 序列化內容儲存為 Blob。預設為未建立索引。選用的關鍵字引數:compressed
PickleProperty Value 是可使用 Python 的 Pickle 通訊協定序列化的 Python 物件 (例如清單、字典或字串);Datastore 會將 Pickle 序列化內容儲存為 Blob。預設為未建立索引。選用的關鍵字引數:compressed
GenericProperty 一般值
大多用於 Expando 類別,但也可直接使用。類型可以是 intlongfloatboolstrunicodedatetimeKeyBlobKeyGeoPtUserNone
ComputedProperty 由使用者定義函式從其他屬性計算得出的值。(請參閱「運算屬性」)。

部分屬性具有選用的關鍵字引數 compressed。如果屬性有 compressed=True,表示資料已在磁碟上以 gzip 壓縮。這種格式佔用的空間較少,但寫入和讀取作業需要 CPU 進行編碼/解碼。

壓縮和解壓縮作業都是「延遲」進行;壓縮屬性值只會在您第一次存取時解壓縮。如果在沒有存取壓縮屬性的情況下讀取並寫回包含壓縮屬性值的實體,將不會進行任何解壓縮和壓縮作業。內容快取也會參與這項延遲配置,但 memcache 一律會儲存壓縮屬性的壓縮值。

由於壓縮需要額外的 CPU 時間,因此通常只有在資料過大而無法容納時,才建議使用壓縮屬性。請注意,gzip 壓縮通常不適用於圖片和其他媒體資料,因為這些格式已使用媒體專用的壓縮演算法壓縮 (例如圖片的 JPEG)。

屬性選項

大多數屬性類型都支援一些標準引數。第一個是選用的位置引數,用於指定屬性的資料儲存庫名稱。您可以藉此為 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=Truedefault=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

這些值屬於標準 Python datetime 模組的對應類別 (datetimedatetime)。這三者中最一般的是 DateTimeProperty,表示日曆日期和一天中的時間;其他兩者偶爾可用於僅需日期 (例如出生日期) 或僅需時間 (例如會議時間) 的特殊用途。基於技術原因,DatePropertyTimePropertyDateTimeProperty 的子類別,但不應依附於這個繼承關係 (請注意,這與 datetime 模組本身定義的基礎類別之間的繼承關係不同)。

這些屬性各有兩個額外的布林值關鍵字選項:

選項 說明
auto_now_add 將屬性設為實體建立時的日期/時間。您可以手動覆寫這項屬性。在實體更新時,屬性不會變更。如要執行這項操作,請使用 auto_now
auto_now 在建立實體時,以及每次更新實體時,將屬性設為目前的日期/時間。

這些選項無法與 repeated=True 搭配使用。兩者預設值皆為 False;如果兩者都設為 True,則 auto_now 的效力優先。您可以透過 auto_now_add=True 覆寫屬性的值,但無法透過 auto_now=True 覆寫。系統會在實體寫入後才產生自動值,也就是說,這些選項不會提供動態預設值。(這些詳細資料與舊版 db 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 實體一併擷取。不過,應用程式可以查詢個別欄位的值;請參閱「篩選結構化屬性值」。請注意,從 Datastore 的角度來看,address.typeaddress.streetaddress.city 是平行陣列,但 NDB 程式庫會隱藏這方面,並建構對應的 Address 執行個體清單。

您可以為結構化屬性指定常用的屬性選項 (indexed 除外)。在這種情況下,Datastore 名稱是第二個位置引數 (第一個是定義子結構的模型類別)。

如果不需要查詢子結構的內部屬性,可以改用本機結構化屬性 (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)

您可以使用 NDB 的 msgprop API,將 Note 物件儲存在 Datastore 中做為實體屬性值。

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 類別使用的通訊協定名稱。支援的通訊協定名稱為 protobufprotojson,預設為 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 會將值儲存為整數;事實上,EnumPropertyIntegerProperty 的子類別。這表示您可以重新命名列舉值,不必修改已儲存的實體,但無法重新編號。

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()

其中 usernameuseridemailAccount 的屬性。

還有其他幾種房源類型。有些小工具可方便地顯示日期和時間,並提供自動更新功能。

應用程式可以在屬性上指定選項,藉此調整屬性的行為,例如簡化驗證、設定預設值或變更查詢索引。

模型可以有更複雜的屬性。重複屬性類似清單。 結構化屬性類似物件,唯讀運算屬性是透過函式定義,因此可輕鬆根據一或多個其他屬性定義屬性。Expando 模型則可動態定義屬性。