エンティティ、プロパティ、キー

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

Datastore のデータ オブジェクトはエンティティと呼ばれます。エンティティは 1 つ以上の名前付きプロパティを持ち、各プロパティが 1 つ以上の値を持ちます。同じ種類のエンティティが同じプロパティを持つとは限りません。また、エンティティの特定のプロパティの値がすべて同じデータ型である必要はありません(必要に応じて、アプリケーション独自のデータモデルでこのような制限を確立して強制できます)。

Datastore では、プロパティ値でさまざまなデータ型がサポートされています。サポートしているデータ型の例を次に示します。

  • 整数
  • 浮動小数点数
  • 文字列
  • 日付
  • バイナリデータ

すべての型の一覧は、プロパティと値の型をご覧ください。

Datastore 内の各エンティティは、キーによって一意に識別されます。キーは次のコンポーネントで構成されています。

  • エンティティの名前空間マルチテナンシーを可能にします。
  • エンティティの種類。Datastore クエリ用にエンティティを分類します。
  • 個々のエンティティの識別子。次のいずれかになります。
    • キー名の文字列
    • 整数の数値 ID
  • オプションの祖先パス。Datastore 階層内のエンティティの位置を指定します。

アプリケーションではエンティティのキーを使用して Datastore から個々のエンティティを取得できます。また、エンティティのキーやプロパティ値に基づくクエリを実行して 1 つ以上のエンティティを取得することもできます。

Python App Engine SDK には、データ モデリング ライブラリが含まれており、これを使用して、Datastore エンティティを Python クラスのインスタンスとして表現し、それらのインスタンスを Datastore に格納および取得できます。

Datastore 自体がエンティティの構造になんらかの制限(特定のプロパティは特定の型の値を持たなければならないなど)を課すことはありません。この役割はアプリケーションが担います。

種類と識別子

Datastore の各エンティティは特定の種類に属しています。種類とは、クエリに対応できるようにエンティティを分類するものです。たとえば、人事アプリケーションでは、社内の各従業員が Employee という種類のエンティティに相当します。Python Datastore API では、エンティティの種類はモデルクラスによって決まります。モデルクラスは、アプリケーション内でデータ モデリング ライブラリのクラス db.Model のサブクラスとして定義します。モデルクラスの名前が、そのエンティティの属する種類となります。2 つのアンダースコア(__)で始まる種類名はすべて予約済みであり、使用できません。

次の例では、種類が 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_namelast_namehire_dateattended_hr_training の 4 つのプロパティを宣言します。Model スーパークラスにより、Employee オブジェクトの属性がこのモデルに準拠することが確実になります。たとえば、hire_date 属性に文字列値を割り当てようとするとランタイムエラーになります。これは、hire_date のデータモデルが db.DateProperty として宣言されていたためです。

種類に加え、各エンティティは作成時に割り当てられた識別子を持ちます。識別子はエンティティのキーの一部であるため、エンティティに永続的に割り当てられており、変更できません。識別子を割り当てる方法は 2 通りあります。

  • アプリケーションからエンティティの固有のキー名文字列を指定する。
  • エンティティに整数の数値 ID が自動的に割り当てられるように Datastore を設定する。

エンティティにキー名を割り当てるには、次のように、エンティティの作成時にモデル クラスのコンストラクタに対して名前付き引数 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()

識別子の割り当て

Datastore は、次の 2 種類の自動 ID ポリシーを使用して自動 ID を生成するように構成できます。

  • default ポリシーは、ほぼ均等に分布する未使用の ID をランダムに生成します。各 ID は最大 16 桁の 10 進数になります。
  • legacy ポリシーは、連続しない小さい整数の ID を作成します。

エンティティの ID をユーザーに表示する場合や、ID の順序に従ってなんらかの処理を行う場合は、手動で割り当てることをおすすめします。

Datastore では、ほぼ一様に分散されたランダムな未使用 ID のシーケンスが生成されます。各 ID は最大 16 桁の 10 進数になります。

祖先パス

