Самый быстрый способ поиска двух моделей, соединенных через таблицу соединений в рельсах при большом наборе данных - PullRequest
2 голосов
/ 05 августа 2009

У меня есть модель пользователя и модель CD, соединенные через таблицу соединений 'cds_users'. Я пытаюсь вернуть хэш пользователей плюс каждый диск, который у них общего с исходным пользователем.

@user.users_with_similar_cds(1,4,5)
# => {:bob => [4], :tim => [1,5]}

Есть ли лучший / более быстрый способ сделать это без много циклов? Может быть, более прямой путь?

def users_with_similar_cds(*args)
  similar_users = {}
  Cd.find(:all, :conditions => ["cds.id IN (?)", args]).each do |cd|
    cd.users.find(:all, :conditions => ["users.id != ?", self.id]).each do |user|
      if similar_users[user.name]
        similar_users[user.name] << cd.id
      else
        similar_users[user.name] = [cd.id]
      end
    end
  end
  similar_users
end

[дополнение]

Принимая идею модели соединения, я мог бы сделать что-то вроде этого. Я назову модель "присоединился".

def users_with_similar_cds(*args)
  similar_users = {}
  Joined.find(:all, :conditions => ["user_id != ? AND cd_id IN (?)", self.id, args]).each do |joined|
    if similar_users[joined.user_id]
      similar_users[joined.user_id] << cd_id
    else
      similar_users[joined.user_id] = [cd_id]
    end
  end
  similar_users
end

Это был бы самый быстрый способ для больших наборов данных?

Ответы [ 2 ]

1 голос
/ 06 августа 2009

Вы можете использовать find_by_sql в модели «Пользователи», и Active Record будет динамически добавлять методы для любых дополнительных полей, возвращаемых запросом. Например:

similar_cds = Hash.new
peeps = Users.find_by_sql("SELECT Users.*, group_concat(Cds_Users.cd_id) as cd_ids FROM Users, Cds_Users GROUP BY Users.id")
peeps.each { |p| similar_cds[p.name] = p.cd_ids.split(',') }

Я не проверял этот код, и этот конкретный запрос будет работать только в том случае, если ваша база данных поддерживает group_concat (например, MySQL, последние версии Oracle и т. Д.), Но вы должны быть в состоянии сделать что-то подобное с любой базой данных вы используете.

0 голосов
/ 06 августа 2009

Да, вы можете, только с 2 выборами:

Создание модели таблицы соединений с именем CdUser (используйте has_many .. through)

# first select
cd_users = CdUser.find(:all, :conditions => ["cd_id IN (?)", args])
cd_users_by_cd_id = cd_users.group_by{|cd_user| cd_user.cd_id }

users_ids = cd_users.collect{|cd_user| cd_user.user_id }.uniq
#second select
users_by_id = User.find_all_by_id(users_ids).group_by{|user| user.id}

cd_users_by_cd_id.each{|cd_id, cd_user_hash| 
  result_hash[:cd_id] = cd_users_hash.collect{|cd_user| users_by_id[cd_user.user_id]}
}

Это просто идеей, еще не проверял:)

К вашему сведению: http://railscasts.com/episodes/47-two-many-to-many

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...