實體屬性參考資料

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

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

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

屬性和值類型

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

支援的值類型如下:

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

  1. 空值
  2. 定點數
    • 整數
    • 日期和時間
  3. 布林值
  4. 位元組序列
    • Unicode 字串
    • Blobstore 索引鍵
  5. 浮點數
  6. 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 鍵
對應舊版資料庫 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 中的空間,讓 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

用於驗證並且可能還會強制轉換該值的選用函式。

系統會使用引數 (propvalue) 呼叫這個函式,且函式應傳回 (可能經過強制轉換的) 值或引發例外狀況。對強制轉換的值再次呼叫函式時,不應進一步修改該值。 (舉例來說,傳回 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 模組本身定義的基礎類別之間的繼承關係不同)。

注意:App Engine 時鐘時間一律以世界標準時間 (UTC) 表示。如果您使用目前日期或時間 (datetime.datetime.now()) 做為值,或在日期時間物件與 POSIX 時間戳記或時間元組之間轉換,這項資訊就非常重要。不過,Datastore 不會儲存明確的時區資訊,因此只要謹慎使用,您就能利用這些資訊表示任何時區的當地時間 (如果您使用目前時間或轉換)。

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

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

這些選項無法與 repeated=True 搭配使用。這兩個屬性都預設為 False;如果兩者都設為 True,則 auto_now 的效力優先。您可以覆寫具有 auto_now_add=True 的屬性值,但無法覆寫具有 auto_now=True 的屬性值。系統會在實體寫入後才產生自動值,也就是說,這些選項不會提供動態預設值。(這些詳細資料與舊版 db API 不同)。

注意:如果交易在寫入具備 auto_now_add=True 的屬性時失敗並在之後重試,該交易會使用與初次嘗試時相同的時間值,而不會更新為重試的時間。如果交易永久失敗,系統仍會在實體的記憶體內副本中設定屬性的值。

結構化屬性

您可以建構模型的屬性。 舉例來說,您可以定義包含地址清單的模型類別 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 程式碼的行為會相同,但資料儲存庫只會看到每個地址的不透明 Blob。範例中建立的 guido 實體會儲存如下: name = 'Guido' address = <opaque blob for {'type': 'home', 'city': 'Amsterdam'}> address = <opaque blob for {'type': 'work', 'city': 'SF', 'street': 'Spear St'}>

這個實體可正確讀回。這類型的屬性一律不會建立索引,因此您無法查詢地址值。

注意:具有巢狀結構屬性的 StructuredProperty (無論是否為結構化屬性) 僅支援單層的重複屬性。StructuredProperty可以重複,或巢狀屬性可以重複,但兩者不能同時重複。解決方法是使用 LocalStructuredProperty,這個方法沒有這項限制 (但無法查詢屬性值)。

運算屬性

「運算屬性」 (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'

注意:如果應用程式查詢運算值,請使用 ComputedProperty。如果只想在 Python 程式碼中使用衍生版本,請定義一般方法或使用 Python 的 @property 內建函式。

注意: 如果您不是手動指定金鑰來建立模型,而是依賴 Datastore 自動產生實體的 ID,則在第一個 put() 中,ComputedProperty 將無法讀取 ID 欄位,因為需要先運算欄位才會產生 ID。 如要使用實體 ID 的 ComputedProperty,可以利用 allocate_ids 方法產生 ID 和金鑰,藉此建立實體,這樣 ComputedProperty 就能在實體的第一個 put() 中參照該 ID。

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 模型則可動態定義屬性。