Edited at

Mongoのリレーションについてまとめてみた

More than 3 years have passed since last update.


通常のリレーション


1対1


モデル作成

rails g model has_one name:string

rails g model belongs_to name:string


has_one

class HasOne

include Mongoid::Document
field :name, type: String

has_one :belongs_to
end



belongs_to

class BelongsTo

include Mongoid::Document
field :name, type: String

# inverse_ofの指定がないと BelonsTo.first.has_oneの検索が行えない
belongs_to :has_one, :inverse_of => :belongs_to
end



検索


RailsConsole

# HasOneに紐づくBelongsToを取得

HasOne.first.belongs_to
# BelongsToに紐づくHasOneを取得
BelongsTo.first.has_one


追加


RailsConsole

# 個別に作成

HasOne.create(name: "HasOne name")
BelongsTo.create(name: "belongs_to")

# いっぺんに作成
HasOne.create(name: "HasOne name").belongs_to = BelongsTo.create(name: "belongs_to name")



1対多

本と本棚の関連を1対多で表してみる。


モデル作成


Railsコマンド

rails g model book_shelf name:string price:integer

rails g model book name:string price:integer


検索


RailsConsole

# 本棚が複数の本を所持しているので、Arrayで帰ってくる

book_shelf = BookShelf.first.books
# 指定の本がしまってある本棚を取得
books = Book.first.book_shelf


追加


RailsConsole

book_shelf = BookShelf.create(name: "book shelf name", price: 12000)

book_shelf.books.create(name: "book name one", price: 1200)
book_shelf.books.create(name: "book name two", price: 500)
# これでも追加できる
book_shelf.books << Book.create(name: "book name three", price: 300)


多対多

使う場面としてはRDBの中間テーブルを作るような場合に多対多(has_and_belongs_to_many)で定義する。

記事に紐づくカテゴリを例に作成してみる。


モデル作成


Railsコマンド

rails g model Article title:string text:string

rails g model Category name:string slang:string


article.rb

class Article

include Mongoid::Document
field :title, type: String
field :text, type: String

has_and_belongs_to_many :categories
end



category.rb

class Category

include Mongoid::Document
field :name, type: String
field :slang, type: String

has_and_belongs_to_many :articles
end



検索


RailsConsole

# Categoryに紐づくArticleを取得

Category.first.articles
# Articleに紐づくCategoryを取得
Article.first.categories


追加


RailsConsole

# Articleと紐づくCategoryを同時に新規追加

Article.create(title: "article title 1", text: "article text 1").categories.create(name: "category name", slang: "category slang")

# Articleは新規追加を行い既存のCategoryを紐付ける
Article.create(title: "article title 2", text: "article text 2").categories << Category.first


# Article

> db.articles.find().forEach(printjson)
{
"_id" : ObjectId("5638eb5a892bea2b5a000000"),
"title" : "article title 1",
"text" : "article text 1",
"category_ids" : [
ObjectId("5638eb5a892bea2b5a000001")
]
}
{
"_id" : ObjectId("5638ee0d892bea2b5a000002"),
"title" : "article title 2",
"text" : "article text 2",
"category_ids" : [
ObjectId("5638eb5a892bea2b5a000001")
]
}

# Category
> db.categories.find().forEach(printjson)
{
"_id" : ObjectId("5638eb5a892bea2b5a000001"),
"name" : "category name",
"slang" : "category slang",
"article_ids" : [
ObjectId("5638eb5a892bea2b5a000000"),
ObjectId("5638ee0d892bea2b5a000002")
]
}


継承

家族の関連を継承で表してみる。


クラス構造

Family (has_many)


Person (belongs_to)
├── Parent
│ ├── Father
│ └── Mother
└── Child
   ├── Brother
   └── Sister

FamilyとPersonは1対多の関係でリレーションが結ばれていて、Person以下は上位のクラスを継承している


モデル作成


Railsコマンド

rails g model Family family_name:string

rails g model Person name:string age:integer
rails g model Parent
rails g model Father
rails g model Mother
rails g model Child
rails g model Brother
rails g model Sister


family

class Family

include Mongoid::Document
field :family_name, type: String
has_many :people

