ActiveRecord vs Mongoid

      Иван Немытченко, 7bits, Омск
PHP → Ruby on Rails →freelance →
JazzCloud → Tulp → 7bits
2007 - Ruby on Rails
2007 - Ruby on Rails
2008 - HAML/SASS
2007 - Ruby on Rails
2008 - HAML/SASS
2010 - Coffeescript
2007 - Ruby on Rails
2008 - HAML/SASS
2010 - Coffeescript
2010 - Tequila → RABL
2007 - Ruby on Rails
2008 - HAML/SASS
2010 - Coffeescript
2010 - Tequila → RABL
2011 - Tulp
2012 - MongoDB
2007 - Ruby on Rails
2008 - HAML/SASS
2010 - Coffeescript
2010 - Tequila → RABL
2012 - ActiveRecord vs Mongoid
MongoDB
MongoDB is a scalable, high-performance, open
“   source NoSQL database
                          ”
НЕТ ТРАНЗАКЦИЙ
НЕТ ТРАНЗАКЦИЙ
НЕТ ДЖОЙНОВ
Таблицы → Коллекции
Таблицы → Коллекции
Строки → Документы
{ "_id" : ObjectId( "4fb8dd1324ec7c1344000003" ),
  "agent" : "Ivan Nemytchenko",
  "challenge" : "Introduce MongoDB documents",
  "status" : "accepted"
  }
{ "_id" : ObjectId( "4fb8dd1324ec7c1344000003" ),
  "agent" : "Ivan Nemytchenko",
  "missions": [
    { "challenge" : "Introduce MongoDB documents",
       "status" : "done"
    },
    { "challenge" : "Tell the truth about MongoDB",
       "status" : "considering"
    }
  ]
  }
Таблицы → Коллекции
Строки → Документы
Индексы → Индексы
Mongoid
Object Document Mapper
“   The philosophy of Mongoid is to provide a
    familiar API to Ruby developers who have been
    using Active Record or Data Mapper, while
    leveraging the power of MongoDB's schemaless
    and performant document-based design, dynamic
    queries, and atomic modifier operations
                                           ”
                             Durran Jordan, автор Mongoid
Связи
has_many
 has_one
belongs_to
has_many
       has_one
      belongs_to

has_and_belongs_to_many
has_many
                      has_one
                     belongs_to

               has_and_belongs_to_many


        address:                organization:
{organization_ids:[1,2,3]} {address_ids:[5,3,1,10]}
has_many
       has_one
      belongs_to

has_and_belongs_to_many

   has_many :through
has_many
       has_one
      belongs_to

has_and_belongs_to_many

   has_many :through
has_many
       has_one
      belongs_to

has_and_belongs_to_many

   has_many :through

     embeds_many
      embeds_one
     embedded_in
Lets Practice!
class Contact                              class Place
  include Mongoid::Document                  include Mongoid::Document
  field :type                                embeds_many :contacts
  field :value                             end
  embedded_in :place
end
place.contacts << Contact.new(:type => "phone", :value => "32-14-90")
place.contacts << Contact.new(:type => "email", :value => "omskps1@rosinter.ru")
place.save
{ "_id" : 1,
  "name" : "Планета Суши"
  "contacts" : [
    { "_id" : 1,
      "type" : "phone", "value" : "32-14-90" },
    { "_id" : 2,
      "type" : "email", "value" : "omskps1@rosinter.ru" }
    ]
  }
{ "_id" : 1,
  "name" : "Планета Суши"
  "contacts" : [
    { "_id" : 1,
      "_type" : "phone", "value" : "32-14-90" },
    { "_id" : 2,
      "_type" : "email", "value" : "omskps1@rosinter.ru" }
    ]
  }
class Contact
                                  class Place
  include Mongoid::Document
                                    include Mongoid::Document
  field :type
                                    embeds_many :contacts
  field :value
                                  end
  embedded_in :place
end                              class Email < Contact
                                 end
                                  class Phone < Contact
                                  end

place.contacts << Phone.new(:value => "32-14-90")
place.contacts << Email.new(:value => "omskps1@rosinter.ru")
place.save
Rating                   RatingType
  embedded_in :place       embedded_in :category

