LoginSignup
25
25

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-11-04

通常のリレーション

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
25
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
25