附註:我們強烈建議建構新應用程式的開發人員使用 NDB 用戶端程式庫,因為 NDB 用戶端程式庫與本用戶端程式庫相較之下有幾個優點,例如能透過 Memcache API 自動將實體加入快取。如果您目前使用的是舊版的 DB 用戶端程式庫,請參閱從 DB 至 NDB 的遷移指南。
Datastore 中的資料物件稱為「實體」。實體有一或多個命名的「屬性」,每個屬性可以有一或多個值。相同種類的實體不需要具有相同屬性,而實體的特定屬性值並不全都需要屬於相同的資料類型 (如有必要,應用程式可在本身的資料模型建立及強制執行這類限制)。
Datastore 支援各種屬性值的資料類型。其中包括:
- 整數
- 浮點數
- 字串
- 日期
- 二進位資料
如需這些類型的完整清單,請參閱屬性和值類型一節。
Datastore 中的每個實體都有專門用來識別該實體的「金鑰」。索引鍵由下列元件組成:
- 實體的「命名空間」,可允許多租戶架構
- 實體的類型,可將實體分類以便進行 Datastore 查詢
- 個別實體的ID,可以是以下兩者之一:
- 「索引鍵名稱」字串
- 整數「數字 ID」
- 選用的祖系路徑,可將實體置於 Datastore 階層之中
應用程式可使用實體的索引鍵從 Datastore 擷取個別實體,或依據實體的索引鍵或屬性值發出查詢,以擷取一或多個實體。
Python App Engine SDK 包含資料模型程式庫,可將 Datastore 實體表示為 Python 類別的例項,並在 Datastore 中儲存及擷取這些例項。
Datastore 本身不會對實體結構強制執行任何限制,例如特定屬性是否具有特定類型的值;這項作業將由應用程式和資料建模程式庫負責。
種類及 ID
每個 Datastore 實體都屬於特定「種類」,可將實體分門別類,方便查詢。舉例來說,人力資源應用程式可以使用屬於 Employee
種類的實體來代表公司的每位員工。在 Python Datastore API 中,實體的種類是由「模型類別」決定,模型類別則是由您在應用程式中,以資料模型程式庫類別 db.Model
定義的子類別決定。模型類別的名稱即成為屬於該類別的實體的種類。以兩條底線 (__
) 開頭的種類名稱均為保留名稱,不得使用。
以下範例示範了如何建立 Employee
類型的實體、填入屬性值,並將實體儲存至 Datastore:
import datetime
from google.appengine.ext import db
class Employee(db.Model):
first_name = db.StringProperty()
last_name = db.StringProperty()
hire_date = db.DateProperty()
attended_hr_training = db.BooleanProperty()
employee = Employee(first_name='Antonio',
last_name='Salieri')
employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True
employee.put()
Employee
類別會為資料模型宣告四個屬性:first_name
、last_name
、hire_date
和 attended_hr_training
。Model
父類別會確保 Employee
物件的屬性與這個模型相符:舉例來說,如果您嘗試為 hire_date
屬性指派字串值,系統會發生執行階段錯誤,因為 hire_date
的資料模型已宣告為 db.DateProperty
。
除了種類以外,建立每個實體時還會指派實體的「ID」。因為 ID 屬於實體索引鍵的一部分,所以會與實體永久關聯,且無法變更。ID 可利用兩種方式指派:
- 您的應用程式可以指定自身的實體「索引鍵名稱」字串。
- 您可以讓 Datastore 自動指派整數的「數字 ID」給實體。
如要指派索引鍵名稱給實體,請在建立實體時,將命名引數 key_name
提供給模型類別建構函式:
# Create an entity with the key Employee:'asalieri'.
employee = Employee(key_name='asalieri')
如要讓 Datastore 自動指派數字 ID,請省略 key_name
引數:
# Create an entity with a key such as Employee:8261.
employee = Employee()
指派 ID
Datastore 可使用兩種不同的自動 ID 政策設定自動產生 ID:
default
政策可產生隨機順序的未使用 ID,近乎均勻分佈。每個 ID 最長可達 16 個十進位數字。legacy
政策可建立一系列非連續的較小整數 ID。
如果您要向使用者顯示實體 ID,並/或依據其順序顯示,最理想的方式就是使用手動分配。
Datastore 會產生順序隨機且近乎均勻分佈的未使用 ID。每個 ID 最長可達 16 個十進位數字。
祖系路徑
Cloud Datastore 中的實體會形成階層結構空間,與檔案系統的目錄結構相似。建立實體時,可選擇將其他實體指定為「父項」;新實體則為父系實體的「子項」(請注意,不同於檔案系統,父項實體不需要實際存在)。沒有父項的實體則是「根實體」。實體與父項實體之間具有永久關聯性,一旦實體建立後就無法變更。Cloud Datastore 絕對不會將相同的數字 ID 指派給父項相同的兩個實體,也不會指派給兩個根實體 (即沒有父項的實體)。
實體的父項、父項的父項等以此類推,全都是這個實體的「祖系」;實體的子項、子項的子項等等,則都是其「子系」。根實體及其所有子系都屬於相同的「實體群組」。從根實體開始,再從父項到子項,最後到指定實體的實體序列,即構成該實體的「祖系路徑」。識別實體的完整索引鍵,包含連串種類-ID 組合序列,其中指定了實體的祖系路徑,最後則以該實體本身的種類-ID 組合做為結尾:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
根實體的祖系路徑是空白路徑,其金鑰只包含實體本身的種類與 ID。
[Person:GreatGrandpa]
本概念以下圖說明:
如要指定實體的父項,請在建立子系實體時,使用模型類別建構函式的 parent
引數。這個引數的值可以是父系實體本身,也可以是父系實體的索引鍵。如要取得索引鍵,請呼叫父系實體的 key()
方法。以下範例會建立 Address
類型的實體,並示範兩種指定 Employee
實體做為父項的方式:
# Create Employee entity
employee = Employee()
employee.put()
# Set Employee as Address entity's parent directly...
address = Address(parent=employee)
# ...or using its key
e_key = employee.key()
address = Address(parent=e_key)
# Save Address entity to datastore
address.put()
交易和實體群組
每次嘗試建立、更新或刪除實體時,都是在交易的背景下進行。單一交易可包含的前述作業數量不受限制。為了維持資料一致性,交易一定會使其中包含的變動整組適用於 Datastore,其中如有任何變動失敗,則整組變動均不適用。此外,在相同交易內執行的所有同步一致性讀取作業 (祖系查詢或「get」) 將出現一致的資料快照。
如前所述,實體群組是一組實體,透過祖系連接至共同根元素。將資料組織為實體群組,可限制執行的交易:
- 一項交易所存取的所有資料,最多只能存在於 25 個實體群組中。
- 如果您想在交易中使用查詢,必須將資料歸納成實體群組,才可指定能夠比對出正確資料的祖系篩選條件。
- 單一實體群組的寫入總處理量大約是每秒一次交易。之所以會有這項限制,原因在於 Datastore 會針對橫跨大範圍地理區域的每個實體執行免主機的同步複製作業,以利發揮高度的可靠性和容錯能力。
在許多應用程式中,當您想取得不相關資料的廣泛檢視畫面時,可以使用最終一致性 (即跨多個實體群組的非祖系查詢,有時可能會傳回稍微過時的資料),然後在查看或編輯一組高度相關資料時,使用同步一致性 (祖系查詢或單一實體的 get
)。在此類應用程式中,針對各組高度相關的資料使用獨立實體群組,通常會有不錯的效果。詳情請參閱「建立同步一致性結構」。
屬性和值類型
與實體相關聯的資料值由一或多個「屬性」組成。每個屬性都有一個名稱及一或多個值。一個屬性可能會有多個類型的值,而兩個實體的相同屬性可能會有不同類型的值。屬性可能已建立索引或未建立索引 (排序或篩選屬性「P」的查詢將忽略「P」未建立索引的實體)。一個實體最多可有 20,000 個已建立索引的屬性。
值的類型 | Python 類型 | 排序順序 | 附註 |
---|---|---|---|
整數 | int long |
數字 | 64 位元整數,帶正負號 |
浮點數 | float |
數字 | 64 位元雙精度, IEEE 754 |
布林值 | bool |
False <True |
|
文字字串 (短) | str unicode |
Unicode ( str 視為 ASCII) |
最多 1500 個位元組 |
文字字串 (長) | db.Text |
無 | 最多 1 MB 未建立索引 |
位元組字串 (短) | db.ByteString |
位元組順序 | 最多 1500 個位元。 |
位元組字串 (長) | db.Blob |
無 | 最多 1 MB 未建立索引 |
日期與時間 | datetime.date datetime.time datetime.datetime |
依時間順序 | |
地理點 | db.GeoPt |
依照緯度、 然後經度 |
|
郵遞地址 | db.PostalAddress |
Unicode | |
電話號碼 | db.PhoneNumber |
Unicode | |
電子郵件地址 | db.Email |
Unicode | |
Google 帳戶使用者 | users.User |
電子郵件地址 (依 Unicode 順序) |
|
即時通訊控點 | db.IM |
Unicode | |
連結 | db.Link |
Unicode | |
類別 | db.Category |
Unicode | |
評分 | db.Rating |
數字 | |
Datastore 索引鍵 | db.Key |
依路徑元素 (種類、ID、 種類、ID...) |
|
Blobstore 金鑰 | blobstore.BlobKey |
位元組順序 | |
零值 | NoneType |
無 |
重要事項:強烈建議您不要儲存 UserProperty
,因為這個項目含有電子郵件地址和使用者的唯一識別碼。如果使用者變更電子郵件地址,但您還是使用先前儲存的 User
來比對新的 User
值,兩者將無法配對。
針對文字字串和未編碼的二進位資料 (位元組字串),Datastore 支援兩種值類型:
- 短字串 (不超過 1,500 個位元組) 會建立索引,可用於查詢篩選器條件和排序順序。
- 長字串 (不超過 1 MB) 不會建立索引,也不能用於查詢篩選器和排序順序。
Blob
。這類型與 Blobstore API 中使用的 blob 無關。
當查詢的屬性具有混合類型的值時,Datastore 會根據內部表示法使用確定性排序:
- 空值
- 固定點數
- 整數
- 日期和時間
- 評分
- 布林值
- 位元組序列
- 位元組字串
- Unicode 字串
- Blobstore 索引鍵
- 浮點數
- 地理點
- Google 帳戶使用者
- Datastore 索引鍵
長文字字串和長位元組字串不會建立索引,因此未定義排序。
處理實體
應用程式可使用 Datastore API 建立、擷取、更新及刪除實體。如果應用程式知道實體的完整索引鍵 (或是可從父系索引鍵、種類和 ID 中取得),即可在該實體上使用索引鍵直接作業。應用程式也可以透過 Datastore 查詢取得實體索引鍵,詳情請參閱「Datastore 查詢」頁面。
建立實體
在 Python 中,您可以透過以下方式建立新的實體:建構特定模型類別的執行個體,視需要填入屬性,然後呼叫 put()
方法,將實體儲存至 Datastore。可傳送 key_name 引數至建構函式,藉此指定實體金鑰:
employee = Employee(key_name='asalieri',
first_name='Antonio',
last_name='Salieri')
employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True
employee.put()
如果您沒有提供索引鍵名稱,Datastore 會自動為實體的索引鍵產生數字 ID:
employee = Employee(first_name='Antonio',
last_name='Salieri')
employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True
employee.put()
擷取實體
如要擷取以特定索引鍵識別的實體,請將 Key
物件當做引數傳送至 db.get()
函式。您可以使用類別方法 Key.from_path()
產生 Key
物件。完整路徑是祖系路徑中的實體序列,每個實體都由其類型 (字串) 和 ID (金鑰名稱或數字 ID) 所代表:
address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
address = db.get(address_k)
db.get()
會傳回適當模型類別的例項。請務必確認已經匯入模型類別,以便擷取實體。
更新實體
如要更新現有實體,請修改物件的屬性,然後呼叫物件的 put()
方法。物件資料會覆寫現有實體,每次呼叫 put()
時,系統會將整個物件傳送至 Datastore。
如果要刪除屬性,請從 Python 物件中刪除該屬性:
del address.postal_code
然後儲存該物件。
刪除實體
如有實體的索引鍵,您就可以透過 db.delete()
函式刪除該實體
address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
db.delete(address_k)
或是呼叫實體本身的 delete()
方法:
employee_k = db.Key.from_path('Employee', 'asalieri')
employee = db.get(employee_k)
# ...
employee.delete()
批次作業
db.put()
、db.get()
和 db.delete()
函式 (以及其非同步對應項目 db.put_async()
、db.get_async()
和 db.delete_async()
) 可接受清單引數,在單一 Datastore 呼叫中一次對多個實體執行動作:
# A batch put.
db.put([e1, e2, e3])
# A batch get.
entities = db.get([k1, k2, k3])
# A batch delete.
db.delete([k1, k2, k3])
批次作業不會影響費用。無論每個索引鍵存在與否,您都必須為批次作業中的每一個索引鍵付費。作業中實體的大小不會影響費用。
刪除大量實體
如果需要刪除大量的實體,建議您使用 Dataflow 大量刪除實體。
使用空白清單
針對 NDB 介面,資料儲存庫先前會將空白清單做為靜態和動態屬性的省略屬性寫入。為了維持向下相容的能力,會繼續以此做為預設行為。如想在全域範圍或各個 ListProperty 範圍內覆寫這項設定,請將 Property 類別中的 write_empty_list 引數設為true
,接著系統就會將空白清單寫入 Datastore,並以空白清單的形式讀取。
對於 DB 介面,在過去如果屬性為動態,則完全不容許寫入空白清單:如果嘗試此做法,就會出現錯誤狀況。這表示無須為了 DB 動態屬性,而保留向下相容的預設行為,如此一來,無須進行變更,即可在動態模型中讀取及寫入空白清單。
不過,對於 DB 靜態資源,空白清單會寫入為省略的資源,且為了回溯相容性,這項行為會繼續保留。如要為 DB 靜態屬性開啟空白清單,請在 Property 類別中將 write_empty_list 引數設為 true
,接著系統就會將空白清單寫入 Datastore。