Place                    Category
  embeds_many :ratings     embeds_many :rating_types
Place
{ "_id" : 1,
  "name" : "Планета Суши"
  "ratings" : [
    { "_id":1, "type":"Качество обслуживания", "value":4 },
    { "_id":2, "type":"Чистота", "value":5 } ]
  }

Rating                          RatingType
  embedded_in :place              embedded_in :category

Place                           Category
  embeds_many :ratings            embeds_many :rating_types
Place
{ "_id" : 1,
  "name" : "Планета Суши"
  "ratings" : [
    { "_id":1, "type":"Качество обслуживания", "value":4 },
    { "_id":2, "type":"Чистота", "value":5 } ]
  }
                                                      Category
{ "_id" : 1,
  "name" : "Рестораны"
  "rating_types" : ["Качество обслуживания", "Чистота"]
  }

Rating                          RatingType
  embedded_in :place              embedded_in :category

Place                           Category
  embeds_many :ratings            embeds_many :rating_types
Place
{ "_id" : 1,
  "name" : "Планета Суши"
  "ratings" : [
    { "_id":1, "type":"Качество обслуживания", "value":4 },
    { "_id":2, "type":"Чистота", "value":5 } ]
  }
                                                      Category
{ "_id" : 1,
  "name" : "Рестораны"
  "rating_types" : ["Качество обслуживания", "Чистота"]
  }




Place.where(:"ratings.type"=>"Чистота", :"ratings.value.gt"=>3)
Answer
  embedded_in :question

Question                   Property
  embedded_in :category      embedded_in :place
  embeds_many :answers       field :question
                             field :answer
Category                   Place
  embeds_many :questions     embeds_many :properties
>> category.questions << Question.new(:value =>
'WiFi', :answers => ['есть', 'нету'])

>> place.properties << Property.new(:question =>
'WiFi', :answer => 'есть')
Place
{ "_id" : 1,
  "name" : ".."
  "contacts" : [
    { "_type" : "phone", "value" : "32-14-90" },
    { "_type" : "email", "value" : "..." }],
  "ratings" : [
    { "type" : "..", "value" : ".." },
    { "type" : "..", "value" : ".." }],
  "properties" : [
    { "question" : "..", "answer" : ".." }],
  "categories" : ["..", ".."],
  "review_ids" : [12, 34, 56]
  }
                                                      Review
{ "_id" : 12,
  "user_id" : 16,
  "place_id" : 1,
  "body" : “..”,
  "comments" : [
    { "body" : "..",   "user_id" : 1, "user_name" : ".." },
    { "body" : "..",   "user_id" : 5, "user_name" : ".."}],
  "ratings" : [
    { "type" : "..",   "value" : ".." },
    { "type" : "..",   "value" : ".." }]
}
Place            Review
  Contacts        AuthorSummary
  Ratings         Ratings
  Properties      Comments
  Categories →
                 User
 Category         Reviews →
  RatingTypes
  Questions
   Answers
To embed or not to embed?

      Соотношение запись/чтение
        Жизненный цикл объекта
   Так ли важна целостность данных?
Extra Stuff
GridFS

     CarrierWave.configure do |config|
       config.storage = :grid_fs
       config.grid_fs_connection = Mongoid.database
       config.grid_fs_host = app_config['domain']
       config.grid_fs_access_url = "/gridfs"
     end


     class PhotoUploader < CarrierWave::Uploader::Base
       storage :grid_fs
     end




gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
Локализация
class Person
  include Mongoid::Document

  field :first_name, :localize => true
  field :last_name, :localize => true
end




{ "_id" : 1,
   "first_name" : { "ru" : "Иван" },
   "last_name" : { "ru" : "Немытченко" }
 }
Полный фарш
class Person
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Versioning
  include Mongoid::Paranoia

  field :first_name
  field :last_name
  key :first_name, :last_name
end
Гибридные приложения

class TodoLog                        class Todo < ActiveRecord::Base
  include Mongoid::Document            after_create :create_log
  field :todo_id, :type => Integer     after_update :create_log
  field :title, :type => String
  field :done, :type => Boolean        private
end                                    def create_log
                                         TodoLog.create(
                                           :title => title,
                                           :done => !!done,
                                           :todo_id => id
                                         )
                                       end
                                     end




    https://github.com/a2ikm/activerecord-and-mongoid-sample