# familyとparentのリレーションを宣言しないと Family.first.parents のように呼べない
has_many :parents
has_many :children
has_many :fathers
has_many :mothers
has_many :sisters
has_many :brothers
end



person

class Person

include Mongoid::Document
field :name, type: String
field :age, type: Integer

belongs_to :family
end



parent

class Parent < Person

include Mongoid::Document
end


mother

class Mother < Parent

include Mongoid::Document
end


father

class Father < Parent

include Mongoid::Document
end


child

class Child < Person

include Mongoid::Document
end


sister

class Sister < Child

include Mongoid::Document
end


brother

class Brother < Child

include Mongoid::Document
end


検索


RailsConsole

# 指定の家族全員を取得

Family.first.people
# 指定の家族の親のみ取得
Family.first.parents
# 指定の家族の子供のみ取得
Family.first.children
# 指定の家族の兄弟のみ取得
Family.first.brothers


追加


newを使う方法


RailsConsole

# 家族を追加

family = Family.create(family_name: "Yamada")

# 家族の人を追加
father = Father.new(name: "father_name", age: 30)
mother = Mother.new(name: "mother_name", age: 29)
brother1 = Brother.new(name: "first_brother_name", age: 6)
brother2 = Brother.new(name: "second_brother_name", age: 4)
sister = Sister.new(name: "first_sister_name", age: 7)
family.people = [father, mother, brother1, brother2, sister]


# families

> db.families.find().forEach(printjson)
{ "_id" : ObjectId("56361e08892bea1af6000006"), "family_name" : "Yamada" }

# people
> db.people.find().forEach(printjson)
{
"_id" : ObjectId("56361e08892bea1af6000007"),
"_type" : "Father",
"name" : "father_name",
"age" : 30,
"family_id" : ObjectId("56361e08892bea1af6000006")
}
{
"_id" : ObjectId("56361e08892bea1af6000008"),
"_type" : "Mother",
"name" : "mother_name",
"age" : 29,
"family_id" : ObjectId("56361e08892bea1af6000006")
}
{
"_id" : ObjectId("56361e08892bea1af6000009"),
"_type" : "Brother",
"name" : "first_brother_name",
"age" : 6,
"family_id" : ObjectId("56361e08892bea1af6000006")
}
{
"_id" : ObjectId("56361e08892bea1af600000a"),
"_type" : "Brother",
"name" : "second_brother_name",
"age" : 4,
"family_id" : ObjectId("56361e08892bea1af6000006")
}
{
"_id" : ObjectId("56361e08892bea1af600000b"),
"_type" : "Sister",
"name" : "first_sister_name",
"age" : 7,
"family_id" : ObjectId("56361e08892bea1af6000006")
}


createを使う方法


RailsConsole

Family.first.sisters.create(name: "build_sister_name", age: 5)



埋め込み型のリレーション


埋め込み (embedds)


モデル作成

rails g model parents name:string

rails g model children parent:references name:string


model/parent.rb

class Parent

include Mongoid::Document
field :name, type: String

embeds_many :children
end



model/child.rb

class Child

include Mongoid::Document
field :name, type: String

embedded_in :parent, :inverse_of => :children
end



追加


RailsConsole

parent = Parent.create(parent_name: "parent_name", parent_age: 24)

# embedds_manyなのでArrayを追加する
parent.children << [
Child.new(child_name: "child_name_1", child_age: 1),
Child.new(child_name: "child_name_2", child_age: 2)
]

> db.parents.find().forEach(printjson)

{
"_id" : ObjectId("562fc7c0892bea15e2000000"),
"parent_name" : "parent_name",
"parent_age" : 24,
"children" : [
{
"_id" : ObjectId("562fc973892bea16cf000000"),
"child_name" : "child_name_1",
"child_age" : 1
},
{
"_id" : ObjectId("562fc978892bea16cf000001"),
"child_name" : "child_name_2",
"child_age" : 2
}
]
}


更新

# childrenはArrayなので、firstやfindでChildクラスを取得してからsave

child = Parent.first.children.first
child.child_name = "child_name_save"
child.save

# update_attributesでも可能
Parent.first.children.first.update_attributes(child_name: "child_name_update", child_age: 1)


削除


RailsConsole

# childrenはArrayなので、firstやfindでChildクラスを取得してからdelete

Parent.first.children.first.delete