Comment Rails ActiveRecord enchaîne-t-il les clauses «where» sans plusieurs requêtes?

Je suis un développeur PHP qui apprend la gaieté de Ruby on Rails, j’aime ActiveRecord et j’ai remarqué quelque chose de très intéressant. Les méthodes ActiveRecord détectent la chaîne de fin de méthode pour exécuter la requête.

@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 

Comment fonctionne cette sorcellerie?

La méthode where renvoie un object ActiveRecord::Relation et, par lui-même, cet object n’émet pas de requête de firebase database. C’est là que vous utilisez cet object qui compte.

Dans la console, vous faites probablement ceci:

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

Et puis, blammo émet une requête de firebase database et renvoie ce qui semble être un tableau de tout le monde nommé Jason. Yay, Active Record!

Mais alors vous faites quelque chose comme ça:

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

Et puis cela émet une autre requête, mais celle-ci concerne les personnes qui s’appellent Jason qui ont 26 ans. Mais il ne s’agit que d’ une seule requête, alors où est passée l’autre requête?


Comme d’autres l’ont suggéré, cela se produit car la méthode where renvoie un object proxy. En réalité, il n’effectue pas de requête et ne renvoie pas de jeu de données, sauf si cela lui est demandé.

Lorsque vous exécutez quoi que ce soit dans la console, la version inspectée du résultat de votre exécution est affichée. Si vous mettez 1 dans la console et appuyez sur Entrée, vous obtiendrez 1 retour parce que 1.inspect est 1 . La magie! Même chose pour "1" . Une variété d’autres objects n’a pas de méthode d’ inspect définie et Ruby se rabat sur celle de Object qui renvoie quelque chose de terrible comme .

Chaque object ActiveRecord::Relation possède la méthode inspect qui lui est associée afin de provoquer une requête. Lorsque vous écrivez la requête dans votre console, IRB appelle inspect sur la valeur de retour de cette requête et affiche quelque chose de presque lisible, comme le tableau que vous voyez.


Si vous émettiez cela dans un script Ruby standard, aucune requête ne serait exécutée tant que l’object n’aurait pas été inspecté (par inspect ) ou itéré en utilisant each , ou si la méthode to_a appelée.

Jusqu’à ce que l’une de ces trois choses se produise, vous pouvez enchaîner autant d’instructions where vous voulez et ensuite, lorsque vous appelez to_a , to_a ou each d’elles, elle exécutera finalement cette requête.

Il existe un certain nombre de méthodes appelées “kickers” qui déclenchent la requête dans la firebase database. Auparavant, ils créaient simplement des nœuds AST qui, une fois lancés, génèreraient le SQL réel (ou le langage en cours de compilation) et exécuteraient la requête.

Voir ce billet de blog pour une explication plus approfondie de la façon dont cela est fait.

Vous pouvez lire le code, mais un concept ici est le modèle de proxy.

Probablement @personnel n’est pas l’object réel mais un proxy pour cet object et lorsque vous avez besoin d’un atsortingbut, l’enregistrement actif exécute finalement la requête. Hibernate a le même concept.

Peut-être un peu trop tard mais vous pouvez utiliser un hachage:

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

Requête résultante:

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