不确定如何在 Mongoid 中使用标志来建模多对多关系

发布于 2024-12-12 21:06:25 字数 1058 浏览 0 评论 0原文

我有两个实体:项目和用户。这些是在 Rails 中使用 Mongoid 以及两个文档实例(用户和项目)进行建模的。

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

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

在 RDBMS 中,我将使用表 usersprojects 和带有 is_creator 标志的 users_projects 连接表来表示这一点。这可以让我轻松选择用户可以看到哪些项目,以及哪些用户是项目的关注者,包括哪些用户是项目的创建者。

Mongoid 支持多对多关系,但与 RDBMS 不同,我无法在关系上添加标记。相反,我想将一个 creator 字段添加到 projects 文档中,其中将包含一个返回到 _id 字段的链接用户文档。

用户->项目关系可能看起来像这样

class User
  has_and_belongs_to_many :projects
end

class Project
  has_and_belongs_to_many: users
end

,但我不知道如何映射创建者->created_projects关系。我相信我可以在 Project 中引用用户创建者,例如 belongs_to :creator, :class_name =>; 'User' 但我不知道如何设置另一方。

如何在 Mongoid 中最好地建模这些关系?

I have two entities, projects and users. These are modeled in Rails using Mongoid with two Document instances, User and Project.

In this system, one user can create one project, but many users can follow many projects. For example, as user_id 1 I've created project_id 1. But user_ids 10, 11, 40, and 60 all follow project_id 1. I need to represent a many-to-many relationship between users and projects, and represent a specific user_id as the creator of the project, to assign him editing rights.

Practically speaking, when a user logs-in, he needs to be able to see all projects that he is following, including any that he created, commingled with other projects created by other users. His special creator status wont influence this list at all. When a user looks at a specific project, he needs to be able to view all users following a project, and if he's a creator, he can add new followers and delete existing ones.

In a RDBMS I would represents this with tables users, projects and a users_projects join table with a flag of is_creator. This would easily let me select which projects a user can see, and which users are followers, including which users are creators, of projects.

Mongoid supports many-to-many relationships, but unlike in an RDBMS there's no way for me to put a flag on the relationship. Instead, I'm thinking I'll add a creator field to the projects document, which will contain a link back to an _id field on the users document.

The user->projects relationship might look like this

class User
  has_and_belongs_to_many :projects
end

class Project
  has_and_belongs_to_many: users
end

But I can't figure out how to map the creator->created_projects relationship. I believe I can reference a user creator in Project like belongs_to :creator, :class_name => 'User' but I'm not sure how to set up the other side.

How best can I model these relationships in Mongoid?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

满栀 2024-12-19 21:06:28

第二个版本使用较少的空间,但您需要额外的查询来获取用户详细信息,例如用户名。对象 ID 最多 12 个字节。文档大小为 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)

the second version uses less space but you need an extra query to get the user details like usernames. An object ID has 12bytes, max. document size is 16mb so the array could hold around 1.3M user ids (theoretically!)..

here you go:

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

How to work with it:

@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)
悲喜皆因你 2024-12-19 21:06:28

创建一个嵌入文档,其中包含所有关注者及其 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:如果您想要一个更简单的解决方案,你可以嵌入项目中的 ObjectID (user_ids) 数组,并使用原子更新 $addToSet$pullAll 来添加/删除关注者。但是您需要一个额外的查询,例如 User.where(:user_id.in => @project.follower_ids) (假设该数组称为 follower_ids)来获取所有用户及其姓名;)

create an embedded document which holds all followers with their user_id and their username so you won't have to query the follower's usernames.

The benefits:

  • Not a single query to lookup a project's followers
  • Only a single query to lookup a user's followed projects

The downside:

  • If a user changes his name, you'll have to update all his "followships" but how often
    do you change your name compared to how often you lookup your followed projects ;)

  • If you have many thousand followers per project you may reach the document limit of 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

How to work with it:

@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: If you want a simpler solution, you could just embedd an array of ObjectIDs (user_ids) in the project and use the atomic updates $addToSet and $pullAll to add/remove a follower. But you'd need an extra query like User.where(:user_id.in => @project.follower_ids) (assuming the array is called follower_ids) to grab all users and their names ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文