App Engineの関係モデリング

リレーショナルDBMSとORMの操作に慣れているGAE開発者が直面する問題の1つは、App Engineのリンクと関係です。 このガイドでは、2つの質問について説明します。まず、一般的なDBMSの関係とは何ですか?; 次に、それらはGAEでどのように使用されますか?



関係タイプ





DBMSは、1対1、1対多、多対多の関係など、いくつかのタイプの関係で動作します。 用語の違いにもかかわらず、関係はリンクと同じ原理で機能します。 リンクは、別のエンティティのキ​​ーを含むエンティティフィールドです。たとえば、「pet」が「owner」を指す場合、「pet」エンティティにはエンティティ「owner」のキーを含むフィールドがあることを意味します。



あらゆる種類の関係をリンクとして表すことができます。 最も単純な形式の1対多タイプはリンクです。各「ペット」には独自の「所有者」があるため、「所有者」はそれを参照する複数の「ペット」を持つことができます。 同時に、「所有者」自体は変わりません-それは彼を彼らのマスターと呼ぶ個々の「ペット」に基づいています。



1対1の関係は、「所有者」を参照する「ペット」が1つだけであるという追加の制限がある1対多の関係です。 この制限は、相互参照(各エンティティの相互のフィールドリンク)を保存することで強化できます。



多対多の関係はもう少し複雑です。 それらはいくつかの方法で実装できますが、すべてリンクのペアのリストになります。 例としてWebページを考えてみましょう。 各ページには、多くのインバウンドリンクとアウトバウンドリンクがあります。 それらは、フォームのペアのリスト(from_url、to_url)として表すことができます。 リレーショナルDBMSでは、このような通信は個別のテーブルに保存され、クエリで結合されて関連レコードを検索します。



次に、上記のタイプのリンクがApp Engineでどのように機能するかを見てみましょう。 一般に、「1対多」などの用語を取り除き、エンティティをオブジェクト指向の観点から検討すると便利です。 質問の仕方が異なります。データ構造に適合するために、あるエンティティが別のエンティティをどのように参照する必要がありますか?



App Engineの関係





1対多





このタイプの関係は、どのシステムにも簡単に実装できます。 App Engineプラットフォームは、「多く」側から本質的に「1」側のキーのストレージを提供します。 Pythonでは、ReferencePropertyリンクフィールドがこれに使用されます。



class Owner(db.Model): name = db.StringProperty() class Pet(db.Model): name = db.StringProperty() owner = db.ReferenceProperty(Owner)
      
      







「pet」の「owner」を見つけるために、pet.owner属性に目を向けると、App Engineは参照しているエンティティを自動的にロードします。 特定の「所有者」を参照するすべての「ペット」を見つけるには、次のクエリを実行するだけで十分です。



 pets = Pet.all().filter('owner =', owner).fetch(100)
      
      







同様の結果を簡単に取得できます。ReferencePropertyは、関連データへの迅速かつ便利なアクセスのためにOwnerクラスにプロパティを自動的に作成するため、次のような「ペット」のリストを取得できます。



 pets = Owner.owner_set.fetch(100)
      
      







デフォルトでは、App Engineはこのプロパティにフィールド名+ "_set"という名前を付けますが、独自のプロパティを指定できます。



 class Pet(db.Model): name = db.StringProperty() owner = db.ReferenceProperty(Owner, collection_name='pets') pets = owner.pets.fetch(100)
      
      







1対多の関係をモデル化する別の方法は、エンティティを親に結び付けることです。 エンティティが作成されると、親を割り当てることができます。 この場合、親エンティティのキ​​ーは子キーの一部になり、将来変更することはできません。 例では次のようになります。



 class Owner(db.Model): name = db.StringProperty() class Pet(db.Model): name = db.StringProperty() bob = Owner(name='Bob') felix = Pet(name='Felix', parent=bob) owner_of_felix = felix.parent
      
      







さらに、エンティティ間の関係をどこにも明示的に示すことはしません。作成時の親の指示に従います。 参照フィールド(ReferenceProperty)の代わりにペアレンタルバインディングを使用したほうがよいのはいつですか? これは、トランザクションの操作に影響します。AppEngineでは、個々のトランザクションごとに、1つのグループのエンティティのみを操作できます。 同じグループの親を持つ多くのエンティティ。 関連エンティティがトランザクションに入らないようにするには、リンクフィールドを使用します。 また、エンティティは1つの直接の親しか持つことができず、そのキーは作成後に変更できないことに注意してください。



一対一





1対1の関係は、1対多の関係の特殊なケースです。 それらは、リンクフィールドの「一方」側に別のエンティティを格納することによって実行されます。



多対多





多対多は実装が最も困難です。 App Engineには、それらを構築するためのいくつかのソリューションがあります。 最も明らかなアプローチは、関係の両側のキーペアを含むリレーショナルデータベースに似たリンクテーブルです。 「ペット/所有者」の例では、次のようになります。



 class Owner(db.Model): name = db.StringProperty() class Pet(db.Model): name = db.StringProperty() class PetOwner(db.Model): pet = db.ReferenceProperty(Pet, collection_name='owners') owner = db.ReferenceProperty(Owner, collection_name='pets')
      
      







この方法の利点は、関係に追加のプロパティを追加できることです。たとえば、ページのリンクリンクをモデリングする場合、リンクテキストを含むフィールドを関係に追加できます。 データアクセスは段階的に実行されます。関連するペアが見つかり、そこから目的のエンティティが抽出されます。 この例では、この記事で説明されているリンクからのエンティティのバッチ抽出のパターンを使用しています *



 petowners = felix.owners.fetch(100) prefetch_refprops(owners, 'owner') owners = [x.owner for x in petowners]
      
      







別の方向(「所有者」から「ペット」へ)のエンティティの抽出も同様に実行されます。



別のアプローチは、関係の反対側にエンティティキーのリストを保存することです。 これは、格納されているアイテムの数が意図的に制限されている場合(たとえば、数百以下)に役立ちます。 このようなリストを使用すると、バッチ操作を実行するのに便利です。 例:



 class Pet(db.Model): name = db.StringProperty() class Owner(db.Model): name = db.StringProperty() pets = db.ListProperty(db.Key)
      
      







各「所有者」から、彼の「ペット」のリストを抽出できます。



 pets = db.get(bob.pets)
      
      







また、特定の「ペット」のすべての「所有者」を見つけるには、次のクエリを実行します。



 owners = Owner.all().filter('pets =', felix).fetch(100)
      
      







最後に、ハイブリッドアプローチが最も生産的で柔軟性が高い場合があります。 このテーマについては、Brett SlatkinsによるApp Engineでの複雑でスケーラブルなアプリケーションの開発に関するすばらしいレポートをご覧になることをお勧めします。



* - リポジトリに対して不要なクエリを実行せずにリンクからエンティティを抽出するために、記事の著者が開発したパターンを指します。 簡単に言えば、参照フィールドはエンティティをすぐにはロードせず、属性またはリンクメソッドにアクセスすると、リクエストが満たされます。 クエリの数を最小限に抑えるために、パターンは一度にリンクごとにエンティティを読み込みます(翻訳者のメモ)。



All Articles