メタデータ

注: 新しいアプリケーションを作成する際には NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュなど、DB クライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを使用している場合は、DB から NDB への移行ガイドをお読みください。

Cloud Datastore では一部のメタデータにプログラムからアクセスできます。これによりメタプログラミングやバックエンドの管理機能の実装が可能となり、整合性のあるキャッシュ保存が簡単になります。これは、アプリケーション用にカスタムのデータストア ビューアを作成する場合などに役立ちます。使用できるメタデータとしては、アプリケーションが使用するエンティティ グループ、名前空間、エンティティの種類、プロパティに関する情報のほか、各プロパティのプロパティ表現などが挙げられます。

アプリケーションに関する一部のメタデータは、GCP Console の Cloud Datastore ダッシュボードでも提供されています。ただし、ここに表示されるデータは、いくつかの重要な側面において関数から返されるデータとは異なります。

  • 鮮度: API を使用してメタデータを読み取ると最新のデータが取得されますが、ダッシュボードのデータが更新されるのは 1 日 1 回だけです。
  • 内容: ダッシュボードの一部のメタデータは 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 例外になって失敗します。

メタデータ クエリは、次の 2 つのクラスのいずれかに属するクエリ オブジェクトを使用して発行できます。

  • (スーパークラス メソッド Model.all() から継承された)クラスメソッド Namespace.all()Kind.all()Property.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() の実装例を示します。この関数は、アプリケーションのすべて(または指定した 2 つの名前 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() の実装例を示します。この関数は、アプリケーションのすべて(または指定した 2 つの名前 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__ であるエンティティを返します。これは、エンティティの種類に関連付けられたプロパティを示します(そのようなプロパティが現在その種類のモデルで定義されているかどうかは関係ありません)。プロパティが P、種類が K で表現されるエンティティは次のように構築されます。

  • エンティティのキーの種類は __property__、キー名は P です。
  • 親エンティティのキーの種類は __kind__、キー名は K です。

プロパティ エンティティは、モデルクラス google.appengine.ext.db.metadata.Property のインスタンスです。文字列プロパティ kind_nameproperty_name(エンティティのキーから作成されます)は、対応する種類とプロパティの名前を返します。Property モデルは、__property__ キーの作成と確認を簡素化するための、次の 4 つのクラスメソッドを備えています。

  • 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

結果には種類 Employeename プロパティ、種類 Managertitle プロパティは含まれません。また、種類 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 のインスタンスであり、種類が K のエンティティで検出されるプロパティ P の表現ごとに 1 つの文字列が含まれます。

表現はプロパティ クラスとは異なり、複数のプロパティ クラスが同じ表現にマップされる場合があります(たとえば、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() の実装例を示します。この関数は、特定のエンティティの種類に関連付けられたアプリケーションのすべて(または指定した 2 つの名前 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 の App Engine スタンダード環境