Sử dụng MongoDB với gem MongoID

Sử dụng MongoDB với gem MongoID

  1. Nested Attributes
    • Nested Attributes cung cấp cơ chế cho việc tạo mới(cập nhật) documents và các mối quan hệ trong cùng 1 action bằng cách lồng các thuộc tính của các mối quan hệ trong cùng 1 hash. Điều này rất tiện lợi cho việc create(update) dữ liệ trong cùng 1 form trên web.
    • Nested Attributes có thể được được sử dụng cho bất kì mối quan hệ nào embedded hoặc referenced. Để sử dụng tính năng này cho các quan hệ ta sử dụng marco accepts_nested_attributes_for
        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: :destroy
          accepts_nested_attributes_for :books
        end
- Lưu ý rằng khi bạn thêm `nested_attributes` cho một mối quan hệ tham chiếu, `Mongoid` sẽ tự động kích hoạt tự động lưu cho mối quan hệ đó.
  1. Document Callbacks
    • MongoId hỗ trợ những callbacks sau cho documents:
      • after_initialize
      • after_build
      • before_validation
      • after_validation
      • before_create
      • around_create
      • after_create
      • after_find
      • before_update
      • around_update
      • after_update
      • before_upsert
      • around_upsert
      • after_upsert
      • before_save
      • around_save
      • after_save
      • before_destroy
      • around_destroy
      • after_destroy
    • Callbacks có sẵn trên bất kì document nào, cho dù nó có được nhúng vào trong document hay không.
    • Lưu ý việc sử dụng callbacks cho các logic là thực thi 1 thiết kế kém, nó có thể dẫn tới những sai sót mà khó có thể gỡ được khi callback được gọi tới.
        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

          has_and_belongs_to_many :tests

          before_save :set_descripiton

          private
          def set_descripiton
            self.description ||= "description"
          end
        end
        [1] pry(main)> Book.create name: "name", author: "author", country: "en"
        => #<Book _id: 56fc7f3f66726120f9000000, created_at: 2016-03-31 01:37:03 UTC, updated_at: 2016-03-31 01:37:03 UTC, deleted_at: nil, name: "name", author: "author", description: "description", user_id: nil, country: "en">

        [2] pry(main)> Book.create name: "name", author: "author", country: "en", description: "test"
        => #<Book _id: 56fc7f5066726120f9000001, created_at: 2016-03-31 01:37:20 UTC, updated_at: 2016-03-31 01:37:20 UTC, deleted_at: nil, name: "name", author: "author", description: "test", user_id: nil, country: "en">
- `Callbacks` đến từ `Active Support`, bạn cũng có thể sử dụng cú pháp mới.
        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

          has_and_belongs_to_many :tests

          set_callback(:save, :before) do |document|
            self.description ||= "description"
          end
        end
        [1] pry(main)> Book.create name: "name", author: "author", country: "en"
        => #<Book _id: 56fc80e1667261226a000000, created_at: 2016-03-31 01:44:01 UTC, updated_at: 2016-03-31 01:44:01 UTC, deleted_at: nil, name: "name", author: "author", description: "description", user_id: nil, country: "en">
        [2] pry(main)> Book.create name: "name", author: "author", country: "en", description: "test"
        => #<Book _id: 56fc80e4667261226a000001, created_at: 2016-03-31 01:44:04 UTC, updated_at: 2016-03-31 01:44:04 UTC, deleted_at: nil, name: "name", author: "author", description: "test", user_id: nil, country: "en">
