Mongoid何时反映关系的子元素?

如果要将其添加到现有文档中

有许多

在以下模式下进行考虑。

class Article
  include Mongoid::Document

  field :title
  field :author
  has_many :comments, class_name: 'Comment'
end
class Comment
  include Mongoid::Document

  field :author
  field :text

  belongs_to :article
end

利用上述的模型,按照以下步骤进行。

    1. 创建一条文章的数据

 

    1. 创建一条评论的数据

 

    将第二步创建的数据存入文章数据的评论中
irb(main):001:0> article = Article.create(title: 'test article', author: 'John')
=> #<Article _id: 5def694121b6206de44b31f5, title: "test article", author: "John">
irb(main):002:0> comment = Comment.create(author: 'Tom', text: 'comment text')
=> #<Comment _id: 5def697321b6206de44b31f6, author: "Tom", text: "comment text", article_id: nil>
rb(main):003:0> comment
=> #<Comment _id: 5def697321b6206de44b31f6, author: "Tom", text: "comment text", article_id: nil>
irb(main):004:0> article.comments << comment
MONGODB | [8] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"comments", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def697321b6206de44b31f6'), "author"=>"Tom", "text"=>"comment text", "article_id"=>BSON::ObjectId('5def694121b6206de44b31f5')}], "lsid"=>{"id"=><BSON::Binary:0x7034371467232...
MONGODB | [8] localhost:28001 | sandbox.insert | SUCCEEDED | 0.001s
MONGODB | [9] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"comments", "filter"=>{"article_id"=>BSON::ObjectId('5def694121b6206de44b31f5')}, "lsid"=>{"id"=><BSON::Binary:0x70343714672320 type=uuid data=0x425a912cb15c4242...>}}
MONGODB | [9] localhost:28001 | sandbox.find | SUCCEEDED | 0.001s
=> [#<Comment _id: 5def697321b6206de44b31f6, author: "Tom", text: "comment text", article_id: BSON::ObjectId('5def694121b6206de44b31f5')>]

在上述 irb(main):003 的时候,MongoDB 上并没有保存。
在 irb(main):004 时,一旦将 article.comments 存储起来,就会立即插入。

> db.comments.find() // irb(main):003 のあと
> db.comments.find() // irb(main):004 のあと
{ "_id" : ObjectId("5def697321b6206de44b31f6"), "author" : "Tom", "text" : "comment text", "article_id" : ObjectId("5def694121b6206de44b31f5") }

只有一个

将模型更改为以下形式。

class Article
  include Mongoid::Document

  field :title
  has_one :author
  has_many :comments, class_name: 'Comment'
end
class Author
  include Mongoid::Document

  field :name
  field :age

  belongs_to :article
end

使用上述的模型,执行以下步骤。

    1. 创建一条文章数据

 

    1. 创建一条作者数据

 

    将数据2中创建的数据存储到文章数据中的作者字段中
irb(main):001:0> article = Article.create(title: 'second article')
=> #<Article _id: 5def73cb21b620e0c14b31f5, title: "second article">
irb(main):002:0> author = Author.create(name: 'Tom', age: 20)
=> #<Author _id: 5def73e521b620e0c14b31f6, name: "Tom", age: 20, article_id: nil>
irb(main):003:0> article.author = author
MONGODB | [8] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"authors", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def73e521b620e0c14b31f6'), "name"=>"Tom", "age"=>20, "article_id"=>BSON::ObjectId('5def73cb21b620e0c14b31f5')}], "lsid"=>{"id"=><BSON::Binary:0x70343916007920 type=uuid data...
MONGODB | [8] localhost:28001 | sandbox.insert | SUCCEEDED | 0.010s
=> #<Author _id: 5def73e521b620e0c14b31f6, name: "Tom", age: 20, article_id: BSON::ObjectId('5def73cb21b620e0c14b31f5')>

与 has_many 的例子一样,当 article.author = author 时,将文档写入到 authors 中。

> db.authors.find() // irb(main):002 のあと
> db.authors.find() // irb(main):003 のあと
{ "_id" : ObjectId("5def73e521b620e0c14b31f6"), "name" : "Tom", "age" : 20, "article_id" : ObjectId("5def73cb21b620e0c14b31f5") }

嵌入多个 duō gè)

