附註:我們強烈建議建構新應用程式的開發人員使用 NDB 用戶端程式庫,因為 NDB 用戶端程式庫與本用戶端程式庫相較之下有幾個優點,例如能透過 Memcache API 自動將實體加入快取。如果您目前使用的是舊版的 DB 用戶端程式庫,請參閱從 DB 至 NDB 的遷移指南。
Datastore 針對部分中繼資料提供程式化的存取機制,進而支援元程式設計、實作後端管理函式、簡化一致性快取等類似目的;舉例來說,您可以將其用於為應用程式建構自訂 Datastore 檢視器。這些中繼資料包括實體群組、命名空間、實體種類、應用程式使用的屬性,以及各個屬性的屬性表示法等相關資訊。
另外在 Google Cloud 主控台的 Datastore 資訊主頁也提供應用程式的部分中繼資料,但該處顯示的資料與這些函式傳回的資料在某些重要層面有所不同。
- 更新間隔。使用 API 讀取中繼資料會傳回最新資料,資訊主頁中的資料每天只更新一次。
- 內容。無法透過 API 讀取資訊主頁中的某些中繼資料;反之亦然。
- 速度。中繼資料的取得與查詢作業計費方式與 Datastore 取得與查詢作業相同。擷取命名空間、種類及屬性相關資訊的中繼資料查詢作業,執行速度通常較慢。根據經驗法則,單次中繼資料查詢傳回 N 個實體所需的時間,大約相當於 N 次一般查詢每次傳回單一實體的加總時間。再者,屬性表示法查詢 (非僅限金鑰屬性查詢) 的速度比僅限金鑰屬性查詢慢。實體群組中繼資料的中繼資料取得作業,比取得一般實體更快。
輔助函式
以下函式可取得中繼資料資訊:
get_entity_group_version()
可取得實體群組的版本號碼;這個函式適用於識別自您上次取得版本號碼後,群組中的任何實體是否有任何變更。get_namespaces()
會傳回清單,其中包含應用程式所有或指定範圍的命名空間名稱。get_kinds()
會傳回清單,其中包含應用程式所有或指定範圍的實體種類名稱。get_properties_of_kind()
會傳回清單,其中包含與指定實體種類相關聯的應用程式所有 (或指定範圍的) 已建立索引的屬性名稱。不含未建立索引的屬性。get_representations_of_kind()
會傳回字典,其中包含與指定實體種類相關聯的應用程式所有或指定範圍已建立索引的屬性表示法。這個字典將各個屬性的名稱與該屬性的表示法清單相互對應。不含未建立索引的屬性。
實體群組中繼資料
Cloud Datastore 提供實體群組的「版本」存取,每次變更實體群組,僅為正值的版本數字一定會增加。
以下範例顯示如何取得實體群組的版本:
from google.appengine.ext import db
from google.appengine.ext.db import metadata
class Simple(db.Model):
x = db.IntegerProperty()
entity1 = Simple(x=11)
entity1.put()
# Print entity1's entity group version
print 'version', metadata.get_entity_group_version(entity1)
# Write to a different entity group
entity2 = Simple(x=22)
entity2.put()
# Will print the same version, as entity1's entity group has not changed
print 'version', metadata.get_entity_group_version(entity1)
# Change entity1's entity group by adding a new child entity
entity3 = Simple(x=33, parent=entity1.key())
entity3.put()
# Will print a higher version, as entity1's entity group has changed
print metadata.get_entity_group_version(entity1)
舊版行為
在舊版實體群組版本行為中,實體群組的版本數字僅會隨實體群組的變更而增加。舉例來說,舊版實體群組中繼資料行為,可用來確保實體群組複雜祖系查詢快取的一致性。
以下範例會快取查詢結果 (相符結果數),並藉由實體群組版本的舊版行為來使用最新的快取值:
from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.db import metadata
def count_entity_group(entity_group_key):
"""Count the entities in the specified entity group."""
# Check if we have a cached version of the current entity group count
cached = memcache.get(str(entity_group_key))
if cached:
(version, count) = cached
# Is the cached value for the current version?
if version == metadata.get_entity_group_version(entity_group_key):
return count
def tx():
# Need to actually count entities. Using a transaction to get a consistent
# count and entity group version.
count = db.Query(keys_only=True).ancestor(entity_group_key).count(limit=5000)
# Cache the count and the entity group version
version = metadata.get_entity_group_version(entity_group_key)
memcache.set(str(entity_group_key), (version, count))
return count
return db.run_in_transaction(tx)
get_entity_group_version()
可能會針對從未寫入的實體群組傳回 None
。
對於包含 __version__
屬性的特殊虛擬實體呼叫 get()
,可取得實體群組版本。詳情請參閱 EntityGroup 的參考說明文件。
中繼資料查詢
如果上節所述的輔助函式不符合您的需求,您可以透過明確的中繼資料查詢發出更詳細或彈性的中繼資料要求。在 Python 中,這類查詢的模型類別是在 google.appengine.ext.db.metadata
套件中定義。這些模型可提供中繼資料查詢專用的特殊實體種類:
模型類別 | 實體種類 |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
這些模型與種類不會與應用程式中可能已存在的同名模型與種類發生衝突。您可以藉由查詢這些特殊種類,而擷取包含所需中繼資料的實體。
中繼資料查詢傳回的實體是根據 Datastore 現狀而動態產生的。雖然您可以建立 Namespace
、Kind
或 Property
模型類別的本機執行個體,但嘗試將這些執行個體儲存於 Datastore 時會發生 BadRequestError
例外狀況的失敗。
您可以使用屬於下列兩個類別的查詢物件來發出中繼資料查詢:
- 類別方法
Namespace.all()
、Kind.all()
或Property.all()
傳回的Query
物件 (繼承自父類別方法Model.all()
) - GQL 樣式查詢的
GqlQuery
物件
以下範例會傳回應用程式所有實體種類的名稱:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
for k in Kind.all():
print "kind: '%s'" % k.kind_name
命名空間查詢
如果應用程式使用 Namespaces API,您可以使用「命名空間查詢」來找出在應用程式的實體中使用的所有命名空間。您可以透過這種方式執行活動,例如跨多個命名空間的管理功能。
命名空間查詢會傳回特殊種類 __namespace__
的實體,其索引鍵名稱就是命名空間的名稱。(由空字串 ""
指定的預設命名空間則為例外:空字串並非有效的索引鍵名稱,因此這個命名空間會改以數字 ID 1
當做索引鍵)。這類型的查詢僅支援對特殊虛擬屬性 __key__
的範圍進行篩選,其屬性值為實體索引鍵。結果可依照 __key__
的值遞增排序 (不可遞減排序)。__namespace__
實體沒有屬性,因此僅限索引鍵和非僅限索引鍵的查詢,都會傳回相同資訊。
命名空間實體是 google.appengine.ext.db.metadata.Namespace
模型類別的例項。從實體金鑰運算得出的 namespace_name
字串屬性,會傳回對應命名空間的名稱。(如果金鑰有數字 ID 1
,屬性會傳回空白字串)。為加快查詢作業,Namespace
模型提供下列類別方法:
Namespace.key_for_namespace()
會根據命名空間名稱建立__namespace__
鍵。Namespace.key_to_namespace()
會傳回與特定__namespace__
鍵相對應的命名空間名稱。
舉例來說,以下是輔助函式 get_namespaces()
的實作方式,它會傳回清單,其中包含應用程式所有命名空間的名稱 (或兩個指定名稱 start
和 end
之間的範圍):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Namespace
def get_namespaces(start=None, end=None):
# Start with unrestricted namespace query
q = Namespace.all()
# Limit to specified range, if any
if start is not None:
q.filter('__key__ >=', Namespace.key_for_namespace(start))
if end is not None:
q.filter('__key__ <', Namespace.key_for_namespace(end))
# Return list of query results
return [ns.namespace_name for ns in q]
種類查詢
「種類查詢」會傳回種類 __kind__
的實體,其索引鍵名稱為實體種類名稱。這種類型的查詢間接受限於目前的命名空間,且僅支援限定 __key__
虛擬屬性範圍的篩選作業。可按照 __key__
的值遞增排序結果 (不可遞減排序)。__kind__
的實體沒有屬性,因此僅限索引鍵和並非僅限索引鍵的查詢,都會傳回相同資訊。
種類實體為 google.appengine.ext.db.metadata.Kind
模型類別的例項。從實體金鑰運算得出的 kind_name
字串屬性,會傳回對應實體種類的名稱。為加快查詢作業,Kind
模型提供下列類別方法:
Kind.key_for_kind()
會根據類型名稱建立__kind__
鍵。Kind.key_to_kind()
會傳回與特定__kind__
鍵相對應的種類名稱。
舉例來說,以下是輔助函式 get_kinds()
的實作方式,這個函式會傳回清單,其中包含應用程式所有或指定範圍的實體種類名稱 (或兩個指定名稱 start
和 end
之間的範圍):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
def get_kinds(start=None, end=None):
# Start with unrestricted kind query
q = Kind.all()
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Kind.key_for_kind(start))
if end is not None:
if end == '':
return [] # Empty string is not a valid kind name, so can't filter
q.filter('__key__ <', Kind.key_for_kind(end))
# Return list of query results
return [k.kind_name for k in q]
以下範例可輸出名稱開頭為小寫字母的所有種類:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
# Start with unrestricted kind query
q = Kind.all()
# Limit to lowercase initial letters
q.filter('__key__ >=', Kind.key_for_kind('a'))
endChar = chr(ord('z') + 1) # Character after 'z'
q.filter('__key__ <', Kind.key_for_kind(endChar))
# Print query results
for k in q:
print k.kind_name
屬性查詢
「屬性查詢」會傳回表示實體種類相關屬性的 __property__
種類實體 (無論目前是否已在這個種類的模型中定義這些屬性)。代表「K」種類的「P」屬性的實體建構方式如下:
- 實體的索引鍵具有種類
__property__
及索引鍵名稱「P」。 - 父系實體的金鑰具有
__kind__
種類,且金鑰名稱為「K」。
屬性實體是 google.appengine.ext.db.metadata.Property
模型類別的例項。從實體金鑰運算得出的 kind_name
和 property_name
字串屬性,會傳回對應種類和屬性的名稱。Property
模型提供四種類別方法,可簡化建構與檢查 __property__
鍵的作業:
Property.key_for_kind()
會為指定實體類別的__property__
金鑰建立父項__kind__
金鑰。Property.key_for_property()
會為指定的類型和屬性建立__property__
鍵。Property.key_to_kind()
會傳回與__property__
鍵相關聯的類別名稱。Property.key_to_property()
會傳回與__property__
金鑰相關聯的屬性名稱 (如果金鑰僅指定一種類型,則會傳回None
)。
以下範例說明這些方法:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
class Employee(db.Model):
name = db.StringProperty()
ssn = db.IntegerProperty()
employee_key = Property.key_for_kind("Employee")
employee_name_key = Property.key_for_property("Employee", "Name")
Property.key_to_kind(employee_key) # Returns "Employee"
Property.key_to_property(employee_name_key) # Returns "Name"
屬性查詢的行為取決於其為僅限金鑰或非僅限金鑰 (屬性表示法) 的查詢,詳細說明如以下子節所述。
屬性查詢:僅限金鑰
「僅限金鑰的屬性查詢」會針對指定實體種類的各個已建立索引的屬性傳回金鑰 (不含未建立索引的屬性)。以下範例會輸出應用程式所有實體種類的名稱,以及與各個實體種類相關屬性的名稱:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Create unrestricted keys-only property query
q = Property.all(keys_only=True)
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
這種類型的查詢間接受限於目前的命名空間,且僅支援限定 __key__
虛擬屬性範圍的篩選作業。其中金鑰代表 __kind__
或 __property__
實體。可按照 __key__
的值遞增排序結果 (不可遞減排序)。種類與屬性組合會套用篩選,先依照種類排序,再依照屬性排序;舉例來說,假設您有具下列屬性的實體:
- 種類
Account
及其下列屬性balance
company
- 種類
Employee
及其下列屬性name
ssn
- 種類
Invoice
及其下列屬性date
amount
- 種類
Manager
及其下列屬性name
title
- 種類
Product
及其下列屬性description
price
要傳回屬性資料的查詢應如下所示:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit range
q.filter('__key__ >=', Property.key_for_property("Employee", "salary"))
q.filter('__key__ <=', Property.key_for_property("Manager", "salary"))
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
上述查詢將傳回下列內容:
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
請注意,結果不包含 Employee
種類的 name
屬性和 Manager
種類的 title
屬性,也不包含 Account
和 Product
種類的所有屬性,因為這些項目不在查詢指定的範圍內。
屬性查詢另外也支援以 __kind__
或 __property__
索引鍵進行祖系篩選,藉此將查詢結果限制為單一種類或屬性。舉例來說,您可以使用屬性查詢取得與指定實體種類相關的屬性,如以下範例所示:
get_properties_of_kind()
的實作程序)
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_properties_of_kind(kind, start=None, end=None):
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Return list of query results
return [Property.key_to_property(p) for p in q]
屬性查詢:非僅限金鑰 (屬性表示法)
非僅限金鑰的屬性查詢,也稱為「屬性表示法查詢」,會傳回各個種類屬性組合使用的表示法詳細資訊 (不含未建立索引的屬性)。「K」種類的「P」屬性傳回的實體具有的金鑰,與對應的僅限金鑰查詢 (搭配會傳回屬性表示法的其他 property_representation
屬性) 傳回的金鑰相同。這個屬性的值是 StringListProperty
類別的例項,其中包含每個實體屬性 P 的表示法字串,這些實體屬性可在任何 K 類型實體中找到。
請注意,表示法跟屬性類別不同;同時可以有多個屬性類別對應至相同的表示法 (例如,StringProperty
和 PhoneNumberProperty
都使用 STRING
表示法)。
下表列出屬性類別分別對應的表示法:
屬性類別 | 表示法 |
---|---|
IntegerProperty |
INT64 |
FloatProperty |
DOUBLE |
BooleanProperty |
BOOLEAN |
StringProperty |
STRING |
ByteStringProperty |
STRING |
DateProperty |
INT64 |
TimeProperty |
INT64 |
DateTimeProperty |
INT64 |
GeoPtProperty |
POINT |
PostalAddressProperty |
STRING |
PhoneNumberProperty |
STRING |
EmailProperty |
STRING |
UserProperty |
USER |
IMProperty |
STRING |
LinkProperty |
STRING |
CategoryProperty |
STRING |
RatingProperty |
INT64 |
ReferenceProperty SelfReferenceProperty |
REFERENCE |
blobstore.BlobReferenceProperty |
STRING |
ListProperty |
清單元素的表示法 |
StringListProperty |
清單元素的表示法 |
舉例來說,以下是輔助函式 get_representations_of_kind()
的實作方式,該函式會傳回字典,其中包含與指定實體種類相關聯的應用程式所有或指定範圍已建立索引的屬性表示法 (或兩個指定名稱 start
和 end
之間的範圍)。這個字典將各個屬性的名稱與該屬性的表示法清單相互對應。
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_representations_of_kind(kind, start=None, end=None):
# Start with unrestricted non-keys-only property query
q = Property.all()
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Initialize result dictionary
result = {}
# Add query results to dictionary
for p in q:
result[p.property_name] = p.property_representation
# Return dictionary
return result