中繼資料

附註:強烈建議建構新應用程式的開發人員使用 NDB 用戶端程式庫;相較於此用戶端程式庫,NDB 用戶端程式庫有幾項優點,例如能透過 Memcache API 自動快取實體。如果您目前使用的是舊版 DB 用戶端程式庫,請參閱 DB 至 NDB 遷移指南

Cloud Datastore 針對其部分中繼資料提供程式化的存取機制,進而支援元程式設計、實作後端管理函式、簡化一致性快取等類似目的;舉例來說,您可以將其用於為應用程式建構自訂 Cloud Datastore 檢視器。這些中繼資料包括實體群組、命名空間、實體種類、應用程式使用的屬性,以及各個屬性的屬性表示法等相關資訊。

另外在 GCP 主控台的 Cloud Datastore 資訊主頁也提供應用程式的部分中繼資料,但該處顯示的資料與這些函式傳回的資料在某些重要層面有所不同。

  • 時效性: 使用 API 讀取中繼資料會傳回最新資料,資訊主頁中的資料每天只更新一次。
  • 內容: 無法透過 API 讀取資訊主頁中的某些中繼資料;反之亦然。
  • 速度: 中繼資料的取得與查詢作業計費方式與 Cloud 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__

這些模型與種類不會與應用程式中可能已存在的同名模型與種類發生衝突。您可以藉由查詢這些特殊種類,而擷取包含所需中繼資料的實體。

中繼資料查詢傳回的實體會根據 Cloud Datastore 的目前狀態而動態產生。雖然您可以建立 NamespaceKindProperty 模型類別的本機執行個體,但嘗試將這些執行個體儲存於 Cloud Datastore 時會發生 BadRequestError 例外狀況的失敗。

您可以使用屬於下列兩個類別的查詢物件來發出中繼資料查詢:

  • Namespace.all(), Kind.all()Property.all() 類別方法 (繼承自父類別方法 Model.all()) 傳回的 Query 物件
  • 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 模型提供下列類別方法:

舉例來說,以下是輔助函式 get_namespaces() 的實作程序,將傳回包含應用程式所有 (或 startend 兩個名稱之間的指定範圍內) 命名空間名稱的清單:

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 模型提供下列類別方法:

舉例來說,以下是輔助函式 get_kinds() 的實作程序,將傳回包含應用程式所有 (或 startend 兩個名稱之間的指定範圍內) 實體種類名稱的清單:

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_nameproperty_name 字串屬性,會傳回對應種類和屬性的名稱。Property 模型提供四種類別方法,可簡化建構與檢查 __property__ 金鑰的作業:

以下範例說明這些方法:

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 屬性,也不包含 AccountProduct 種類的所有屬性,因為這些項目不在查詢的指定範圍內。

屬性查詢另外也支援針對 __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 類別的執行個體,其中包含的每個字串代表的是「K」種類的任何實體中找到的「P」屬性的各個表示法。

請注意,表示法跟屬性類別不同;同時可以有多個屬性類別對應至相同的表示法 (舉例來說,StringPropertyPhoneNumberProperty 同樣使用 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() 的實作程序,將傳回包含與指定實體種類相關聯的應用程式所有 (或 startend 兩個名稱之間的指定範圍內) 已建立索引的屬性表示法的字典。這個字典將各個屬性的名稱與該屬性的表示法清單相互對應。

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
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境