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