Sử Dụng MongoDB Với Gem MongoID Phần IV

Sử dụng MongoDB với gem MongoID Phần IV

  1. Mongoid(tiếp)
    • Relations
      • Common Behaviour Tất cả các quan hệ đều chứa đích đến, đó là những documents được đại diện, hoặc những documents, cơ sở đó là những documents liên kết ra, và các metadata cung cấp thông tin về các quan hệ.
              class Test
                  include Mongoid::Document
                  include Mongoid::Timestamps
                  include Mongoid::Paranoia
                  include Mongoid::Attributes::Dynamic

                  field :first_name, type: String
                  field :last_name, type: String
                  field :age, type: Integer, default: 18
                  field :address, type: Array
                  validates :last_name, presence: true

                  has_many :books
         end
            [9] pry(main)> User.first.books
          MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 1.1196ms
          MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "user_id"=><BSON::ObjectId:0x52474000 data=5653bead667261191d000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.8906ms
     =>[#<Book _id: 5653bf636672611948000004, created_at: 2015-11-24 01:37:39 UTC, updated_at: 2015-11-24 01:37:39 UTC, deleted_at: nil, name: "Consectetur nulla autem natus voluptatem saepe eum quia.", author: "Mrs. Hettie Jenkins", description: "Est iste aperiam blanditiis quod dolorum sint ullam.", country: nil, user_id: <BSON::ObjectId:0x52345920 data=5653bead667261191d000000>>ư
            [10] pry(main)> User.first.books.target
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.4310ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "user_id"=><BSON::ObjectId:0x51830620 data=5653bead667261191d000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.9328ms
=> [#<Book _id: 5653bf636672611948000004, created_at: 2015-11-24 01:37:39 UTC, updated_at: 2015-11-24 01:37:39 UTC, deleted_at: nil, name: "Consectetur nulla autem natus voluptatem saepe eum quia.", author: "Mrs. Hettie Jenkins", description: "Est iste aperiam blanditiis quod dolorum sint ullam.", country: nil, user_id: <BSON::ObjectId:0x51775860 data=5653bead667261191d000000>>]
      [11] pry(main)> User.first.books.base
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} runtime: 0.7714ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 1.1108ms
=> #<User _id: 5653bead667261191d000000, created_at: 2015-11-24 01:34:37 UTC, updated_at: 2015-11-24 01:34:37 UTC, deleted_at: nil, first_name: "Alfredo", last_name: "Keebler", age: 18, address: "67756 Reba Place">
	+ Mở rộng
            class User
              include Mongoid::Document
              include Mongoid::Timestamps
              include Mongoid::Paranoia
              include Mongoid::Attributes::Dynamic

              field :first_name, type: String
              field :last_name, type: String
              field :age, type: Integer, default: 18
              field :address, type: Array
              validates :last_name, presence: true

              has_many :books do
                def find_by_country country
                  where(country: country).first
                end
              end
            end
            [1] pry(main)> User.first.books.find_by_country "en"
      MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 1.1882ms
      MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x31950220 data=567a37ab6672613f3e000000>, "country"=>"en"}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.6782ms
    => #<Book _id: 567a39696672613f5f000000, created_at: 2015-12-23 06:04:25 UTC, updated_at: 2015-12-23 06:04:25 UTC, deleted_at: nil, name: "Name", author: "Author", description: "description", user_id: nil, country: "en", user_id: <BSON::ObjectId:0x30725100 data=567a37ab6672613f3e000000>>
	+ Custom Relation Names
	Bạn có thể đặt tên cho mối quan hệ của bạn bất cứ điều gì bạn thích, nhưng nếu lớp không thể được suy ra bởi `Mongoid` từ tên gọi, và không thể ở phía đối diện, bạn sẽ phải cung cấp các `macro` với một số tùy chọn bổ sung để cho `Mongoid` có thể gọi ra được chúng.
            class User
              include Mongoid::Document
              include Mongoid::Timestamps
              include Mongoid::Paranoia
              include Mongoid::Attributes::Dynamic

              field :first_name, type: String
              field :last_name, type: String
              field :age, type: Integer, default: 18
              field :address, type: Array
              validates :last_name, presence: true

              has_many :xyz, class_name: "Book"
            end
        [2] pry(main)> User.first.xyz
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.9731ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x41428280 data=567a37ab6672613f3e000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.5000ms
=> [#<Book _id: 567a39696672613f5f000000, created_at: 2015-12-23 06:04:25 UTC, updated_at: 2015-12-23 06:04:25 UTC, deleted_at: nil, name: "Name", author: "Author", description: "description", country: "en", user_id: <BSON::ObjectId:0x40116860 data=567a37ab6672613f3e000000>>, #<Book _id: 567a37fd6672613f3e000001, created_at: 2015-12-23 05:58:21 UTC, updated_at: 2015-12-23 05:58:21 UTC, deleted_at: nil, name: "Name", author: "Author", description: "description", country: nil, user_id: <BSON::ObjectId:0x40081300 data=567a37ab6672613f3e000000>>]
	+ Validations<br>
	Điều quan trọng là phải lưu ý rằng theo mặc định, `Mongoid` sẽ xác nhận con của bất kỳ mối quan hệ đó được nạp vào bộ nhớ thông qua một `validates_associated`. Các mối quan hệ rằng điều này áp dụng cho là:
        + embeds_many
        + embeds_one
        + has_many
        + has_one
        + has_and_belongs_to_many

		Nếu bạn không muốn hành vi này, bạn có thể tắt nó đi khi xác định các mối quan hệ.
            class User
              include Mongoid::Document
              include Mongoid::Timestamps
              include Mongoid::Paranoia
              include Mongoid::Attributes::Dynamic

              field :first_name, type: String
              field :last_name, type: String
              field :age, type: Integer, default: 18
              field :address, type: Array
              validates :last_name, presence: true

              has_many :books , validate: false
            end
	+ Polymorphism
	Khi 1 document con thuộc về nhiều documents cha, bạn có thể thông báo cho `Mongoid` hỗ trợ bằng cách tùy chọn thêm vào để định nghĩa về cha của nó, và các tùy trọn `polymorphic` trên lớp con. Trên lớp con một trường bổ sung sẽ được lưu trữ mà chỉ ra loại của lớp con. `Polymorphic` được cho phép trên tất cả các mối quan hệ với các trường hợp ngoại lệ của `has_and_belongs_to_many`.
            class Comment
              include Mongoid::Document
              field :content, type: String

              belongs_to :target, polymorphic: true
            end
            class Post
              include Mongoid::Document
              field :content, type: String

              has_many :comments, as: :target
            end
            class Picture
              include Mongoid::Document

              has_many :comments, as: :target
            end
        [1] pry(main)> Comment.all.to_a
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=comments selector={} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.6912ms
=> [#<Comment _id: 567b55c06672612074000001, content: "description", target_type: "Post", target_id: <BSON::ObjectId:0x35268340 data=567b55876672612074000000>>,
 #<Comment _id: 567b55cb6672612074000002, content: "description2", target_type: "Post", target_id: <BSON::ObjectId:0x35267060 data=567b55876672612074000000>>,
 #<Comment _id: 567b55cd6672612074000003, content: "description3", target_type: "Post", target_id: <BSON::ObjectId:0x35231040 data=567b55876672612074000000>>,
 #<Comment _id: 567b55e96672612074000005, content: "description3", target_type: "Picture", target_id: <BSON::ObjectId:0x35229020 data=567b55da6672612074000004>>,
 #<Comment _id: 567b55ed6672612074000006, content: "description2", target_type: "Picture", target_id: <BSON::ObjectId:0x35226940 data=567b55da6672612074000004>>,
 #<Comment _id: 567b55f06672612074000007, content: "description1", target_type: "Picture", target_id: <BSON::ObjectId:0x35224960 data=567b55da6672612074000004>>]
        [3] pry(main)> Post.first.comments.to_a
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=posts selector={"$query"=>{}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 1.0727ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=comments selector={"target_id"=><BSON::ObjectId:0x33209560 data=567b55876672612074000000>, "target_type"=>"Post"} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.7899ms
=> [#<Comment _id: 567b55c06672612074000001, content: "description", target_type: "Post", target_id: <BSON::ObjectId:0x33115140 data=567b55876672612074000000>>,
 #<Comment _id: 567b55cb6672612074000002, content: "description2", target_type: "Post", target_id: <BSON::ObjectId:0x33102300 data=567b55876672612074000000>>,
 #<Comment _id: 567b55cd6672612074000003, content: "description3", target_type: "Post", target_id: <BSON::ObjectId:0x33097560 data=567b55876672612074000000>>]
        [4] pry(main)> Picture.first.comments.to_a
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=pictures selector={"$query"=>{}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.4518ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=comments selector={"target_id"=><BSON::ObjectId:0x21141100 data=567b55da6672612074000004>, "target_type"=>"Picture"} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.3860ms
=> [#<Comment _id: 567b55e96672612074000005, content: "description3", target_type: "Picture", target_id: <BSON::ObjectId:0x20960940 data=567b55da6672612074000004>>,
 #<Comment _id: 567b55ed6672612074000006, content: "description2", target_type: "Picture", target_id: <BSON::ObjectId:0x20957120 data=567b55da6672612074000004>>,
 #<Comment _id: 567b55f06672612074000007, content: "description1", target_type: "Picture", target_id: <BSON::ObjectId:0x20884620 data=567b55da6672612074000004>>]
	+ Dependent Behaviour
	Bạn có thể cung cấp tùy chọn `dependent` vào các tham chiếu để hướng dẫn `Mongoid` cách xử lý tình huống mà một bên của mối quan hệ này sẽ bị xóa, hoặc là cố gắng để được xóa. Các tùy chọn như sau:
    	+ `:delete`: xóa documents con mà không cần chạy bất kì `model callbacks`
    	+ `:destroy`: Xóa documents con và chạy tất cả `model callbacks`
    	+ `:nullify`: Không xóa các documents con
    	+ `:restrict`: Thông báo lỗi khi child không có
               class User
                  include Mongoid::Document
                  include Mongoid::Timestamps
                  include Mongoid::Paranoia
                  include Mongoid::Attributes::Dynamic

                  field :first_name, type: String
                  field :last_name, type: String
                  field :age, type: Integer, default: 18
                  field :address, type: Array
                  validates :last_name, presence: true

                  has_many :books , dependent: :delete
                end
        [2] pry(main)> User.first.books
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 1.1021ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x36030720 data=567a37ab6672613f3e000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 17.0405ms
=> [#<Book _id: 567a39696672613f5f000000, created_at: 2015-12-23 06:04:25 UTC, updated_at: 2015-12-23 06:04:25 UTC, deleted_at: nil, name: "Name", author: "Author", description: "description", user_id: nil, country: "en", test_id: <BSON::ObjectId:0x34669480 data=567a37ab6672613f3e000000>>, #<Book _id: 567a37fd6672613f3e000001, created_at: 2015-12-23 05:58:21 UTC, updated_at: 2015-12-23 05:58:21 UTC, deleted_at: nil, name: "Name", author: "Author", description: "description", user_id: nil, country: nil, test_id: <BSON::ObjectId:0x34649740 data=567a37ab6672613f3e000000>>]
[3] pry(main)> User.first.delete
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.9939ms
  MOPED: 127.0.0.1:27017 COMMAND      database=test_development command={:count=>"books", :query=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x34041200 data=567a37ab6672613f3e000000>}} runtime: 1.1251ms
  MOPED: 127.0.0.1:27017 DELETE       database=test_development collection=books selector={"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x34041200 data=567a37ab6672613f3e000000>} flags=[]
                         COMMAND      database=test_development command={:getlasterror=>1, :w=>1} runtime: 0.6609ms
  MOPED: 127.0.0.1:27017 UPDATE       database=test_development collection=tests selector={"_id"=><BSON::ObjectId:0x34041200 data=567a37ab6672613f3e000000>} update={"$set"=>{"deleted_at"=>2015-12-24 09:34:32 +0700}} flags=[]
                         COMMAND      database=test_development command={:getlasterror=>1, :w=>1} runtime: 0.5785ms
=> true
	+ Autosaving
	Một khác biệt cốt lõi giữa `Mongoid` và `ActiveRecord` là `Mongoid` không tự động lưu lại các mối quan hệ con cho các `relational associations`. Đây là lý do hiệu suất của `Mongoid`.<br>
    Để kích hoạt một `autosave` vào một liên kết quan hệ thêm tùy chọn `autosave` đến quan hệ.<br>
    Lưu ý rằng chức năng `autosave` sẽ tự động được thêm vào một mối quan hệ khi sử dụng `accepts_nested_attributes_for` hoặc xác nhận sự hiện diện của các mối quan hệ.
        [3] pry(main)> User.first.books
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=user selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.6753ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "user_id"=><BSON::ObjectId:0x30727820 data=567b5c4866726124d1000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.3228ms
=> []
        [4] pry(main)> user = User.first
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.4337ms
=> #<User _id: 567b5c4866726124d1000000, created_at: 2015-12-24 02:45:28 UTC, updated_at: 2015-12-24 02:45:28 UTC, deleted_at: nil, first_name: "First Name", last_name: "Last Name", age: 18, address: ["HaNoi"]>
[5] pry(main)> user.books.build(name: "name", author: "author", country: "en")
=> #<Book _id: 567b5d4266726124fd000001, created_at: nil, updated_at: nil, deleted_at: nil, name: "name", author: "author", description: nil, country: "en", user_id: <BSON::ObjectId:0x29381580 data=567b5c4866726124d1000000>>
[6] pry(main)> user.save
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil, "user_id"=><BSON::ObjectId:0x29381580 data=567b5c4866726124d1000000>}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.8037ms
  MOPED: 127.0.0.1:27017 INSERT       database=test_development collection=books documents=[{"_id"=><BSON::ObjectId:0x28139260 data=567b5d4266726124fd000001>, "deleted_at"=>nil, "name"=>"name", "author"=>"author", "country"=>"en", "user_id"=><BSON::ObjectId:0x29381580 data=567b5c4866726124d1000000>, "updated_at"=>2015-12-24 02:49:42 UTC, "created_at"=>2015-12-24 02:49:42 UTC}] flags=[]
                         COMMAND      database=test_development command={:getlasterror=>1, :w=>1} runtime: 0.9621ms
=> true
[7] pry(main)> user.books
=> [#<Book _id: 567b5d4266726124fd000001, created_at: 2015-12-24 02:49:42 UTC, updated_at: 2015-12-24 02:49:42 UTC, deleted_at: nil, name: "name", author: "author", description: nil, country: "en", user_id: <BSON::ObjectId:0x29381580 data=567b5c4866726124d1000000>>]
	+ Existence Predicates<br>
	Tất cả các mối quan hệ có các vị từ sự tồn tại trên chúng trong các hình thức của `name?` và `has_name?` để kiểm tra xem các mối quan hệ có trống không.
        [3] pry(main)> User.first.has_books?
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 32.3015ms
  MOPED: 127.0.0.1:27017 COMMAND      database=test_development command={:count=>"books", :query=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x36370220 data=567b5c4866726124d1000000>}} runtime: 25.6522ms
=> true
[4] pry(main)> User.first.books?
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=users selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.5985ms
  MOPED: 127.0.0.1:27017 COMMAND      database=test_development command={:count=>"books", :query=>{"deleted_at"=>nil, "test_id"=><BSON::ObjectId:0x34686280 data=567b5c4866726124d1000000>}} runtime: 0.3523ms
=> true
    + Autobuilding<br>
    Một với một mối quan hệ (`embeds_one`, `has_one)` có một tùy chọn `autobuild` thông báo cho Mongoid để khởi tạo một tài liệu mới khi quan hệ được truy cập và nó là `nil`.
            class Test
              include Mongoid::Document
              include Mongoid::Timestamps
              include Mongoid::Paranoia
              include Mongoid::Attributes::Dynamic

              field :first_name, type: String
              field :last_name, type: String
              field :age, type: Integer, default: 18
              field :address, type: Array
              validates :last_name, presence: true

              has_one :book , dependent: :delete, autobuild: true
            end
        [2] pry(main)> user = User.new
=> #<User _id: 5689bc4c667261104b000001, created_at: nil, updated_at: nil, deleted_at: nil, first_name: nil, last_name: nil, age: 18, address: nil>
[3] pry(main)> user.book
=> #<Book _id: 5689bc4f667261104b000002, created_at: nil, updated_at: nil, deleted_at: nil, name: nil, author: nil, description: nil, country: nil, user_id: <BSON::ObjectId:0x39557320 data=5689bc4c667261104b000001>>
    + Touching<br>
   Bất kỳ mối quan hệ `belongs_to`, nó có thể bị treo,có một tùy chọn: tùy chọn `touch` sẽ gọi phương thức liên lạc vào nó và bất kỳ mối quan hệ cha với các tùy chọn định nghĩa khi các tài liệu cơ sở gọi `#touch`.
            class Book
              include Mongoid::Document
              include Mongoid::Timestamps
              include Mongoid::Paranoia
              include Mongoid::Attributes::Dynamic

              field :name, type: String
              field :author, type: String
              field :description, type: String
              field :user_id, type: Integer
              field :country, type: String

              belongs_to :user
            end
        [4] pry(main)> book = Book.first
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} runtime: 0.7424ms
  MOPED: 127.0.0.1:27017 QUERY        database=test_development collection=books selector={"$query"=>{"deleted_at"=>nil}, "$orderby"=>{"created_at"=>-1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.3885ms
=> #<Book _id: 567b5d4266726124fd000001, created_at: 2015-12-24 02:49:42 UTC, updated_at: 2015-12-24 02:49:42 UTC, deleted_at: nil, name: "name", author: "author", description: nil, user_id: nil, country: "en", test_id: <BSON::ObjectId:0x25420200 data=567b5c4866726124d1000000>>
[5] pry(main)> book.touch
  MOPED: 127.0.0.1:27017 UPDATE       database=test_development collection=books selector={"_id"=><BSON::ObjectId:0x25423040 data=567b5d4266726124fd000001>} update={"$set"=>{"updated_at"=>2016-01-04 00:32:42 UTC}} flags=[]
                         COMMAND      database=test_development command={:getlasterror=>1, :w=>1} runtime: 1.3841ms
=> true