Cloud Datastore 内の各エンティティは、ファイル システムのディレクトリ構造と同様の階層的に構造化された空間を形成します。エンティティを作成するときに、別のエンティティを親として設定することもできます。新しいエンティティは、この親エンティティの子になります(ファイル システムとは異なり、親エンティティが実際に存在している必要はありません)。親を持たないエンティティは「ルート エンティティ」となります。エンティティと親との割り当ては永続的であり、エンティティの作成後は変更できません。同じ親を持つ 2 つのエンティティ、または 2 つのルート エンティティ(親を持たないエンティティ)に同じ数値 ID が割り当てられることはありません。

エンティティの親、親の親、さらにその親などは順に「祖先」として位置付けられ、エンティティの子、子の子、さらにその子などは順に「子孫」として位置付けられます。ルート エンティティとその子孫のエンティティは、すべて同じエンティティ グループに属します。ルート エンティティから始まり、親から子を経由して対象のエンティティに至るまでのエンティティの連なりのことを、エンティティの「祖先パス」といいます。エンティティを識別する完全なキーは、エンティティの祖先パスから始まってそのエンティティ自身で終わる一連の「種類と識別子のペア」で構成されています。

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

ルート エンティティの場合は祖先パスが空で、エンティティ自身の種類と識別子だけでキーが構成されています。

[Person:GreatGrandpa]

このコンセプトを次の図に示します。

エンティティ グループ内のルート エンティティと子エンティティの関係を示す図

エンティティの親を指定するには、子エンティティの作成時にモデルクラスのコンストラクタに対して parent 引数を使用します。この引数の値には、親エンティティ自体またはそのキーを指定できます。指定するキーを取得するには、親エンティティの key() メソッドを呼び出します。次の例では、種類が Address のエンティティを作成し、Employee エンティティを親として指定する 2 つの方法を示します。

# 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()

トランザクションとエンティティ グループ

エンティティの作成、更新、削除は、すべてトランザクションのコンテキストで行われます。1 つのトランザクションで、このようなオペレーションが複数実行される場合もあります。トランザクションでは、データの整合性を維持するため、すべてのオペレーションを 1 つの単位として Datastore に適用します。いずれかのオペレーションが失敗した場合、すべてのオペレーションが適用されません。また同一のトランザクション内で実行されるすべての強整合性読み取り(祖先クエリまたは取得)は、データの一貫性のあるスナップショットを維持します。

上記のとおり、エンティティ グループとは、祖先から共通のルート要素までが連結された一連のエンティティです。データをエンティティ グループに編成する場合、実行可能なトランザクションの種類が制限されることがあります。

  • トランザクションがアクセスするすべてのデータは、最大 25 のエンティティ グループに制限されます。
  • トランザクション内でクエリを使用する場合は、適切なデータと一致する祖先フィルタを指定できるように、データをエンティティ グループに編成する必要があります。
  • 1 つのエンティティ グループに対し、1 秒につき約 1 つのトランザクションという書き込みスループットの制限があります。これは高い信頼性とフォールト トレラントを実現するため、Datastore が広範な地域にわたって個々のエンティティ グループをマスターなしで同期複製するために生じる制限です。

多くのアプリケーションでは、相互に関連性のないデータに対する広範なビューを取得するのであれば、結果整合性(複数のエンティティ グループに対する非祖先クエリ、多少古いデータが返される場合もある)の使用で対応できます。一方、関連性の高いデータからなる単一データセットを表示または編集する場合は、強整合性の使用が適します(祖先クエリ、または単一エンティティに対する get)。このようなアプリケーションでは通常、関連性の高い個々のデータセットに対し、個別のエンティティ グループを作成すると効率的です。詳細については、強整合性に対応するデータ構造をご覧ください。

プロパティと値の型

エンティティに関連付けられたデータ値は 1 つ以上のプロパティで構成されます。各プロパティには名前と 1 つ以上の値があります。プロパティは異なる型の複数の値を持つことができるため、2 つのエンティティの同じプロパティに異なる型の値が存在する場合もあります。プロパティはインデックス付けされている場合とされていない場合があります(プロパティ P で並べ替えまたはフィルタリングを行うクエリでは、P がインデックス付けされていないエンティティは無視されます)。1 つのエンティティに、インデックスが付けられたプロパティを最大 20,000 個まで割り当てることができます。

