Docs Menu

継承

このガイドでは、Mongoid モデルに継承を実装する方法を学習できます。継承により、1 つの「親」クラスの特性を 1 つ以上の「子」クラスに適用できます。

Mongoid は、トップレベル および 埋め込みドキュメントの継承をサポートしています。 子モデルクラスが親クラスから継承する場合、Mongoid は親クラスのフィールド、関連付け、検証、スコープを子クラスにコピーします。

子モデルクラスを作成するときは、< 文字を使用して、指定された親クラスからの継承を実装します。 次のモデル クラスは、PersonEmployeeManager モデル間で親クラスと子クラスを作成する方法を示しています。

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 は、PersonEmployeeManager のインスタンスを 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 メソッドを使用して EmployeeManager のインスタンスを 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 は、保存されたドキュメントに弁別子フィールドを引き続き追加します。

一部の子クラスに代替ターゲットコレクションを設定し、他の子クラスではない場合、指定されたコレクションを持たないクラスのインスタンスは、親クラスに関連付けられたコレクションに保存されます。

注意

子クラスのターゲットコレクションを変更すると、そのクラスのインスタンスは親クラスのクエリの結果に表示されません。

操作のターゲットコレクションの構成の詳細については、 「永続性構成ガイド」を参照してください。