Анти-mongoid


    This tool exposes simplicity and power of
“   MongoDB and leverages its differences
                                               ”
                        Алексей Петрушин, автор MongoModel




http://alexeypetrushin.github.com/mongodb_model/index.html
Спасибо. Давайте поговорим.




         @inem
       inem@bk.ru

ActiveRecord vs Mongoid

  • 1.
    ActiveRecord vs Mongoid Иван Немытченко, 7bits, Омск
  • 2.
    PHP → Rubyon Rails →freelance → JazzCloud → Tulp → 7bits
  • 3.
    2007 - Rubyon Rails
  • 4.
    2007 - Rubyon Rails 2008 - HAML/SASS
  • 5.
    2007 - Rubyon Rails 2008 - HAML/SASS 2010 - Coffeescript
  • 6.
    2007 - Rubyon Rails 2008 - HAML/SASS 2010 - Coffeescript 2010 - Tequila → RABL
  • 7.
    2007 - Rubyon Rails 2008 - HAML/SASS 2010 - Coffeescript 2010 - Tequila → RABL 2011 - Tulp 2012 - MongoDB
  • 8.
    2007 - Rubyon Rails 2008 - HAML/SASS 2010 - Coffeescript 2010 - Tequila → RABL 2012 - ActiveRecord vs Mongoid
  • 9.
  • 10.
    MongoDB is ascalable, high-performance, open “ source NoSQL database ”
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
    { "_id" :ObjectId( "4fb8dd1324ec7c1344000003" ), "agent" : "Ivan Nemytchenko", "challenge" : "Introduce MongoDB documents", "status" : "accepted" }
  • 16.
    { "_id" :ObjectId( "4fb8dd1324ec7c1344000003" ), "agent" : "Ivan Nemytchenko", "missions": [ { "challenge" : "Introduce MongoDB documents", "status" : "done" }, { "challenge" : "Tell the truth about MongoDB", "status" : "considering" } ] }
  • 17.
    Таблицы → Коллекции Строки→ Документы Индексы → Индексы
  • 18.
  • 19.
    The philosophy of Mongoid is to provide a familiar API to Ruby developers who have been using Active Record or Data Mapper, while leveraging the power of MongoDB's schemaless and performant document-based design, dynamic queries, and atomic modifier operations ” Durran Jordan, автор Mongoid
  • 20.
  • 21.
  • 22.
    has_many has_one belongs_to has_and_belongs_to_many
  • 23.
    has_many has_one belongs_to has_and_belongs_to_many address: organization: {organization_ids:[1,2,3]} {address_ids:[5,3,1,10]}
  • 24.
    has_many has_one belongs_to has_and_belongs_to_many has_many :through
  • 25.
    has_many has_one belongs_to has_and_belongs_to_many has_many :through
  • 26.
    has_many has_one belongs_to has_and_belongs_to_many has_many :through embeds_many embeds_one embedded_in
  • 27.
  • 35.
    class Contact class Place include Mongoid::Document include Mongoid::Document field :type embeds_many :contacts field :value end embedded_in :place end place.contacts << Contact.new(:type => "phone", :value => "32-14-90") place.contacts << Contact.new(:type => "email", :value => "omskps1@rosinter.ru") place.save
  • 36.
    { "_id" :1, "name" : "Планета Суши" "contacts" : [ { "_id" : 1, "type" : "phone", "value" : "32-14-90" }, { "_id" : 2, "type" : "email", "value" : "omskps1@rosinter.ru" } ] }
  • 37.
    { "_id" :1, "name" : "Планета Суши" "contacts" : [ { "_id" : 1, "_type" : "phone", "value" : "32-14-90" }, { "_id" : 2, "_type" : "email", "value" : "omskps1@rosinter.ru" } ] }
  • 38.
    class Contact class Place include Mongoid::Document include Mongoid::Document field :type embeds_many :contacts field :value end embedded_in :place end class Email < Contact end class Phone < Contact end place.contacts << Phone.new(:value => "32-14-90") place.contacts << Email.new(:value => "omskps1@rosinter.ru") place.save
  • 42.
    Rating RatingType embedded_in :place embedded_in :category Place Category embeds_many :ratings embeds_many :rating_types
  • 43.
    Place { "_id" :1, "name" : "Планета Суши" "ratings" : [ { "_id":1, "type":"Качество обслуживания", "value":4 }, { "_id":2, "type":"Чистота", "value":5 } ] } Rating RatingType embedded_in :place embedded_in :category Place Category embeds_many :ratings embeds_many :rating_types
  • 44.
    Place { "_id" :1, "name" : "Планета Суши" "ratings" : [ { "_id":1, "type":"Качество обслуживания", "value":4 }, { "_id":2, "type":"Чистота", "value":5 } ] } Category { "_id" : 1, "name" : "Рестораны" "rating_types" : ["Качество обслуживания", "Чистота"] } Rating RatingType embedded_in :place embedded_in :category Place Category embeds_many :ratings embeds_many :rating_types
  • 45.
    Place { "_id" :1, "name" : "Планета Суши" "ratings" : [ { "_id":1, "type":"Качество обслуживания", "value":4 }, { "_id":2, "type":"Чистота", "value":5 } ] } Category { "_id" : 1, "name" : "Рестораны" "rating_types" : ["Качество обслуживания", "Чистота"] } Place.where(:"ratings.type"=>"Чистота", :"ratings.value.gt"=>3)
  • 49.
    Answer embedded_in:question Question Property embedded_in :category embedded_in :place embeds_many :answers field :question field :answer Category Place embeds_many :questions embeds_many :properties
  • 50.
    >> category.questions <<Question.new(:value => 'WiFi', :answers => ['есть', 'нету']) >> place.properties << Property.new(:question => 'WiFi', :answer => 'есть')
  • 54.
    Place { "_id" :1, "name" : ".." "contacts" : [ { "_type" : "phone", "value" : "32-14-90" }, { "_type" : "email", "value" : "..." }], "ratings" : [ { "type" : "..", "value" : ".." }, { "type" : "..", "value" : ".." }], "properties" : [ { "question" : "..", "answer" : ".." }], "categories" : ["..", ".."], "review_ids" : [12, 34, 56] } Review { "_id" : 12, "user_id" : 16, "place_id" : 1, "body" : “..”, "comments" : [ { "body" : "..", "user_id" : 1, "user_name" : ".." }, { "body" : "..", "user_id" : 5, "user_name" : ".."}], "ratings" : [ { "type" : "..", "value" : ".." }, { "type" : "..", "value" : ".." }] }
  • 55.
    Place Review Contacts AuthorSummary Ratings Ratings Properties Comments Categories → User Category Reviews → RatingTypes Questions Answers
  • 56.
    To embed ornot to embed? Соотношение запись/чтение Жизненный цикл объекта Так ли важна целостность данных?
  • 57.
  • 58.
    GridFS CarrierWave.configure do |config| config.storage = :grid_fs config.grid_fs_connection = Mongoid.database config.grid_fs_host = app_config['domain'] config.grid_fs_access_url = "/gridfs" end class PhotoUploader < CarrierWave::Uploader::Base storage :grid_fs end gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
  • 59.
    Локализация class Person include Mongoid::Document field :first_name, :localize => true field :last_name, :localize => true end { "_id" : 1, "first_name" : { "ru" : "Иван" }, "last_name" : { "ru" : "Немытченко" } }
  • 60.
    Полный фарш class Person include Mongoid::Document include Mongoid::Timestamps include Mongoid::Versioning include Mongoid::Paranoia field :first_name field :last_name key :first_name, :last_name end
  • 61.
    Гибридные приложения class TodoLog class Todo < ActiveRecord::Base include Mongoid::Document after_create :create_log field :todo_id, :type => Integer after_update :create_log field :title, :type => String field :done, :type => Boolean private end def create_log TodoLog.create( :title => title, :done => !!done, :todo_id => id ) end end https://github.com/a2ikm/activerecord-and-mongoid-sample
  • 62.
    Анти-mongoid This tool exposes simplicity and power of “ MongoDB and leverages its differences ” Алексей Петрушин, автор MongoModel http://alexeypetrushin.github.com/mongodb_model/index.html
  • 63.