Ruby on Rails: есть ли способ найти с ActiveRecord, который сначала попробует кеш, и если он потерпит неудачу, запустит запрос? - PullRequest
1 голос
/ 05 марта 2010

Допустим, я ищу определенный элемент в ассоциации has_many.

Есть ли способ найти, который не перепишет базу данных, если то, что я запрашиваю, уже кэшировано?

Вот моя ситуация (которая не работает и не выполняет то, что я просил):

class User
  has_many :friendships

  def some_function(buddy)
    f = friendships.detect{|friendship| friendship.user == self && 
                                        friendship.friend == buddy}
    f.do_something
  end
end

Функция подвержена проблемам N + 1, но большую часть времени я уже использовал: включаю или предварительно загружаю дружбу пользователя в другом месте. Однако, если я этого не сделал, то эта текущая функция вернется к запросу всех друзей, даже если она нужна. Казалось бы, решение выглядит примерно так:

f = Friendship.find_by_user_id_and_friend_id id, buddy.id

Однако, это снова попадет в базу данных, даже если все дружеские отношения кэшируются, и у меня есть то, что мне нужно прямо здесь.

Есть ли способ использовать кэшированную копию или, если ее нет, искать определенную запись?

Ответы [ 3 ]

2 голосов
/ 06 марта 2010

Что вы хотите, это #loaded?метод ассоциации прокси:

class User
  has_many :friendships

  def some_function(buddy)
    if friendships.loaded? then
      f = friendships.detect{|friendship| friendship.user == self && 
                                          friendship.friend == buddy}
    else
      Friendship.find_by_user_id_and_friend_id id, buddy.id
    end
  end
end

К сожалению, #loaded?не появляется в публичной документации.Вы всегда можете охранять свой звонок на #loaded?with friendships.respond_to? (: загружен?).

Документация для AssociationProxy # загружен? на GitHub.

1 голос
/ 05 марта 2010

Если вы уже предоставили названный @ user.friendships и хотите получить доступ к кэшированной версии, вы можете использовать

def some_function(buddy)
  f = friendships.select { |f|  f.buddy_id == buddy.id }
  f.do_something
end

Метод select не выполняет запрос к БД и использует отношения кэшированных дружеских отношений. Если вы еще не вызвали @ user.friendships, первый вызов этого метода запустит запрос, а последующие вызовы будут использовать кэшированную версию.

0 голосов
/ 05 марта 2010

Если вы уже получили доступ к атрибуту @ user.friendships (и, таким образом, получили доступ к базе данных для получения данных), эти ассоциации будут сохранены в памяти, и вам больше не нужно будет обращаться к базе данных.

Когда дело доходит до поиска конкретной записи из связанного набора данных :has_many, я не совсем уверен. Один из вариантов - сделать find_by_sql , чтобы написать sql, чтобы получить эту конкретную запись. Вы также можете сделать что-то вроде этого:

class User
  has_many :friendships

  def some_function(buddy)
    f = Friendships.find_by_user_id(self.id, :conditions => "buddy_id = #{buddy.id}")
    f.do_something
  end
end

Который выполняет базовый поиск SQL для поиска любых записей с идентификатором текущего пользователя и идентификатором партнера, который передается в функцию.

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