Frage

Let's say I have a User with a field name, and which has_many teams, and a Team that belongs_to a user, and belongs_to a sport. A Sport has a field name and has_many teams.

I want to walk through the sports, do some stuff, and collect an array of the teams sorted by the name of the user.

result = []
Sport.asc(:name).each do |spt|
  # some other stuff not relevant to this question but that
  # justifies my looping through each sport.
  spt.teams.asc(:'user.name').each { |t| result << t }
end

This runs but and the sorting of the sports is as expected, but the order of the teams in result is not sorted as I'd expect.

What is the correct way, using Mongoid to sort a collection by the value of a relation?

War es hilfreich?

Lösung

I don't think there is a way to do this with Mongoid. It might work if the field you were sorting by was part of an embedded document, but not in this case where you're using a referenced document.

I guess you probably have two options here, the less efficient way would be to just sort the collection of teams in ruby:

sport.teams.sort{|t1, t2| t1.user.name <=> t2.user.name}.each{ |team| result << team }

The better, and arguably more 'MongoDB-y' solution, would be to cache the user name inside each team, using a before_save callback, and then use that to sort the teams:

# app/models/team.rb
class Team
  include Mongoid::Document

  field :user_name, :type => String

  before_save :update_user_name

  protected
  def update_user_name
    self.user_name = self.user.name if self.user
  end
end

Then you can just do:

spt.teams.asc(:user_name).each { |t| result << t }

Obviously, if the user's name field is mutable, then you'll to trigger it to save each child-team whenever the user's name field is changed.

class User
  after_save :update_teams_if_name_changed

  def update_teams_if_name_changed
    if self.name_changed?
      self.teams.each { |team| team.save }
    end
  end
end

Given that is not fantastically simple to maintain, this could arguably be a good candidate to use an observer, rather than callbacks, but you get the idea.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top