Sử dụng MongoDB với gem MongoID
I. Giới thiệu 1. Mongodb là gì? - Hiểu một cách nôm na thì MongoDB là một mã nguồn mở và là một tập tài liệu dùng cơ chế NoSQL để truy vấn, nó được viết bởi ngôn ngữ C++. Chính vì được viết bởi C++ nên nó có khả năng tính toán với tốc độ cao chứ không giống như các hệ quản trị CSDL hiện nay. - ...
I. Giới thiệu
1. Mongodb là gì?
- Hiểu một cách nôm na thì MongoDB là một mã nguồn mở và là một tập tài liệu dùng cơ chế NoSQL để truy vấn, nó được viết bởi ngôn ngữ C++. Chính vì được viết bởi C++ nên nó có khả năng tính toán với tốc độ cao chứ không giống như các hệ quản trị CSDL hiện nay.
- Document trong MongoDB có cấu trúc tương tự như kiểu dữ liệu JSON, nghĩa là sẽ có các cặp (key => giá trị) nên nó có tính năng động rất lớn. Document ta có thể hiểu nó giống như các record dữ liệu trong MYSQL, tuy nhiên nó có sự khác biệt là các cặp (key => value) có thể không giống nhau ở mỗi document.
- MongoDB có những ưu điểm sau đây:
- Dễ học, có một số nét khá giống với CSDL quan hệ – Quản lý bằng command line hoặc bằng GUI như RockMongo hoặc phpMoAdmin
- Linh động, không cần phải định nghĩa cấu trúc dữ liệu trước khi tiến hành lưu trữ nó -> rất tốt khi ta cần làm việc với các dạng dữ liệu không có cấu trúc.
- Khả năng mở rộng tốt (distributed horizontally), khả năng cân bằng tải cao, tích hợp các công nghệ quản lý dữ liệu vẫn tốt khi kích thước và thông lượng trao đổi dữ liệu tăng.
- Miễn phí
2. Mongoid - Mongoid là 1 Object-Document-Mapper(ODM) cho MongoDB được viết bằng Ruby. Nó được hình tháng vào 8/2009 bởi Durran Jordan. - Triết lý của Mongoid là cung cấp một API quen thuộc với các nhà phát triển của Ruby đã sử dụng Active Record hoặc dữ liệu Mapper, trong khi tận dụng sức mạnh của schema MongoDB ít hơn và thiết kế dựa trên tài liệu, hiệu suất, truy vấn động, và hoạt động sửa đổi.<br> Cách cài đặt: - MongoId được đóng gói như một gem, và nó được lưu trữ Rubygems. Nó có thể được cài đặt bằng tay hoặc với bundler. + Bằng tay
$ gem install mongoid
+ Bằng Gemfile<br> Add vào Gemfile
gem "mongoid"
Sau đó sử dụng bundle
$ bundle install
- Khả năng tương thích với các version của Ruby Ruby Version|2.4.x|2.6.x|3.0.x --- | --- | --- MRI 1.8.x|No|No|No MRI 1.9.x|Yes|Yes|Yes MRI 2.0.x|Yes|Yes|Yes MRI 2.1.x|Yes|Yes|Yes MRI 2.2.x|Yes|Yes|Yes JRuby 1.7.x|Yes|Yes|Yes - Config mongodb cho rails app + Cài đặt mongodb xem tại http://docs.mongodb.org/master/installation/ + Sinh file config bằng cách chạy lệnh
$ rails g mongoid:config
sẽ tạo ra file `../config/mongoid.yml`
default: &default database: <%= ENV["MONGODB_NAME"] %> hosts: - <%= ENV["MONGODB_HOST"] %> development: sessions: default: <<: *default options: options: test: sessions: default: <<: *default options: read: primary max_retries: 1 retry_interval: 0 production: sessions: default: database: two hosts: - mongo_pri.local:27017 - mongo_sec.local:27017 options: read: :primary_preferred staging: sessions: default: <<: *default options:
- Logging + Config Logging level
Mongoid.logger.level = Logger::DEBUG Mongo::Logger.logger.level = Logger::DEBUG
Logging level Debug là mặc định của mogoid - Documents + Documents là những đối tượng cốt lõi trong Mongoid và bất kỳ đối tượng muốn tồn tại phải được include `Mongoid::Document`. Đại diện của Documents trong MongoDB là BSON nó cũng tương tự như HASH trong ruby hoặc đối tượng JSON. - Storage + Khai báo 1 storage trong rails
class UserLastLogin include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia field :last_login_date, type: Time field :user_id, type: Integer index({user_id: 1}, {name: "user_id_index"}) end
Khi một document được lưu trữ trong cơ sở dữ liệu đối tượng ruby sẽ xếp theo thứ tự vào BSON và có một cấu trúc như vậy:
{ "_id" : ObjectId("55f6728f6672615edc000000"), "deleted_at" : null, "user_id" : 21, "last_login_date" : ISODate("2015-09-14T07:09:03.420Z"), "updated_at" : ISODate("2015-09-14T07:09:03.421Z"), "created_at" : ISODate("2015-09-14T07:09:03.421Z") }
- Mặc dù MongoDB là một cơ sở dữ liệu schemaless, hầu hết sử dụng sẽ được với các ứng dụng web, nơi tham số đến với các server như chuỗi kí tự. Mongoid cung cấp một cơ chế cho việc chuyển đổi chuỗi kí tự vào loại thích hợp của họ thông qua các định nghĩa của các fields trong Document::Mongoid.
class User include Mongoid::Document field :first_name, type: String field :last_name, type: String field :full_name, type: String end
- Danh sách các kiểu dữ liệu được định nghĩa
Array BigDecimal Boolean Date DateTime Float Hash Integer BSON::ObjectId BSON::Binary Range Regexp String Symbol Time TimeWithZone
- Nếu như không quyết định kiểu dữ liệu cho các fiedls, Mongoid sẽ đối xử với nó như là một đối tượng và không cố gắng để định kiểu đó khi gửi các giá trị cơ sở dữ liệu - Get Set gía trị field
pry(main)> user_last_login.user_id => 1075 pry(main)> user_last_login[:user_id] => 1075 pry(main)> user_last_login.read_attribute :user_id => 1075
pry(main)> user_last_login.user_id = 123 => 123 pry(main)> user_last_login[:user_id] = 123 => 123 pry(main)> user_last_login.write_attribute(:user_id) = 123 => 123
- Hash Fields Khi sử dụng loại Hash, nên chú ý những keyword của MongoDB, nếu không các giá trị sẽ không lưu trữ đúng.
class User include Mongoid::Document field :first_name, type: String field :last_name, type: String field :full_name, type: String field :address, type: Hash end
pry(main)> User.create first_name: "First Name", last_name: "Last Name", address: {city: "Ha Noi", country: "Viet Nam"} MOPED: 127.0.0.1:27017 INSERT database=two_development collection=users documents=[{"_id"=><BSON::ObjectId:0x38280040 data=55fb70ce66726111c2000000>, "deleted_at"=>nil, "first_name"=>"First Name", "last_name"=>"Last Name", "address"=>{:city=>"Ha Noi", :country=>"Viet Nam"}, "updated_at"=>2015-09-18 02:02:54 UTC, "created_at"=>2015-09-18 02:02:54 UTC}] flags=[] COMMAND database=two_development command={:getlasterror=>1, :w=>1} runtime: 0.9666ms => #<User _id: 55fb70ce66726111c2000000, created_at: 2015-09-18 02:02:54 UTC, updated_at: 2015-09-18 02:02:54 UTC, deleted_at: nil, first_name: "First Name", last_name: "Last Name", address: {:city=>"Ha Noi", :country=>"Viet Nam"}>
- Gía trị mặc định của field
class User include Mongoid::Document field :first_name, type: String field :last_name, type: String field :full_name, type: String field :address, type: Hash field :age, type: Integer, default: 18 end
pry(main)> User.create first_name: "First Name", last_name: "Last Name", address: {city: "Ha Noi", country: "Viet Nam"} MOPED: 127.0.0.1:27017 INSERT database=two_development collection=users documents=[{"_id"=><BSON::ObjectId:0x38280040 data=55fb70ce66726111c2000000>, "deleted_at"=>nil, "first_name"=>"First Name", "last_name"=>"Last Name", "address"=>{:city=>"Ha Noi", :country=>"Viet Nam"}, "updated_at"=>2015-09-18 02:02:54 UTC, "created_at"=>2015-09-18 02:02:54 UTC}] flags=[] COMMAND database=two_development command={:getlasterror=>1, :w=>1} runtime: 0.9666ms => #<User _id: 55fb70ce66726111c2000000, created_at: 2015-09-18 02:02:54 UTC, updated_at: 2015-09-18 02:02:54 UTC, deleted_at: nil, first_name: "First Name", last_name: "Last Name", address: {:city=>"Ha Noi", :country=>"Viet Nam"}, age: 18>
- Aliasing Fields<br> Một trong những hạn chế của việc có một cơ sở dữ liệu schema-less là MongoDB phải lưu trữ tất cả thông tin trường cùng với tất cả tài liệu, có nghĩa là nó chiếm rất nhiều không gian lưu trữ trong bộ nhớ RAM và trên đĩa nhớ. Một mô hình phổ biến để hạn chế này là sử dụng alias, trong khi vẫn giữ tên miền trong các ứng dụng. Mongoid cho phép bạn làm điều này và tìm đến các fields tương ứng với tên dài hơn trong qúa trình setter, getter.
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 :ad, as: :address, type: String end
[1] pry(main)> User.create first_name: "First name", last_name: "Last name", ad: "Ha Noi" MOPED: 127.0.0.1:27017 INSERT database=two_development collection=users documents=[{"_id"=><BSON::ObjectId:0x42547800 data=5601fad366726110c2000000>, "age"=>18, "deleted_at"=>nil, "first_name"=>"First name", "last_name"=>"Last name", "ad"=>"Ha Noi", "updated_at"=>2015-09-23 01:05:23 UTC, "created_at"=>2015-09-23 01:05:23 UTC}] flags=[] COMMAND database=two_development command={:getlasterror=>1, :w=>1} runtime: 38.5406ms => #<User _id: 5601fad366726110c2000000, created_at: 2015-09-23 01:05:23 UTC, updated_at: 2015-09-23 01:05:23 UTC, deleted_at: nil, first_name: "First name", last_name: "Last name", age: 18, ad(address): "Ha Noi">
pry(main)> User.last.ad => "Ha Noi" pry(main)> User.last.address => "Ha Noi"
- Custom Ids Đối với trường hợp khi bạn không muốn có BSON::ObjectId ids, bạn có thể ghi đè lên trưòng _id Mongoid và đặt chúng vào bất cứ điều gì bạn muốn.
class Test include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia field :_id, type: Integer, default: 1 field :first_name, type: String field :last_name, type: String field :age, type: Integer, default: 18 field :ad, as: :address, type: String end
- Dynamic Fields Theo mặc định Mongoid không hỗ trợ các dynamic fields. Bạn cần phải thông báo cho Mongoid bạn muốn dynamic fields bằng cách include Mongoid::Attributes::Dynamic trong model. Mongoid::Attributes::Dynamic sẽ cho phép các thuộc tính để thiết lập và tồn tại trên documents nếu field không được xác định.
class Mash include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia include Mongoid::Attributes::Dynamic end
Khi thực hiện với các dynamic fields ta thực hiện các luật sau: + Nếu thuộc tính tồn tại trong document, Mongoid sẽ cung cấp cho bạn phương thức getter và setter chuẩn.
pry(main)> Mash.first.user_id => 11 pry(main)> Mash.first[:user_id] => 11 pry(main)> Mash.first.read_attribute(:user_id) => 11 pry(main)> Mash.first.write_attribute(:user_id, 12) => 12 pry(main)> Mash.first.user_id = 12 => 12 pry(main)> Mash.first[:user_id] = 12 => 12
+ Nếu các thuộc tính không tồn tại trên các document, Mongoid sẽ không cung cấp cho bạn với các getter và setter chuẩn và sẽ thông báo lỗi `method_missing`
pry(main)> a = Mash.new => #<Mash _id: 5602059e667261127e000001, created_at: nil, updated_at: nil, deleted_at: nil> pry(main)> a.user_id NoMethodError: undefined method `user_id' for #<Mash:0x0000000727ff68> from /home/likewise-open/FRAMGIA/bui.van.quynh/.rvm/gems/ruby-2.2.1/gems/mongoid-4.0.2/lib/mongoid/attributes/dynamic.rb:136:in `method_missing'
pry(main)> a.user_id = 11 NoMethodError: undefined method `user_id=' for #<Mash:0x0000000727ff68> from /home/likewise-open/FRAMGIA/bui.van.quynh/.rvm/gems/ruby-2.2.1/gems/mongoid-4.0.2/lib/mongoid/attributes/dynamic.rb:136:in `method_missing'
Cách getter và setter an toàn:
pry(main)> a[:user_id] => nil pry(main)> a.read_attribute(:user_id) => nil pry(main)> a[:user_id] = 11 => 11 pry(main)> a.write_attribute(:user_id, 11) => 11
- Localized Fields MongoId hỗ trợ các localized fields mà không cần sử dụng thêm các gem bên ngoài
class Mash include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia field :message, localize: true end
Để sử dụng localize ta set `localize: true`, Mongoid sẽ lưu trữ nó dưới dạng hash dạng key/value
pry(main)> I18n.default_locale = :en => :en pry(main)> mash = Mash.new => #<Mash _id: 56020fd36672610817000000, created_at: nil, updated_at: nil, deleted_at: nil, message: nil> pry(main)> I18n.locale = :ja => :ja pry(main)> mash.message = "新しいマッチがあります" => "新しいマッチがあります" pry(main)> mash => #<Mash _id: 56020fd36672610817000000, created_at: nil, updated_at: nil, deleted_at: nil, message: {"en"=>"new match", "ja"=>"新しいマッチがあります"}>
Bạn có thể get và set tất cả các bản dịch cùng lúc bằng cách sử dụng `_translations method`
pry(main)> mash.message_translations => {"en"=>"new match", "ja"=>"新しいマッチがあります"} /n pry(main)> mash.message_translations = {en: "get new match", message: "新しいマッチがあります"} => {:en=>"get new match", :ja=>"新しいマッチがあります"}
+ Querying Khi ta querying của trường chứa localized sử dụng Mongoid API, Mongoid sex tự động thay đổi để lấy gía trị phù hợp với locale hiện tại.
pry(main)> mash => #<Mash _id: 56020371667261127e000000, created_at: 2015-09-23 01:42:09 UTC, updated_at: 2015-09-23 03:17:50 UTC, deleted_at: nil, message: {"en"=>"new match", "ja"=>"新しいマッチがあります"}> pry(main)> I18n.locale => :en pry(main)> mash.message => "new match" pry(main)> I18n.locale = :ja => :ja pry(main)> mash.message => "新しいマッチがあります"
+ Indexing Nếu bạn sẽ querying nhiều trên trường localized, bạn nên đánh index cho từng locales mà bạn sẽ tìm kiếm.
class Mash include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia field :message, localize: true index "message.en" => 1 index "message.ja" => 1 end
II. Kết luận - MongoId là công cụ hiệu quả và dễ dàng sử dùng để kết nối và làm việc với mongodb của rails - Phần 2 giới thiệu tiếp về Runtime Persistence Options, Querying, Relations...
III.