サポートされている値の型は、次のとおりです。

値の型 Python の型 並べ替え順
整数 int
long
数値 64 ビット整数(符号付き)
浮動小数点数 float 数値 64 ビット倍精度、
IEEE 754
ブール値 bool False<True
テキスト文字列(短い) str
unicode
Unicode
(ASCII として処理された str
最大 1,500 バイト
テキスト文字列(長い) db.Text なし 最大 1 メガバイト

インデックス付けなし
バイト文字列(短) db.ByteString バイト順 最大 1,500 バイト
バイト文字列(長い) db.Blob なし 最大 1 メガバイト

インデックス付けなし
日時 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 数値
データストアのキー db.Key パス要素順
(種類、識別子、
種類、識別子...)
Blobstore のキー blobstore.BlobKey バイト順
Null NoneType なし

重要: メールアドレスとユーザーの一意の ID が含まれているため、UserProperty を保存しないことを強くおすすめします。ユーザーがメールアドレスを変更すると、そのユーザーの古いメールアドレス(User に格納されている)と新しい User の値を比較したときに一致しません。

テキスト文字列とエンコードされていないバイナリデータ(バイト文字列)については、Datastore は次の 2 つの値の型をサポートします。

  • 短い文字列(最大 1,500 バイト)は、インデックスに登録され、クエリのフィルタ条件や並べ替えの順序付けに使用できます。
  • 長い文字列(最大 1 メガバイト)は、インデックスに登録されず、クエリのフィルタや並べ替えの順序付けには使用できません。
注: 長いバイト文字列型は、Datastore API では Blob と呼ばれています。この型は、Blobstore API で使用される blob とは関係ありません。

型が混合した値を持つプロパティをクエリで扱う場合、Datastore では内部表現に基づく決定論的な順序付けが使用されます。

  1. Null 値
  2. 固定小数点数
    • 整数
    • 日時型
    • 評価
  3. ブール値
  4. バイト列
    • バイト文字列
    • Unicode 文字列
    • Blobstore のキー
  5. 浮動小数点数
  6. 地理的座標
  7. Google アカウントのユーザー
  8. Datastore のキー

長いテキスト文字列と長いバイト文字列はインデックスに登録されないため、順序付けは定義されていません。

エンティティの操作

アプリケーションは Datastore API を使用してエンティティを作成、取得、更新、削除できます。エンティティの完全なキーがわかっている場合(または親のキー、種類、識別子からキーを導出できる場合)、アプリケーションはそのキーを使用してエンティティを直接操作できます。また、Datastore クエリの結果としてエンティティのキーを取得することもできます。詳細については、Datastore のクエリをご覧ください。

エンティティの作成

Python で新しいエンティティを作成するには、モデルクラスのインスタンスを作成し、必要に応じてプロパティを設定したうえで、そのインスタンスの put() メソッドを呼び出してエンティティをデータストアに保存します。エンティティのキー名は、コンストラクタに 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)で表されます:

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())にはリスト引数を指定できるため、1 回の 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 インターフェースでは、Datastore はこれまで静的なプロパティと動的なプロパティのどちらについても空のリストを省略されたプロパティとして書き込んでいました。下位互換性を維持するため、この動作は引き続きデフォルトになっています。この動作をグローバルに、または ListProperty ごとにオーバーライドするには、Property クラスで write_empty_list 引数を true に設定します。これで、空のリストが Datastore に書き込まれ、空のリストとして読み取れるようになります。

DB インターフェースでは、これまでプロパティが動的な場合は空のリストの書き込みがまったく許可されていませんでした。空のリストを書き込もうとすると、エラーが発生しました。つまり、DB の動的なプロパティについては、下位互換性のために保持する必要があるデフォルトの動作がないため、動的なモデルでは変更なしで空のリストを読み書きできます。

ただし、DB の静的なプロパティについては、空のリストが省略されたプロパティとして書き込まれていたため、下位互換性を維持するためこの動作がデフォルトで継続されます。DB の静的なプロパティで空のリストを有効にするには、Property クラスで write_empty_list 引数を true に設定します。これで、空のリストが Datastore に書き込まれるようになります。