繁体   English   中英

不确定如何使用Mongoid中的标志建立多对多关系的模型

[英]Unsure how to model a many-to-many relationship with a flag in Mongoid

我有两个实体,项目和用户。 这些在Rails中使用Mongoid建模,并带有两个Document实例:User和Project。

在此系统中,一个用户可以创建一个项目,但是许多用户可以关注许多项目。 例如,作为user_id 1,我创建了project_id1。但是user_id 10、11、40和60都遵循project_id1。我需要表示用户和项目之间的多对多关系,并表示一个特定的user_id为项目的创建者,为他分配编辑权。

实际上,当用户登录时,他需要能够看到他正在关注的所有项目,包括他创建的任何项目,与其他用户创建的其他项目混合。 他的特殊创造者地位根本不会影响这个名单。 当用户查看特定项目时,他需要能够查看关注该项目的所有用户,如果他是创建者,则可以添加新关注者并删除现有关注者。

在RDBMS中,我用表usersprojects和一个带users_projects标志的is_creator表来is_creator 这很容易让我选择用户可以看到哪些项目,以及哪些用户是项目的追随者,包括哪些用户是创建者。

Mongoid支持多对多关系,但与RDBMS不同的是,我无法在关系上添加标记。 取而代之的是,我想在projects文档中添加一个creator字段,其中将包含指向用户文档上_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中的这些关系?

第二个版本使用较少的空间,但您需要额外的查询来获取用户名等用户详细信息。 对象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)

创建一个嵌入式文档,其中包含所有关注者的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 )来获取所有用户及其名称;)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM