• Jump To … +
    assignment.rb associations.rb basics.rb callbacks.rb composite.rb conversions.rb database.rb index.rb modifiers.rb queries.rb spec.rb validations.rb
  • associations.rb

  • ¶

    Example of Associations in [Mongo Model][mongodb_model].

    MongoDB is Document Database, and unlike the Relational Database its key feature is Composite Documents. It support Associations also but there are some limitations.

    So, with MongoDB You usually use Composite Documents a lot and Associations not so much.

    According to this Mongo Model provides You with advanced tools for [Composite Models][composite], and basic only for modelling Associations.

    In this example we’ll create simple Blog Application and see how to associate Comments with the Post using one-to-many association (take a look at the [composite][composite] example to see how to embed Comments into Post).

    require 'mongo/model'
  • ¶

    Connecting to test database and cleaning it before starting.

    Mongo::Model.default_database_name = :default_test
    Mongo::Model.default_database.clear
  • ¶

    Basics

  • ¶

    Defining Post.

    class Post
      inherit Mongo::Model
  • ¶

    Storing post in posts collection.

      collection :posts
    
      attr_accessor :text
  • ¶

    Creating and returning [Query Object][queries] that can be used later to select comments belongign to this post (there’s no database call at this point).

      def comments
        Comment.where post_id: id
      end
  • ¶

    If the post will be deleted, all comments also should be deleted.

      after_delete{|post| post.comments.each(&:delete!)}
    
      def inspect; text.to_s end
    end
  • ¶

    Defining Comment.

    class Comment
      inherit Mongo::Model
  • ¶

    Storing comment in comments collection.

      collection :comments
    
      attr_accessor :text
  • ¶

    Every comment has post_id attribute with id of corresponding post.

      attr_accessor :post_id
  • ¶

    We need to ensure that comment always belongs to some post.

      validates_presence_of :post_id
  • ¶

    Adding method allowing to assign post to comment.

      def post= post
        self.post_id = post.id
        _cache[:post] = post
      end
  • ¶

    Retrieving the post this comment belongs to.

      def post
        _cache[:item] ||= Post.by_id post_id
      end
    
      def inspect; text.to_s end
    end
  • ¶

    Creating Post with Comments and saving it to database.

    post = Post.create text: 'Zerg infestation found on Tarsonis!'
    post.comments.create text: "I can't believe it."
  • ¶

    Retrieving post and comments.

    post = Post.first
    p post.text                                      # => "Zerg infestation found on Tarsonis!"
    p post.comments.count                            # => 1
    p post.comments.first.text                       # => "I can't believe it."
    p post.comments.first.post == post               # => true
  • ¶

    You can also add comments directly, without any syntaxis sugar.

    comment = Comment.new text: "Me too, but it's true."
    comment.post = post
    comment.save
    p post.comments.count                             # => 2
  • ¶

    Comments belonging to post are returned as [Query Object][queries] thus giving You access to all kind of operations.

    per_page = 2
    p post.comments.paginate(1, per_page).all         # => first page of comments
  • ¶

    After deleting the post all dependent comments also should be deleted.

    post.delete
    p Comment.count                                   # => 0
  • ¶

    Caching comments count.

    It would be nice to know how many comments does the post have withoug executing additional query to count it, let’s do this by caching it in the comments_count attribute of the post.

  • ¶

    Adding comments_count attribute to Post.

    class Post
      attr_writer :comments_count
      def comments_count; @comments_count ||= 0 end
    end
  • ¶

    Updating comments_count every time comment created and deleted. Actually we can do this by retrieving, updating and then saving the post, but let’s do it in more efficient way using [modifiers][modifiers].

    class Comment
      after_create do |comment|
        Post.update({id: comment.post_id}, {_inc: {comments_count: 1}})
      end
      after_delete do |comment|
        Post.update({id: comment.post_id}, {_inc: {comments_count: -1}})
      end
    end
  • ¶

    Now, every time comment will be created and deleted, post comments_count attribute will be updated.

    post = Post.create text: 'Zerg infestation found on Tarsonis!'
    post.comments.create text: "I can't believe it."
    post.reload
    p post.comments_count                            # => 1
    post.comments.delete_all
    post.reload
    p post.comments_count                            # => 0
  • ¶

    In this example we covered 1-to-N association, but You can implement all other types using similar technics (the only complex case is M-to-N - use array of ids to do it).

    Also, remember that MongoDB is Document Database, not Relational. If You want to get most of it – use Composite Documents whenever possible, avoid Associations and use it only if You really need it.