- Relation Callbacks<br>
`Mongoid` có một bộ `callbacks` được cụ thể dựa trên `relations`:
    + `after_add`
    + `after_remove`
    + `before_add`
    + `before_remove`<br>
 Mỗi khi một `document` được thêm vào hoặc xóa đi từ bất kỳ mối quan hệ sau, `callbacks` tương ứng được bắn: `embeds_many`, `has_many`, và `has_and_belongs_to_many`.<br>
 `Relation Callbacks`quy định như một tùy chọn trên các mối quan hệ. Yếu tố  `added/removed` là tham số cho phương thức bạn gọi `callback`.
            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, after_add: :update_age

              private
              def update_age book
                self.update age: 20
              end
            end
     [1] pry(main)> User.first
     => #<User _id: 56a985146672612ca3000000, created_at: 2016-01-28 03:03:56 UTC, updated_at: 2016-03-31 02:02:32 UTC, deleted_at: nil, first_name: "First Name", last_name: "Last Name", age: 18, address: ["HaNoi"]>
     [2] pry(main)> User.first.books.new name: "name", author: "auhtor", country: "en"
     => #<Book _id: 56fc861a66726125ea000000, created_at: nil, updated_at: nil, deleted_at: nil, name: "name", author: "auhtor", description: nil, country: "en", user_id: <BSON::ObjectId:0x32060100 data=56a985146672612ca3000000>>
     [3] pry(main)> User.first
     => #<User _id: 56a985146672612ca3000000, created_at: 2016-01-28 03:03:56 UTC, updated_at: 2016-03-31 02:06:18 UTC, deleted_at: nil, first_name: "First Name", last_name: "Last Name", age: 20, address: ["HaNoi"]>
  1. Validation
    • Mongoid bao gồm ActiveModel::Validations để cung cấp validations cơ bản thêm additional associateduniqueness validator.
    • Để kiểm tra 1 Active Record có hợp lệ hay không ta có thể sử dụng valid?
    [5] pry(main)>User.first.valid?
      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.0486ms
    => true
    [6] pry(main)> a = User.new
    => #<User _id: 56fc881266726125ea000001, created_at: nil, updated_at: nil, deleted_at: nil, first_name: nil, last_name: nil, age: 18, address: nil>
    [7] pry(main)> a.valid?
    => false
    [8] pry(main)> a.errors
    => #<ActiveModel::Errors:0x00000008447c00
     @base=
      #<Test _id: 56fc881266726125ea000001, created_at: nil, updated_at: nil, deleted_at: nil, first_name: nil, last_name: nil, age: 18, address: nil>,
     @messages={:last_name=>["can't be blank"]}>
  1. Indexes
    • Bạn có thể xác địnhindexestrên documentes bằng cách sử dụng macro index. Cung cấp chìa khóaindex cho Mongoid . Đối với tùy chọn bổ sung, ta thêm tham số vào hash.
        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

          index({name: 1}, {unique: true, name: "user_name_index"})
        end
      [2] pry(main)> User.collection.indexes.map{|i| puts  i.inspect}
        {"v"=>1, "key"=>{"_id"=>1}, "name"=>"_id_", "ns"=>"test_development.users"}
        {"v"=>1, "unique"=>true, "key"=>{"name"=>1}, "name"=>"user_name_index", "ns"=>"test_development.users"}
- Ta có thể định nghĩa `indexes` cho `embedded document`
        class User
          include Mongoid::Document
          include Mongoid::Timestamps
          include Mongoid::Paranoia

          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

          embeds_many :books

          index "books.name" => 1
        end
- Ta cũng có thể định nghĩa `index` cho nhiều trường
        class User
          validates :last_name, presence: true

          has_many :books

          index({name: 1, last_name: 1}, {unique: true})
        end
    [1] pry(main)> User.collection.indexes.map{|i| puts  i.inspect}
    {"v"=>1, "key"=>{"_id"=>1}, "name"=>"_id_", "ns"=>"test_development.users"}
    {"v"=>1, "unique"=>true, "key"=>{"name"=>1, "last_name"=>1}, "name"=>"name_1_last_name_1", "ns"=>"test_development.users"}
- `Indexes` cũng có thể `sparse` bằng cách thêm tham số `sparse`:
        class User
          include Mongoid::Document
          include Mongoid::Timestamps

          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

          index({name: -1}, {sparse: true})
        end
- `Indexes` có thể chạy dưới `background` trong trường hợp có thể mất thời gian do việc tạo `index`, ta thêm tham số  `background: true` vào `hash`
        class Test
          include Mongoid::Document
          include Mongoid::Timestamps

          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

          index({name: 1}, {background: true})
        end
- Ta cũng có thể định nghĩa `indexes` cho `foreign key` cho relations.
        class Book
          include Mongoid::Document
          include Mongoid::Timestamps

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

          belongs_to :user, index: true
        end
     [3] pry(main)> Book.collection.indexes.map{|i| puts  i.inspect}
    {"v"=>1, "key"=>{"_id"=>1}, "name"=>"_id_", "ns"=>"test_development.books"}
    {"v"=>1, "key"=>{"user_id"=>1}, "name"=>"user_id_1", "ns"=>"test_development.books", "background"=>true}
- Khi bạn muốn tạo `indexes` cho cơ sở dữ liệu, ta có thể sử dụng `rake task` do `Mongoid` cung cấp:
        /test$ rake db:mongoid:create_indexes
- `Mongoid` cũng cung cấp `rake task` để xóa toàn bộ `indexes` cho cơ sở dữ liệu
        /test$ rake db:mongoid:remove_indexes
  1. Rake Tasks
    • db:create: Tạo cơ sở dữ liệu mới, nếu tồn tại thì không thực hiện gì.
    • db:create_indexes: Đọc toàn bộ index được định nghĩa trong model và tạo chúng
    • db:remove_indexes: Đọc tất cả index thứ 2 và xóa toàn bộ nó
    • db:drop: xóa tất cả collection trong cơ sở dữ liệu, nếu cơ sở dữ liệu không tồn tại thông báo lỗi
    • db:migrate: Nếu version hiện tại là mới nhất thì không thực hiện, thực hiện chạy migration file
    • db:purge: Xóa toàn bộ dữ liệu bao gồm cả index của cơ sở dữ liêu
    • db:schema:load: Nếu tồn tại schema có thể lấy từ đó ra, nếu không tồn tại thì không thực hiện
    • db:seed: Chạy seed database từ db/seeds.rb
    • db:setup: tạo indexesseeds database
    • db:test:prepare: chuẩn bị database test