継承
Overview
このガイドでは、Mongoid モデルに継承を実装する方法を学習できます。継承により、1 つの「親」クラスの特性を 1 つ以上の「子」クラスに適用できます。
Mongoid は、トップレベル および 埋め込みドキュメントの継承をサポートしています。 子モデルクラスが親クラスから継承する場合、Mongoid は親クラスのフィールド、関連付け、検証、スコープを子クラスにコピーします。
継承の割り当て
子モデルクラスを作成するときは、<
文字を使用して、指定された親クラスからの継承を実装します。 次のモデル クラスは、Person
、Employee
、Manager
モデル間で親クラスと子クラスを作成する方法を示しています。
class Person include Mongoid::Document field :name, type: String end class Employee < Person field :company, type: String field :tenure, type: Integer scope :new_hire, ->{ where(:tenure.lt => 1) } end class Manager < Employee end
Mongoid は、Person
、Employee
、Manager
のインスタンスを people
コレクションに保存します。 Mongoid は、読み取り操作を実行するときにドキュメントが期待された型として返されるようにするために、_type
弁別子フィールドをドキュメント内のモデルクラス名に設定します。
埋め込みドキュメント
埋め込み関連付けに継承パターンを実装することもできます。 最上位のモデル クラスの動作と同様に、Mongoid は、埋め込みドキュメントの作成に使用されるモデルクラスに応じて、埋め込みドキュメント内の _type
弁別子フィールドを設定します。
次の例では、 Person
モデルに埋め込み関連付けを追加し、埋め込み Info
クラスの親モデルと子モデルを作成します。
class Person include Mongoid::Document field :name, type: String embeds_many :infos end ... class Info include Mongoid::Document field :active, type: Boolean embedded_in :person end class Phone < Info field :value, type: Float field :country, type: String end class Email < Info field :value, type: String field :category, type: String end
クエリ動作
子モデルクラスをクエリすると、クエリは _type
フィールドの値がクエリされたクラスまたはそれ以降の子クラスと一致するドキュメントのみを返します。 例、Employee
クラスでクエリを実行すると、クエリは _type
値が "Employee"
または "Manager"
のいずれかである people
コレクションのドキュメントを返します。 他のすべての弁別子の値は、親の Person
クラスのインスタンスとして扱われます。
Person
などの親クラスをクエリすると、Mongoid は次のいずれかの条件を満たすドキュメントを返します。
弁別子の値は、親クラスまたは子クラスの名前です。 例、、
"Person"
、"Employee"
、"Manager"
などがあります。弁別子の値がありません。
弁別子の値は、親クラスまたはその子クラスのいずれにもマップされません。 例、
"Director"
、"Specialist"
など。
弁別子キーの変更
次のいずれかの理由で、デフォルトのフィールド名 _type
から弁別子キーを変更することができます。
最適化:
_t
などの短いキーを選択できます。既存のシステムとの整合性: 事前に定義されたキーを持つ既存のシステムまたはデータセットを使用している場合があります。
弁別子キーは、クラスレベルまたはグローバル レベルで変更できます。 クラスレベルで弁別子キーを変更するには、discriminator_key
メソッドを使用して親クラスにカスタムキー名を設定します。
次の例は、モデルクラス を定義するときにカスタム弁別子キーを設定する方法を示しています。
class Person include Mongoid::Document field :name, type: String self.discriminator_key = "sub_type" end
Person
またはその子クラスのインスタンスを作成すると、Mongoid はMongoDBのドキュメントに sub_type
フィールドを追加します。
注意
弁別子キーは、親クラスでのみ変更できます。 子クラスにカスタムキーを設定すると、Mongoid はエラーを発生させます。
子クラス を定義した後に弁別子のキーを変更すると、Mongoid は新しいキーフィールドを追加しますが、古いフィールドは変更されません。 例、モデル クラスを定義した後、アプリケーションに次のコードを追加するとします。
Person.discriminator_key = "sub_type"
この場合、Employee
などの子クラスのインスタンスを作成すると、Mongoid は sub_type
フィールドと _type
フィールドの両方をドキュメントに追加します。
また、グローバル レベルで弁別子キーを変更して、すべてのクラスが _type
フィールドの代わりに指定されたキーを使用するようにすることもできます。
モデル クラスを定義する前に、アプリケーションに次のコードを追加することで、グローバル キーを設定できます。
Mongoid.discriminator_key = "sub_type"
すべてのクラスは sub_type
を弁別子キーとして使用し、_type
フィールドを含みません。
注意
そのグローバル値を使用するクラスの子クラスを定義する前に、グローバルレベルで弁別子キーを設定する必要があります。 子クラスを定義した後にグローバルキーを設定すると、保存されたドキュメントにはデフォルトの_type
フィールドが含まれます。
弁別子の値を変更
MongoDBでは、Mongoid が弁別子の値として設定する値をカスタマイズできます。 次の例に示すように、弁別子の値をカスタマイズするには、クラスを定義するときに discriminator_value
メソッドを使用します。
class Employee include Mongoid::Document field :company, type: String self.discriminator_value = "Worker" end
Employee
のインスタンスを作成すると、ドキュメントの _type
弁別子フィールドにはクラス名ではなく "Worker"
の値が含まれます。
注意
弁別子の値のカスタマイズは子クラスで宣言されているため、クエリを送信する前にクエリによって検索された子クラスをロードする必要があります。
前の例では、返されたドキュメントに Employee
のインスタンスが含まれている場合は、Person
をクエリする前に Employee
クラス定義をロードする必要があります。 オートロードでは、弁別子の値 "Worker"
を解決して、ドキュメントを Employee
のインスタンスとして返すことはできません。
埋め込み関連付け
割り当て、または build
メソッドと create
メソッドを使用して、埋め込み関連付けに任意のタイプの親クラスまたは子クラスを作成できます。 任意のモデルクラスを2 番目のパラメーターとして build
メソッドと create
メソッドに渡して、その特定のインスタンスを埋め込みドキュメントとして作成するよう Mongoid に指示できます。
次のコードは Employee
のインスタンスを作成し、さまざまな作成方法を使用して埋め込みドキュメントを追加する方法を示します。
# Creates a new Employee instance e = Employee.create( name: "Lance Huang", company: "XYZ Communications", tenure: 2 ) # Builds an Info object e.infos.build({ active: true }) # Builds a Phone object e.infos.build( { active: true, value: 1239007777, country: "USA" }, Phone ) # Creates an Email object e.infos.create( { active: true, value: "l.huang@company.com", category: "work" }, Email ) # Creates and assigns an Email object p = Email.new(active: false, value: "lanceh11@mymail.com", category: "personal" ) e.infos << p # Saves the Employee instance to database e.save
次のドキュメントはpeople
データベースに保存されています。
{ "_id": {...}, "name": "Lance Huang", "company": "XYZ Communications", "tenure": 2, "_type": "Employee", "infos": [ { "_id": {...}, "active": true, "value": "l.huang@company.com", "category": "work", "_type": "Email" }, { "_id": {...}, "active": false, "value": "lanceh11@mymail.com", "category": "personal", "_type": "Email" }, { "_id": {...}, "active": true, "_type": "Info" }, { "_id": {...}, "active": true, "value": 1239007777, "country": "USA", "_type": "Phone" } ] }
永続性コンテキスト
子クラスの永続化コンテキストを親の永続化コンテキストから変更して、ドキュメントをデフォルトとは異なる場所に保存できます。 store_in
メソッドを使用すると、子クラスのインスタンスを親モデルのインスタンスとは異なるコレクション、データベース、または クラスター に保存できます。
次のモデル定義は、store_in
メソッドを使用して Employee
と Manager
のインスタンスを people
コレクションとは異なるコレクションに保存する方法を示しています。
class Person include Mongoid::Document end class Employee < Person # Specifies "employees" as target collection store_in collection: :employees end class Manager < Employee # Specifies "managers" as target collection store_in collection: :managers end
注意
Mongoid は、保存されたドキュメントに弁別子フィールドを引き続き追加します。
一部の子クラスに代替ターゲットコレクションを設定し、他の子クラスではない場合、指定されたコレクションを持たないクラスのインスタンスは、親クラスに関連付けられたコレクションに保存されます。
注意
子クラスのターゲットコレクションを変更すると、そのクラスのインスタンスは親クラスのクエリの結果に表示されません。