--
You received this message because you are subscribed to the Google
Groups "MongoMapper" group.
For more options, visit this group at
http://groups.google.com/group/mongomapper?hl=en?hl=en
I thought about this problem a bunch while working on MongoMapper's association documentation.
There's the supported Many-to-Many, which works find with objects of the same class:
class ContentType
include MongoMapper::Document
key :from_content_type_ids, Array, :typecast => 'ObjectId'
many :from_content_types, :in => :from_content_type_ids
# sorry, no many-to-many inverse yet, our bad!
def to_content_types
ContentType.where(:from_content_type_ids => self.id)
end
def related_content_types
(from_content_types.to_a + to_content_types.to_a).uniq
end
end
cfrom = ContentType.create
cto = ContentType.create
cto.from_content_types << cfrom
cto.related_content_types
cfrom.related_content_types
e.g. in Mongo...
{
_id: ObjectId('abc123'),
from_content_type_ids: [
ObjectId('abc456'),
ObjectId('abc789'),
]
}
{
_id: ObjectId('abc456')
}
{
_id: ObjectId('abc789')
}
You can also roll your own join table as two one-to-many relationships:
class Relationship
include MongoMapper::Document
key :from_content_type_id, ObjectId
key :to_content_type_id, ObjectId
belongs_to :from_content_type, :class_name => 'ContentType'
belongs_to :to_content_type, :class_name => 'ContentType'
end
class ContentType
include MongoMapper::Document
many :from_relationships, :class_name => 'Relationship', :foreign_key => :from_content_type_id
many :to_relationships, :class_name => 'Relationship', :foreign_key => :to_content_type_id
def relationships
from_relationships.to_a + to_relationships.to_a
end
def from_content_types
from_content_type_ids = from_relationships.map { |r| r.from_content_type_id }
ContentType.where(:id.in => from_content_type_ids)
end
def to_content_types
to_content_type_ids = to_relationships.map { |r| r. to_content_type_id }
ContentType.where(:id.in => to_content_types)
end
def related_content_types
(from_content_types.to_a + to_content_types.to_a).uniq
end
end
r = Relationship.create
cfrom = ContentType.create
cto = ContentType.create
r.from_content_type = cfrom
r.to_content_type = cto
r.save
cfrom.relationships
cfrom.related_content_types
Or you can roll your relationship objects as embedded docs. You'd set it up like MongoMapper's standard Many-to-Many BUT instead of storing an array of id's you store an array of embedded documents. It would look like this (note that embedded docs always have their own id):
{
_id: ObjectId('abc123'),
from_content_type_ids: [
{ _id: ObjectId('e321'), from_content_type_id: ObjectId('abc456') },
{ _id: ObjectId('e654'), from_content_type_id: ObjectId('abc789') }
]
}
{
_id: ObjectId('abc456')
}
{
_id: ObjectId('abc789')
}
(Email the list again if you want to go this route and need code help...)
The advantage of embedded docs over MongoMapper's standard Many-to-Many is that you can add attributes to your relationship embedded documents to store more information about the relation, just like you can with ActiveRecord's many :through. For example, you could add a "related_at" timestamp to know when content types were linked up. BUT, if you don't need extra information about the relationship, you don't gain anything with embedded docs over MongoMapper's Many-to-Many.
Development wise, MM needs a Many-to-Many inverse first, then perhaps eager loading, and then maybe we can talk about "join tables" in Mongo. The embedded relationship documents approach is much more Mongo-y than a full out join collection. Embedded relationship document are the same number of queries as the current Many-to-Many implementation. A join collection requires an extra query because Mongo doesn't support joins. With the development pain to code it and maintain it, it might not be worth the gain. Does anyone else need join tables?
Does that answer your question?
Brian