修改模型如下(同时也修改了embeds_one的验证部分)。

class Article
  include Mongoid::Document

  field :title
  embeds_one :author
  embeds_many :comments, class_name: 'Comment'
end
class Comment
  include Mongoid::Document

  field :author
  field :text

  embedded_in :article
end

按照之前的步骤进行操作。

irb(main):001:0> article = Article.create(title: 'third article')
=> #<Article _id: 5def78c221b62011e762e62c, title: "third article">
irb(main):002:0> comment = Comment.create(author: 'Ken', text: 'new comment')
Traceback (most recent call last):
        1: from (irb):3
Mongoid::Errors::NoParent ()
message:
  Cannot persist embedded document Comment without a parent document.
summary:
  If the document is embedded, in order to be persisted it must always have a reference to its parent document. This is most likely caused by either calling Comment.create or Comment.create! without setting the parent document as an attribute.
resolution:
  Ensure that you've set the parent relation if instantiating the embedded document directly, or always create new embedded documents via the parent relation.
irb(main):003:0> comment = Comment.new(author: 'Ken', text: 'new comment')
=> #<Comment _id: 5def792a21b62011e762e62d, author: "Ken", text: "new comment">
irb(main):004:0> article.comments << comment
MONGODB | [8] localhost:28001 #1 | sandbox.update | STARTED | {"update"=>"articles", "ordered"=>true, "updates"=>[{"q"=>{"_id"=>BSON::ObjectId('5def78c221b62011e762e62c')}, "u"=>{"$push"=>{"comments"=>{"_id"=>BSON::ObjectId('5def792a21b62011e762e62d'), "author"=>"Ken", "text"=>"new comment"}}}}], "lsid"=>{"id"=>...
MONGODB | [8] localhost:28001 | sandbox.update | SUCCEEDED | 0.002s
=> [#<Comment _id: 5def792a21b62011e762e62d, author: "Ken", text: "new comment">]

由于存在embedded_in元素,因此必须指定父元素,否则Comment.create()将会出错。

当article.comments << comment发生时,会对article进行更新。

嵌入一个

将 author.rb 嵌入到 embedded_in 中。

class Author
  include Mongoid::Document

  field :name
  field :age

  embedded_in :article
end

这个也是用相同的步骤来执行。

irb(main):001:0> article = Article.create(title: 'fourth article')
=> #<Article _id: 5def7a8721b620366a62e62c, title: "fourth article">
irb(main):002:0> author = Author.create(name: 'Sam', age: 22) # 先と同様にエラーになる。エラーは省略。
irb(main):003:0> author = Author.new(name: 'Sam', age: 22)
=> #<Author _id: 5def7ab121b620366a62e62d, name: "Sam", age: 22>
irb(main):004:0> article.author = author
MONGODB | [8] localhost:28001 #1 | sandbox.update | STARTED | {"update"=>"articles", "ordered"=>true, "updates"=>[{"q"=>{"_id"=>BSON::ObjectId('5def7a8721b620366a62e62c')}, "u"=>{"$set"=>{"author"=>{"_id"=>BSON::ObjectId('5def7ab121b620366a62e62d'), "name"=>"Sam", "age"=>22}}}}], "lsid"=>{"id"=><BSON::Binary:0x7...
MONGODB | [8] localhost:28001 | sandbox.update | SUCCEEDED | 0.002s
=> #<Author _id: 5def7ab121b620366a62e62d, name: "Sam", age: 22>

在 article.author = author 的同时,也会将文档写入 author,类似于 has_one。

如果要添加新数据

模型将与上述的每个模型具有相同的形式。

由于要做的事情基本相同,所以从这里开始只需要在控制台上操作并添加注释。

有许多

irb(main):001:0> article = Article.new(title: 'fifth article', author: 'John')
=> #<Article _id: 5def7ef821b6206f8662e62c, title: "fifth article", author: "John">
irb(main):002:0> comment = Comment.create(author: 'Ken', text: 'next comment')
=> #<Comment _id: 5def7f1621b6206f8662e62d, author: "Ken", text: "next comment", article_id: nil>
irb(main):003:0> article.comments << comment # ここではどちらも保存されない
=> [#<Comment _id: 5def7f1621b6206f8662e62d, author: "Ken", text: "next comment", article_id: BSON::ObjectId('5def7ef821b6206f8662e62c')>]
irb(main):004:0> Article.where(title: 'fifth article', author: 'John').first # まだないことの確認
MONGODB | [8] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"fifth article", "author"=>"John"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [8] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> nil
irb(main):005:0> article.save
MONGODB | [9] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"articles", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def7ef821b6206f8662e62c'), "title"=>"fifth article", "author"=>"John"}], "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [9] localhost:28001 | sandbox.insert | SUCCEEDED | 0.002s
=> true
irb(main):006:0> Article.where(title: 'fifth article', author: 'John').first # 保存されたことの確認
MONGODB | [10] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"fifth article", "author"=>"John"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [10] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> #<Article _id: 5def7ef821b6206f8662e62c, title: "fifth article", author: "John">
irb(main):007:0> Comment.where(author: 'Ken', text: 'next comment').first # comment が保存されていないことの確認
MONGODB | [11] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"comments", "filter"=>{"author"=>"Ken", "text"=>"next comment"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [11] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> nil
irb(main):008:0> comment.save
MONGODB | [12] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"comments", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def7f1621b6206f8662e62d'), "author"=>"Ken", "text"=>"next comment", "article_id"=>BSON::ObjectId('5def7ef821b6206f8662e62c')}], "lsid"=>{"id"=><BSON::Binary:0x7016285296608...
MONGODB | [12] localhost:28001 | sandbox.insert | SUCCEEDED | 0.002s
=> true
irb(main):009:0> Comment.where(author: 'Ken', text: 'next comment').first # 保存されたことの確認
MONGODB | [13] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"comments", "filter"=>{"author"=>"Ken", "text"=>"next comment"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [13] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> #<Comment _id: 5def7f1621b6206f8662e62d, author: "Ken", text: "next comment", article_id: BSON::ObjectId('5def7ef821b6206f8662e62c')>
irb(main):010:0> Article.where(title: 'fifth article', author: 'John').first.comments # リレーションが成立していることの確認
MONGODB | [14] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"fifth article", "author"=>"John"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [14] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
MONGODB | [15] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"comments", "filter"=>{"article_id"=>BSON::ObjectId('5def7ef821b6206f8662e62c')}, "lsid"=>{"id"=><BSON::Binary:0x70162852966080 type=uuid data=0xa7f9242254954cfb...>}}
MONGODB | [15] localhost:28001 | sandbox.find | SUCCEEDED | 0.001s
=> [#<Comment _id: 5def7f1621b6206f8662e62d, author: "Ken", text: "next comment", article_id: BSON::ObjectId('5def7ef821b6206f8662e62c')>]

在保存文章时,即使已经添加了评论,评论本身并不会被保存。
只有同时保存两者才能建立起关联关系。

顺便说一下,即使先保存了评论,文章也不会被保存。
因此,MongoDB中,与评论关联的文章集合articles是不存在的。

有一个

irb(main):001:0> article = Article.new(title: 'sixth article')
=> #<Article _id: 5def815721b62004d962e62c, title: "sixth article">
irb(main):002:0> author = Author.create(name: 'Kenny', age: 21)
=> #<Author _id: 5def816421b62004d962e62d, name: "Kenny", age: 21, article_id: nil>
irb(main):003:0> article.author = author
=> #<Author _id: 5def816421b62004d962e62d, name: "Kenny", age: 21, article_id: BSON::ObjectId('5def815721b62004d962e62c')>
irb(main):004:0> Article.where(title: 'sixth article').first
MONGODB | [7] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"sixth article"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [7] localhost:28001 | sandbox.find | SUCCEEDED | 0.001s
=> nil
irb(main):005:0> article.save
MONGODB | [8] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"articles", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def815721b62004d962e62c'), "title"=>"sixth article"}], "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [8] localhost:28001 | sandbox.insert | SUCCEEDED | 0.001s
=> true
irb(main):006:0> Article.where(title: 'sixth article').first # article は保存されている
MONGODB | [9] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"sixth article"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [9] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> #<Article _id: 5def815721b62004d962e62c, title: "sixth article">
irb(main):007:0> Author.where(name: 'Kenny', age: 21).first # author は保存されていない
MONGODB | [10] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"authors", "filter"=>{"name"=>"Kenny", "age"=>21}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [10] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> nil
irb(main):008:0> article.author.save
MONGODB | [11] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"authors", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def816421b62004d962e62d'), "name"=>"Kenny", "age"=>21, "article_id"=>BSON::ObjectId('5def815721b62004d962e62c')}], "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid da...
MONGODB | [11] localhost:28001 | sandbox.insert | SUCCEEDED | 0.002s
=> true
irb(main):009:0> Author.where(name: 'Kenny', age: 21).first
MONGODB | [12] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"authors", "filter"=>{"name"=>"Kenny", "age"=>21}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [12] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
=> #<Author _id: 5def816421b62004d962e62d, name: "Kenny", age: 21, article_id: BSON::ObjectId('5def815721b62004d962e62c')>
irb(main):010:0> Article.where(title: 'sixth article').first.author # リレーションが成立していることの確認
MONGODB | [13] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"articles", "filter"=>{"title"=>"sixth article"}, "sort"=>{"_id"=>1}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [13] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
MONGODB | [14] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"authors", "filter"=>{"article_id"=>BSON::ObjectId('5def815721b62004d962e62c')}, "limit"=>1, "singleBatch"=>true, "lsid"=>{"id"=><BSON::Binary:0x70163658507040 type=uuid data=0xab53b65883964307...>}}
MONGODB | [14] localhost:28001 | sandbox.find | SUCCEEDED | 0.001s
=> #<Author _id: 5def816421b62004d962e62d, name: "Kenny", age: 21, article_id: BSON::ObjectId('5def815721b62004d962e62c')>

和has_many一样。

嵌入很多

irb(main):001:0> article = Article.new(title: 'seventh article')
=> #<Article _id: 5def82ee21b620184662e62c, title: "seventh article">
irb(main):002:0> comment = Comment.new(author: 'Lita', text: 'wild comment')
=> #<Comment _id: 5def832421b620184662e62d, author: "Lita", text: "wild comment">
irb(main):003:0> article.comments << comment
=> [#<Comment _id: 5def832421b620184662e62d, author: "Lita", text: "wild comment">]
irb(main):004:0> article.save # comment.save でも同じ挙動をする
MONGODB | [7] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"articles", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5def82ee21b620184662e62c'), "title"=>"seventh article", "comments"=>[{"_id"=>BSON::ObjectId('5def832421b620184662e62d'), "author"=>"Lita", "text"=>"wild comment"}]}], "lsid"...
MONGODB | [7] localhost:28001 | sandbox.insert | SUCCEEDED | 0.001s
=> true

将comment存储在article.comments中并保存,从而使得两者都在数据库中得到反映。

嵌入一个

irb(main):001:0> article = Article.new(title: 'eighth article')
=> #<Article _id: 5df0561821b6205f2062e62c, title: "eighth article">
irb(main):002:0> author = Author.new(name: 'Alex', age: 27)
=> #<Author _id: 5df0563221b6205f2062e62d, name: "Alex", age: 27>
irb(main):003:0> article.author = author
=> #<Author _id: 5df0563221b6205f2062e62d, name: "Alex", age: 27>
irb(main):004:0> article.save
MONGODB | [7] localhost:28001 #1 | sandbox.insert | STARTED | {"insert"=>"articles", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5df0561821b6205f2062e62c'), "title"=>"eighth article", "author"=>{"_id"=>BSON::ObjectId('5df0563221b6205f2062e62d'), "name"=>"Alex", "age"=>27}}], "lsid"=>{"id"=><BSON::Bina...
MONGODB | [7] localhost:28001 | sandbox.insert | SUCCEEDED | 0.001s
=> true

与embeds_many类似。

总结

如果在MongoDB中已经存在了父元素的文档,那么无论是has_*还是embeds_*,在为父元素分配/存储子元素时,将针对相应的文档进行更新。

如果父元素的文档尚未插入到MongoDB中,且关系为has_*,则保存父元素的数据而不会保存子元素,需要另外单独保存子元素。如果关系为embeds_*,无论保存父元素还是子元素,两者都会被保存。

广告
将在 10 秒后关闭
bannerAds