我有两个实体,项目和用户。这些是在使用Mongoid的Rails中使用两个Document实例建模的,User和Project。
在这个系统中,一个用户可以创建一个项目,但许多用户可以关注许多项目。例如,作为user_id 1,我创建了project_id 1.但是user_ids 10,11,40和60都遵循project_id 1.我需要表示用户和项目之间的多对多关系,并将特定的user_id表示为项目的创建者,为他分配编辑权。
实际上,当用户登录时,他需要能够看到他正在关注的所有项目,包括他创建的任何项目,与其他用户创建的其他项目混合。他的特殊创造者地位根本不会影响这个名单。当用户查看特定项目时,他需要能够查看项目后的所有用户,如果他是创建者,他可以添加新关注者并删除现有关注者。
在RDBMS中,我会用表users
,projects
和users_projects
联接表来表示这一点,其中标志为is_creator
。这很容易让我选择用户可以看到的项目,以及哪些用户是项目的追随者,包括哪些用户是创建者。
Mongoid支持多对多关系,但与RDBMS不同的是,我无法在关系上添加标记。相反,我想我会在creator
文档中添加projects
字段,该字段将包含返回用户文档上_id
字段的链接。
user-> projects关系可能如下所示
class User
has_and_belongs_to_many :projects
end
class Project
has_and_belongs_to_many: users
end
但我无法弄清楚如何映射creator-> created_projects关系。我相信我可以在Project
belongs_to :creator, :class_name => 'User'
中引用用户创建者,但我不确定如何设置另一方。
我如何才能在Mongoid中建模这些关系?
答案 0 :(得分:2)
第二个版本占用的空间较少,但您需要额外的查询来获取用户名等用户详细信息。对象ID有12bytes,max。文档大小为16mb,因此数组可以容纳大约130万用户ID(理论上!)..
你去:
<强> user.rb 强>
class User
# projects this user owns
has_many :projects
has_many :followed_projects,
:class_name => 'Project',
:foreign_key => :follower_ids
# Uncomment if the relation does not work
#def followed_projects
# Project.where(:follower_ids => self.id)
#end
# get projects that this user has created and projects he is following
def related_projects
Project.any_of({:user_id => self.id}, {:follower_ids => self.id})
end
end
<强> project.rb 强>
class Project
# creator
belongs_to :user
field :follower_ids, :type => Array
# adds a follower
def add_follower!(user_obj)
# adds a user uniquely to the follower_ids array
self.add_to_set(:follower_ids, user_obj.id)
end
def remove_follower!(user_obj)
# remove the user
self.pull(:follower_ids, user_obj.id)
end
end
如何使用它:
@project = Project.first
@some_user = User.last
@project.add_follower!(@some_user)
@some_user.followed_projects
@some_user.related_projects
# create hash like @ids_to_user[user_id] = user
@ids_to_users = User.find(@project.follower_ids).inject({}) {|hsh, c_user| hsh[c_user.id] = c_user; hsh}
@project.followers.each do |c_follower|
puts "I'm #{@ids_to_users[c_follower].username} and I'm following this project!"
end
@project.remove_follower!(@some_user)
答案 1 :(得分:1)
创建一个嵌入式文档,其中包含所有关注者的user_id及其用户名,因此您无需查询关注者的用户名。
好处:
缺点:
如果用户更改了自己的名字,则必须更新所有“跟进”但频率更新 与您查找以下项目的频率相比,您更改了名称;)
如果每个项目有数千名粉丝,则可达到16mb的文件限制
<强> user.rb 强>
class User
# projects this user owns
has_many :projects
def followed_projects
Project.where('followers.user_id' => self.id)
end
# get projects that this user has created and projects he is following
def related_projects
Project.any_of({:user_id => self.id}, {'followers.user_id' => self.id})
end
end
<强> project.rb 强>
class Project
# creator
belongs_to :user
embeds_many :followers
# add an index because we're going to query on this
index 'followers.user_id'
# adds a follower
# maybe you want to add some validations, preventing duplicate followers
def add_follower!(user_obj)
self.followers.create({
:user => user_obj,
:username => user_obj.username
})
end
def remove_follower!(user_obj)
self.followers.destroy_all(:conditions => {:user_id => user_obj.id})
end
end
<强> follower.rb 强>
class Follower
include Mongoid::Document
include Mongoid::Timestamps
embedded_in :project
# reference to the real user
belongs_to :user
# cache the username
field :username, :type => String
end
如何使用它:
@project = Project.first
@some_user = User.last
@project.add_follower!(@some_user)
@some_user.followed_projects
@some_user.related_projects
@project.followers.each do |c_follower|
puts "I'm #{c_follower.username} and I'm following this project!"
end
@all_follower_user_ids = @project.followers.map{|c| c.user_id}
# find a specific follower by user_id
@some_follower = @project.followers.where(:user_id => 1234)
# find a specific follower by username
@some_follower = @project.followers.where(:username => 'The Dude')
@project.remove_follower!(@some_user)
PS:如果你想要一个更简单的解决方案,你可以在项目中嵌入一个ObjectIDs(user_ids)数组,并使用原子更新$addToSet
和$pullAll
添加/删除一个关注者。但是你需要额外的查询,例如User.where(:user_id.in => @project.follower_ids)
(假设数组被称为follower_ids
)来获取所有用户及其名称;)