Mejora la eficiencia en la búsqueda de asociaciones

Tengo una aplicación con Usuarios, Publicaciones y Comentarios. Los usuarios tienen muchas publicaciones, las publicaciones tienen muchos comentarios y los comentarios pertenecen a usuarios y publicaciones.

En mi plantilla de vista, estoy recorriendo los comentarios como este para obtener el nombre de usuario del comentarista:

User.find(comment.user_id).name

¿Qué tan eficiente es esto si estoy haciendo esto para cada comentario por publicación? Podría imaginar que tal vez sea más rápido almacenar el nombre de usuario en una fila separada en la tabla de comentarios, pero duplicar los datos parece simplemente incorrecto.

¿Estoy siendo paranoico y ActiveRecord está haciendo algo de magia de almacenamiento en caché o hay una mejor manera de hacer algo como esto?

preguntado el 04 de julio de 12 a las 10:07

4 Respuestas

Puede precargar los usuarios en la consulta inicial. Como tiene la configuración de asociación entre Usuario y Comentario, puede acceder al usuario a través de la asociación.

Supongamos que tiene un método de controlador:

def show
  @posts = Post.limit(10).includes(:comments => :user)
end

En tu vista mientras recorres @comments:

@posts.each do |post|
  # show post stuff
  post.comments.each do |comment|
    comment.user.name
  end
end

Respondido 04 Jul 12, 10:07

entonces, si limito las publicaciones devueltas (digamos 10), el @comments ¿Solo capturará a los usuarios por las 10 publicaciones devueltas? - DeanAlexRainier

@daanVOS Actualicé mi ejemplo. Esto carga diez publicaciones e incluye comentarios y el comentario del usuario con entusiasmo. - lebreeze

@daanVOS de esta manera, la aplicación probablemente realizará una consulta SQL para las publicaciones, una para los comentarios y otra para los usuarios que hicieron esos comentarios, independientemente de cuántos comentarios haya. - lebreeze

Revisado

Si tiene una asociación como la que se muestra a continuación, puede acceder al usuario desde el propio objeto de comentario comment.user

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
end

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

Respondido 04 Jul 12, 10:07

¿No significa esto que el usuario del comentario es el mismo que el propietario de la publicación a la que está asociado el comentario? ¿En cuyo caso eso sería totalmente incorrecto ya que seguramente muchos usuarios pueden comentar en la publicación de otro usuario? - lebreeze

@lebreeze, tengo la misma pregunta: cortejo

@cutalion nuestras dos respuestas son muy similares y estoy casi seguro de que son correctas. No estoy seguro de por qué fueron rechazados, ya que eso es engañoso para las personas que realmente están tratando de aprender sobre las asociaciones de Rails. - lebreeze

Carga ansiosa de asociaciones

class Post < ActiveRecord::Base
  has_many :comments
  # ...
end

class Comment < ActiveRecord::Base
  belongs_to :user
end


comments = @post.comments.includes(:user)

Precargará a los usuarios.

SQL se verá así:

SELECT * FROM `comments` WHERE `comments`.`post_id` = 42;
SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3,4,5,6) # 1,2,3,4,5,6 - user_ids from comments

Respondido 04 Jul 12, 10:07

¿Quiere decir que desea cargar publicaciones con comentarios con usuarios con publicaciones de usuarios? - cortejo

Si el rendimiento es un problema para usted, usar una base de datos no relacional, como Mongodb, es el camino a seguir.

Si aún desea utilizar ActiveRecord, utilice la carga ansiosa con Post.comments.include(:user) (que cargará la información de los usuarios no utilizados, y eso no es bueno), o puede usar el almacenamiento en caché técnicas.

Me parece bien almacenar en caché el user.name en la tabla de comentarios, como sugirió, siempre que controle los cambios que pueden ocurrir. Puede hacerlo configurando devoluciones de llamada en su User modelo:

after_save do |user|
  Comment.where(user_id: user.id).update_all(:username, user.name)
end

Esta técnica es una especie de almacenamiento en caché de base de datos, pero, por supuesto, puede almacenar en caché fragmentos de HTML. Y un bloque de comentarios es bueno para almacenar en caché el bloque HTML.

Respondido 04 Jul 12, 11:07

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.