There was a question in the Google Group for MongoMapper, so I decided to post an answer in the form of a simple demo. You can find the source code on Github.com here.
The basic shape of the problem was this:
And the solution issues were around the multiple many-to-many associations, more or less.
For a simple one-to-many, MongoMapper has the normal:
- User has many :events
- Event belongs_to :user (its Owner)
But how to do the other associations? A given User can be involved with many events in different capacities:
- Attending
- Interested in attending
- Likes
There are different ways to tackle these many-to-many associations.
- You can use a Set (to obtain the uniqueness factor) of Users that are attending or are interested.
- You can use an Array of instance IDs (I think this is probably the more standard technique)
class Event include MongoMapper::Document key :title, :required => true key :user_id belongs_to :user # One way to do it... key :attendees, Set key :interested, Set # Another way to do it... key :like_ids, Array many :likes, :class_name => 'User', :in => :like_ids def attending(a_user) # self.push_uniq(:attendees => a_user.id) attendees << a_user.id save end def interested_in(a_user) interested << a_user.id save end ...
In which direction you allow making the association, that is up to your application’s needs. For example, above you can see that an Event instance could be messaged with the user to indicate attending or interested_in. The “likes” is immediately accessible from an Event, or I could have added a wrapper method (def likes(a_user)).
And the User class has some simple retrieval methods to see what a User likes, what they are attending, and what they are interested_in:
class User include MongoMapper::Document key :name, :required => true many :events def likes Event.where(:like_ids => id).all end def attending Event.where(:attendees => id).all end def interested_in Event.where(:interested => id).all end def likes_event(event) event.likes << self event.save! end
To see how the different styles are used, you can check out the specs. For example:
Adding users who like an Event:
it "should track interested" do expect { @event.interested_in(@jared) @event.interested_in(@sally) }.to change {@event.interested.size}.by(2) end it "should allow 'likes'" do expect { @event_2.likes << @martha }.to change {@event_2.likes.size}.by(1) end
Or from the User perspective:
it "should allow me to add an event I like" do @fred.likes_event(@event_2) @event_2.likes.size.should > 0 @fred.likes.count.should > 0 end
Or attendees, from the Event:
it "should list the events I am attending" do [@fred, @harry].each {|u| @event.attending(u)} @fred.attending.count.should > 0 end
I have a very similar use case, but am struggling with sorting related documents.
From the user view, if I wanted a sorted list of events by event date, how would I query that in plucky?
Hi Nick,
I will add some examples… but the short version is
1. To your model, add key :date, Time, :index => true
2. query like: Event.where(:user => fred, :order => “date desc”).all
or events = Event.where(:user.in => [fred, harry]).sort(:date.desc).all
Pingback: Cucumber, RSpec, and MongoMapper » Technical Debt
https://github.com/JonKernPA/mongo_examples
and check out my recent posts for more goodies