¿Cómo encadena Rails ActiveRecord las cláusulas “where” sin múltiples consultas?

Soy un desarrollador de PHP y aprendo el asombro de Ruby on Rails, me encanta ActiveRecord y noté algo realmente interesante. Así es como los métodos de ActiveRecord detectan el final de la cadena de métodos para ejecutar la consulta.

@person = Person.where(name: 'Jason').where(age: 26) # In my humble imagination I'd think that each where() executes a database query # But in reality, it doesn't until the last method in the chain 

¿Cómo funciona esta hechicería?

El método where devuelve un objeto ActiveRecord::Relation y, por sí mismo, este objeto no emite una consulta de base de datos. Es donde usas este objeto que importa.

En la consola, probablemente estés haciendo esto:

 @person = Person.where(name: "Jason") 

Y luego blammo emite una consulta de base de datos y devuelve lo que parece ser una matriz de todos los que se llaman Jason. Yay, Active Record!

Pero luego haces algo como esto:

 @person = Person.where(name: "Jason").where(age: 26) 

Y luego eso genera otra consulta, pero esta es para las personas que se llaman Jason que tienen 26 años. Pero solo emite una consulta, entonces, ¿a dónde fue la otra consulta?


Como otros han sugerido, esto está sucediendo porque el método where devuelve un objeto proxy. En realidad, no realiza una consulta y devuelve un conjunto de datos a menos que se le pida que haga eso.

Cuando ejecutas cualquier cosa en la consola, se generará la versión inspeccionada del resultado de lo que sea que hayas ejecutado. Si coloca 1 en la consola y 1.inspect enter, obtendrá 1 vuelta porque 1.inspect es 1 . ¡Magia! Lo mismo vale para "1" . Una variedad de otros objetos no tienen un método de inspect definido y, por lo tanto, Ruby al de Object que devuelve algo espantoso como .

Cada objeto ActiveRecord::Relation tiene el método de inspect definido para que cause una consulta. Cuando escribas la consulta en tu consola, el IRB llamará para inspect el valor de retorno de esa consulta y generará un texto casi legible, como la matriz que verías.


Si acabara de publicar esto en un script estándar de Ruby, entonces no se ejecutaría ninguna consulta hasta que el objeto se inspeccionara (mediante inspect ) o se repitiera mediante el uso de each , o se le to_a método to_a .

Hasta que ocurra una de esas tres cosas, puede encadenar tantas declaraciones en él como desee y luego cuando llame inspect , to_a o each en él, entonces finalmente ejecutará esa consulta.

Hay una serie de métodos que se conocen como “kickers” que realmente disparan la consulta a la base de datos. Antes de eso, solo crean nodos AST, que una vez pateados, generarán el SQL real (o el idioma que se está comstackndo) y ejecutarán la consulta.

Vea esta publicación en el blog para una explicación más profunda de cómo se hace esto.

Puedes leer el código, pero un concepto aquí es el patrón de proxy.

Probablemente @person no es el objeto real sino un proxy para este objeto y cuando se necesita algún atributo, el registro activo finalmente ejecuta la consulta. Hibernate tiene el mismo concepto.

Tal vez un poco tarde pero puedes usar un hash:

 @person = Person.where({name: "Jason", age: 26}) 

Consulta resultante:

 SELECT "person".* FROM "person" WHERE "person"."name" = 'Jason' AND "person"."age" = 26