2

I have a datamodel that contains a Project, which contains a list of Suggestions, and each Suggestion is created by a User. Is there a way that I can create a list of all distinct Users that made Suggestions within a Project?

I'm using Mongoid 3. I was thinking something like this, but it doesn't work:

@project = Project.find(params[:id])
@users = Array.new
@users.push(@project.suggestions.user)  <-- this doesn't work

Any ideas? Here's my model structure:

class Project
  include Mongoid::Document
  has_many :suggestions, :dependent => :destroy
  ...
end

class Suggestion
  include Mongoid::Document
  belongs_to :author, class_name: "User", :inverse_of => :suggestions
  belongs_to :project
  ...
end

class User
  include Mongoid::Document
  has_many :suggestions, :inverse_of => :author
  ...
end
Ryan McGeary
  • 235,892
  • 13
  • 95
  • 104
netwire
  • 7,108
  • 12
  • 52
  • 86

2 Answers2

1

While Mongoid can give MongoDB the semblance of relationships, and MongoDB can hold foreign key fields, there's no underlying support for these relationships. Here are a few options that might help you get the solution you were looking for:

Option 1: Denormalize the data relevant to your patterns of access

In other words, duplicate some of the data to help you make your frequent types of queries efficient. You could do this in one of a few ways.

One way would be to add a new array field to User perhaps called suggested_project_ids. You could alternatively add a new array field to Project called suggesting_user_ids. In either case, you would have to make sure you update this array of ObjectIds whenever a Suggestion is made. MongoDB makes this easier with $addToSet. Querying from Mongoid then looks something like this:

User.where(suggested_project_ids: some_project_id)

Option 2: Denormalize the data (similar to Option 1), but let Mongoid manage the relationships

class Project
  has_and_belongs_to_many :suggesting_users, class_name: "User", inverse_of: :suggested_projects
end

class User
  has_and_belongs_to_many :suggested_projects, class_name: "Project", inverse_of: :suggesting_users
end

From here, you would still need to manage the addition of suggesting users to the projects when new suggestions are made, but you can do so with the objects themselves. Mongoid will handle the set logic under the hood. Afterwards, finding the unique set of users making suggestions on projects looks like this:

some_project.suggesting_users

Option 3: Perform two queries to get your result

Depending on the number of users that make suggestions on each project, you might be able to get away without performing any denormalization, but instead just make two queries.

First, get the list of user ids that made suggestions on a project.

author_ids = some_project.suggestions.map(&:author_id)
users = User.find(author_ids)
Ryan McGeary
  • 235,892
  • 13
  • 95
  • 104
  • Option 3 will work. As for denormalizing, I'm waiting for Alize or Mongoid-Denormalize to support Mongoid 3 and then use one of them. – netwire Aug 02 '12 at 15:48
0

In your Project class add this :

has_many :users, :through => :suggestions

You'll then be able to do :

@users.push(@project.users)

More info on :through here :

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

For mongoid, take a look at this answer : How to implement has_many :through relationships with Mongoid and mongodb?

Community
  • 1
  • 1
Anthony Alberto
  • 10,325
  • 3
  • 34
  • 38