Mongoid many to many problem
I want to store blog and its tags as separate documents.
Blog post should have tag_ids field and tag shouldn't have blog_posts_ids field.
Mongoid provides many to many relations out of the box, but it requires both documents of many to many relation to have _ids field.
class BlogPost
include Mongoid::Document
field :title
references_many :tags, :stored_as => :array, :inverse_of => :blog_posts
end
class Tag
include Mongoid::Document
field :name
# I DON'T WANT TO STORE BLOG_POS开发者_StackOverflow中文版TS_IDS IN TAG DOCUMENT
references_many :blog_posts, :stored_as => :array, :inverse_of => :tags
end
You can get around it with a method on Tag
to fake the other side of the association
class BlogPost
include Mongoid::Document
field :title
references_many :tags, :stored_as => :array, :inverse_of => :blog_posts
end
class Tag
include Mongoid::Document
field :name
def blog_posts
# will match all BlogPost records where the tag_ids array contains self.id
BlogPost.where(:tag_ids => self.id)
end
end
Obviously, this isn't as full-featured as :references_many
, but you can similarly fake other aspects of the many-to-many relation. For example if you want the ability to assign a new blog_post to a tag you can add a simple create_blog_post
method to Tag
.
For many real-world situations, this kind of approach is practical as long as you keep the methods simple and don't get carried away.
Map Reduce may be Your answer. Look in to MongoDB map reduce and use the permanent collection output for the tags
class BlogPost
include Mongoid::Document
field :title
field :tags, :type => Array
references_many :tags, :stored_as => :array, :inverse_of => :blog_posts
end
map =<<JS
function(){
if(!this.tags){
return;
}
for(index in this.tags){
emit(this.tags[index],{count:1,posts:[this.post.id]})
}
}
JS
reduce =<<JS
function(key,values){
var tagging = {count:0,posts:new Array()}
values.forEach ( function(val) {
tagging.count++;
tagging.posts.push(val.posts[0])
});
return tagging;
}
JS
BlogPost.collection.map_reduce(map,reduce,{:out => 'tags',:keeptemp => true})
The result collection is always {id,values} where {:id => TAGNAME, :value => {:count => NUMBER_OF_TIMES_TAG_IS_USED,:posts => [ARRAY_OF_BLOG_ARTICLES]}} You can create a formatl Tag class or use:
Mongoid.master.collection("tags")
http://api.mongodb.org/ruby/1.1.2/Mongo/Collection.html#map_reduce-instance_methodt
精彩评论