注: 新しいアプリケーションを作成する際は、NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュをはじめ、このクライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを現在使用している場合は、DB から NDB への移行ガイドをお読みください。
Datastore では一部のメタデータにプログラムからアクセスできます。これによりメタプログラミングやバックエンドの管理機能の実装が可能となり、一貫性のあるキャッシュ保存が簡単になります。これは、アプリケーション用にカスタム Datastore ビューアを作成する場合などに役立ちます。使用できるメタデータとしては、アプリケーションが使用するエンティティ グループ、名前空間、エンティティの種類、プロパティに関する情報のほか、各プロパティのプロパティ表現などが挙げられます。
アプリケーションに関する一部のメタデータは、Cloud Console の Datastore ダッシュボードでも提供されています。ただし、ここに表示されるデータは、いくつかの重要な側面において関数から返されるデータとは異なります。
- 鮮度: API を使用してメタデータを読み取ると最新のデータが取得されますが、ダッシュボードのデータが更新されるのは 1 日 1 回だけです。
- 内容: ダッシュボードの一部のメタデータは 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
例外が発生して失敗します。
メタデータ クエリは、次の 2 つのクラスのいずれかに属するクエリ オブジェクトを使用して発行できます。
- クラスメソッド
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__
エンティティはプロパティを持たないため、キーのみのクエリでもキーのみではないクエリでも同じ情報が返されます。
Namespace のエンティティは、モデルクラス google.appengine.ext.db.metadata.Namespace
のインスタンスです。文字列プロパティ namespace_name
(エンティティのキーから作成されます)は、対応するエンティティの種類の名前を返します(キーが数値 ID 1
の場合、このプロパティは空の文字列を返します)。クエリを容易に実行できるように、Namespace
モデルは次のクラスメソッドを備えています。
Namespace.key_for_namespace()
は、名前空間名から__namespace__
キーを作成します。Namespace.key_to_namespace()
は、特定の__namespace__
キーに対応する名前空間名を返します。
例として、ヘルパー関数 get_namespaces()
の実装を次に示します。これは、アプリケーションのすべての名前空間(または指定された 2 つの名前、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__
エンティティはプロパティを持たないため、キーのみのクエリでもキーのみではないクエリでも同じ情報が返されます。
Kind のエンティティは、モデルクラス google.appengine.ext.db.metadata.Kind
のインスタンスです。文字列プロパティ kind_name
(エンティティのキーから作成されます)は、対応するエンティティの種類の名前を返します。クエリを容易に実行できるように、Kind
モデルは次のクラスメソッドを備えています。
Kind.key_for_kind()
は、種類名から__kind__
キーを作成します。Kind.key_to_kind()
は、特定の__kind__
キーに対応する種類の名前を返します。
以下にヘルパー関数 get_kinds()
の実装例を示します。この関数は、アプリケーションのすべてのエンティティの種類(または指定された 2 つの名前、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__
であるエンティティを返します。これは、エンティティの種類に関連付けられたプロパティを示します(そのようなプロパティが現在その種類のモデルで定義されているかどうかは問いません)。プロパティが P、種類が K で表現されるエンティティは次のように構築されます。
- エンティティのキーの種類は
__property__
で、キー名は P です。 - 親エンティティのキーの種類は
__kind__
で、キー名は K です。
Property のエンティティは、モデルクラス google.appengine.ext.db.metadata.Property
のインスタンスです。文字列プロパティ kind_name
と property_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
結果には種類 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
のインスタンスです。このインスタンスには、種類が K のエンティティで検出されたプロパティ P の表現ごとに 1 つの文字列が含まれます。
表現はプロパティ クラスとは異なり、複数のプロパティ クラスが同じ表現にマップされる場合があります(たとえば、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()
の実装例を示します。この関数は、特定のエンティティの種類に関連付けられたアプリケーションのすべてのインデックス付きプロパティ(または指定された 2 つの名前、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