开发者

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