Requêtes dans Rails, Partie 1

Dans cet article, vous allez apprendre les bases des requêtes Active Record et quelques notions de base sur SQL tout au long du processus. Il est destiné aux débutants qui souhaitent en savoir plus sur les requêtes de base de données dans Ruby on Rails.

Les sujets

  • Objets uniques
  • Objets multiples
  • Conditions
  • Commande
  • Limites
  • Groupe et avoir

Enregistrement actif est utilisé pour interroger la base de données. Il peut être utilisé avec SQL, PostgresSQL et SQLite. Pour récupérer des enregistrements de votre base de données, vous disposez de plusieurs méthodes de recherche. La bonne chose à leur sujet est que vous pouvez vous épargner la peine d’écrire du SQL brut. 

Qu'est-ce qu'une méthode de recherche fait vraiment? Fondamentalement, trois choses: vos options fournies sont converties en une requête SQL. Ensuite, la requête SQL est exécutée et extrait les données de la base de données. De plus, pour chaque ligne de cette liste de résultats, nous obtenons les objets Ruby nouvellement instanciés du modèle qui correspond à la requête.. 

Si vous n'avez jamais joué avec SQL, je ferai de mon mieux pour que les choses restent simples et que je vous présente les bases. Suivez les exemples SQL et essayez de donner un sens à ces requêtes simples. SQL n'est pas vraiment sorcier, la syntaxe demande un peu de temps pour s'y habituer. J'espère que cela vous donnera envie de dénicher des didacticiels utiles pour combler les lacunes.. 

Voyons quelques méthodes à votre disposition:

  • trouver
  • premier
  • dernier
  • trouver_par
  • tout
  • find_each
  • find_in_batches
  • ordre
  • limite
  • décalage
  • groupe
  • ayant

Tous ces éléments renverront une instance de ActiveRecord :: Relation. Un quoi? C'est une classe qui est namespaced au sein de la module ActiveRecord, et cela nous permet d'appeler plusieurs méthodes de requête et de les chaîner. Cet objet est le cœur de la syntaxe de requête utilisée dans Rails. Vérifions la classe d'un tel objet et voyons nous-mêmes:

Des rails

Agent.where (nom: 'James Bond'). Class # => ActiveRecord :: Relation

Objets uniques

  • trouver

Cette méthode vous permet de fournir l'identifiant principal d'un objet et de récupérer ce seul objet pour vous. Si vous fournissez un tableau d'identifiants, vous pouvez également récupérer plusieurs objets..

Des rails

bond = Agent.find (7)

SQL

SELECTIONNEZ "agents". * FROM "agents" O "agents". "Id" =? LIMITE 1 [["" id ", 7]]

Cette ligne de code SQL indique que vous souhaitez tout sélectionner (*) attributs de la agents table et "filtre" uniquement l'enregistrement ayant l'identifiant 7. Une limite lui permet de ne renvoyer qu'un seul enregistrement à partir de la base de données..

  • premier, dernier

Sans surprise, cela vous donnera les premier et dernier enregistrements qui peuvent être identifiés par leur clé primaire. La partie intéressante, cependant, est que vous pouvez fournir un nombre optionnel qui vous renvoie le premier ou le dernier de ce nombre d'enregistrements.. 

Des rails

ennemis_agents = SpectreAgent.first (10) ennemis_agents = SpectreAgent.last (10)

Sous le capot, vous fournissez une nouvelle limite pour le nombre que vous fournissez et vous le commandez par ordre croissant ou décroissant..

SQL

SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
  • trouver_par

Ce chercheur renvoie le premier objet correspondant à la condition fournie..

Des rails

bond = Agent.find_by (last_name: 'Bond')

SQL

SELECT "agents". * FROM "agents" O "agents". "Dernier_nom" =? LIMIT 1 [["" nom "," Bond "]]

Objets multiples

De toute évidence, nous avons souvent besoin de parcourir une collection d'objets avec un ordre du jour. Récupérer un ou plusieurs objets à la main est bien, mais le plus souvent, nous voulons qu'Active Record récupère les objets par lots.. 

Présenter aux utilisateurs toutes sortes de listes est le pain quotidien de la plupart des applications Rails. Ce dont nous avons besoin, c'est d'un outil puissant avec une API pratique pour collecter ces objets pour nous, d'une manière qui nous permet d'éviter d'écrire le code SQL impliqué nous-mêmes la plupart du temps..

  • tout

Des rails

mi6_agents = Agents.all

SQL

SELECT "agents". * FROM "agents"

Cette méthode est pratique pour les collections d'objets relativement petites. Essayez d'imaginer faire cela sur une collection de tous les utilisateurs de Twitter. Non, pas une bonne idée. Ce que nous voulons plutôt, c'est une approche plus précise pour les tables de plus grande taille.. 

Aller chercher toute la table ne va pas à l'échelle! Pourquoi? Parce que nous ne demanderions pas seulement un tas d’objets, nous aurions également besoin de construire un objet par ligne dans cette table et de les placer dans un tableau en mémoire. J'espère que cela ne semble pas être une bonne idée! Alors, quelle est la solution pour cela? Des lots! Nous divisons ces collections en lots qui permettent de traiter plus facilement la mémoire. Woohoo!

Regardons find_each et find_in_batches. Les deux sont similaires mais se comportent différemment dans la manière dont ils produisent des objets en blocs. Ils acceptent une option pour réguler la taille du lot. La valeur par défaut est 1 000.

  • find_each

Des rails

NewRecruit.find_each do | recruit | recruit.start_hellweek end

SQL

SÉLECTIONNEZ "newrecruits". * FROM "newrecruits" ORDER BY "newrecruits". "Id" ASC LIMIT 1000

Dans ce cas, nous récupérons un lot par défaut de 1 000 nouvelles recrues, nous les cédons au bloc et nous les envoyons à la semaine de l'enfer, une par une. Comme les lots découpent des collections, nous pouvons également leur dire par où commencer via début. Disons que nous voulons traiter 3 000 recrues possibles en une fois et que nous voulons partir à 4 000.

Des rails

NewRecruit.find_each (début: 4000, batch_size: 3000) faire | recruter | recruit.start_hellweek end

SQL

SÉLECTIONNEZ "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000) ORDER BY "newrecruits". "Id" ASC LIMIT 3000

Pour rappel, nous récupérons d’abord un lot de 3 000 objets Ruby, puis nous les envoyons dans le bloc.. début nous permet de spécifier l'id des enregistrements où nous voulons commencer à aller chercher ce lot.

  • find_in_batches

Celui-ci renvoie son lot sous forme de tableau au bloc - il le transmet à un autre objet qui préfère gérer les collections. Le SQL est le même ici.

Des rails

NewRecruit.find_in_batches (début: 2700, batch_size: 1350) ne | recrute | field_kitchen.prepare_food (recrues) end

Conditions

Nous devons passer en revue avant de continuer plus loin. Cela nous permet de spécifier des conditions qui limitent le nombre d'enregistrements renvoyés par nos requêtes: un filtre pour «où» récupérer les enregistrements de la base de données. Si vous avez joué avec SQL clauses alors vous pourriez vous sentir juste à la maison-même chose avec cette enveloppe Ruby. 

En SQL, cela nous permet de spécifier la ligne de la table que nous voulons affecter, en gros, là où elle remplit certains critères. C'est une clause facultative, en passant. Dans le SQL brut ci-dessous, nous sélectionnons uniquement les recrues orphelines via

Sélectionnez une ligne spécifique dans une table.

SQL

SELECT * FROM recrues WHERE FamilyStatus = 'Orphan';

Via , vous pouvez spécifier des conditions avec des chaînes, des hachages ou des tableaux. En mettant tout cela ensemble, Active Record vous permet de filtrer pour de telles conditions:

Des rails

promise_candidates = Recruit.where ("family_status = 'orphan'")

SQL

SELECT "recrute". * FROM "recrute" WHERE (family_status = 'orphan')

Plutôt chouette, non? Je tiens à mentionner qu'il s'agit toujours d'une opération de recherche. Nous spécifions simplement la manière dont nous souhaitons filtrer cette liste immédiatement. Dans la liste de toutes les recrues, une liste filtrée de candidats orphelins sera renvoyée. Cet exemple est une condition de chaîne. Éloignez-vous des conditions de chaîne pures car elles ne sont pas considérées comme sûres en raison de leur vulnérabilité aux injections SQL.

Argumentaire

Dans l'exemple ci-dessus, nous mettons le orphelin variable dans la chaîne avec les conditions. Ceci est considéré comme une mauvaise pratique parce que c'est dangereux. Nous devons échapper à la variable pour éviter cette vulnérabilité de sécurité. Vous devriez vous renseigner sur l'injection SQL s'il s'agit d'une nouvelle totale pour vous, votre base de données pourrait en dépendre.

Des rails

promise_candidates = Recruter.where ("family_status =?", 'orphelin' ")

le ? sera remplacé comme valeur de condition par la valeur suivante dans la liste d'arguments. Donc, le point d'interrogation est un espace réservé essentiellement. Vous pouvez également spécifier plusieurs conditions avec plusieurs ? et les enchaîner. Dans un scénario réel, nous utiliserions un hachage params comme ceci:

promise_candidates = Recruter.where ("family_status =?", params [: recruits])

Si vous avez un grand nombre de conditions variables, vous devez utiliser des conditions d'espace réservé clé / valeur..

Des rails

promise_candidates = Recruit.where ("family_status =: statut_pronféré ET iq> =: requis_iq ET charmant =: lady_killer", statut_précié: 'orphelin', requis_iq: 140, lady_killer: vrai)

SQL

SELECT "recrute". * FROM "recrute" WHERE (family_status = 'orphan' ET iq> = 140 AND lady_killer = true)

L'exemple ci-dessus est stupide, bien sûr, mais il montre clairement les avantages de la notation d'espace réservé. La notation de hachage, en général, est certainement la plus lisible.

Des rails

promise_candidates = Recruter.where (statut de famille: 'orphelin') promise_candidates = Recruit.where ('charmant': vrai)

Comme vous pouvez le constater, vous pouvez utiliser des symboles ou des chaînes de caractères. Fermons cette section avec des plages et des conditions négatives via NOT.

Des rails

promise_candidates = Recruit.where (anniversaire: ('1994-01-01'… '2000-01-01'))

Deux points et vous pouvez établir la plage dont vous avez besoin.

promise_candidates = Recruter.where.not (caractère: 'lâche')

Vous pouvez rentrer le ne pas sur la pour filtrer tous les lâches et obtenir uniquement des résultats qui n'ont pas cet attribut spécifique non désiré. Sous le capot, un != annule le «filtre» WHERE.

SQL

SELECT "recrute". * FROM "recrute" WHERE ("recrute". "Character"! =?) [["Character", "lâche"]]

Commande

  • ordre

Pour ne pas vous ennuyer à mort, faisons-en un rapide.

candidats = Recruit.order (: date_of_birth)
candidats = Recruit.order (: date_au_naissance,: desc)

Appliquer : asc ou : desc pour le trier en conséquence. C'est fondamentalement ça, alors passons à autre chose!

Limites

  • limite

Vous pouvez réduire le nombre d'enregistrements renvoyés à un nombre spécifique. Comme mentionné précédemment, la plupart du temps, vous n'aurez pas besoin de récupérer tous les enregistrements. L'exemple ci-dessous vous donnera les cinq premières recrues de la base de données - les cinq premiers identifiants.

Des rails

five_candidates = Recruit.limit (5) 

SQL

SELECT "recrute". * FROM "recrute" LIMIT 5
  • décalage

Si vous vous êtes déjà demandé comment la pagination fonctionne sous le capot, limite et décalage-en même temps, faire le dur travail. limite peut être autonome, mais décalage dépend de l'ancien.

La définition d'un décalage est surtout utile pour la pagination et vous permet d'ignorer le nombre de lignes souhaité dans la base de données. La page deux d'une liste de candidats pourrait être consultée comme ceci:

Des rails

Recruter.limite (20) .offset (20)

Le SQL ressemblerait à ceci:

SELECT "recrute". * FROM "recrute" LIMITE 20 OFFSET 20

Encore une fois, nous sélectionnons toutes les colonnes de la Recruter modèle de base de données, en limitant les enregistrements renvoyés à 20 objets Ruby de Class Recruit et en sautant par-dessus les 20 premiers.

Groupe et avoir

Disons que nous voulons une liste de recrues regroupées par QI. En SQL, cela pourrait ressembler à quelque chose comme ça.

SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Iq"

Cela vous donnerait une liste où vous verrez quelles recrues possibles ont un QI de 120, puis un autre groupe de 140 et ainsi de suite, quel que soit leur QI, et combien pourraient figurer sous un chiffre spécifique. Ainsi, lorsque deux recrues ont le même QI de 130, elles sont regroupées. 

Une autre liste pourrait être regroupée par candidats potentiels souffrant de claustrophobie, de peur des hauteurs ou médicalement impropres à la plongée. La requête Active Record ressemblerait simplement à ceci:

  • groupe

Des rails

Candidate.group (: iq)

Lorsque nous comptons le nombre de candidats, nous obtenons un hachage très utile.

Des rails

Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1

Voilà, nous avons sept recrues possibles avec un QI de 130 et une seule avec 141. Le résultat SQL ressemblerait à ceci:

SQL

SELECT COUNT (*) AS count_all, iq AS iq FROM "candidats" GROUP BY "candidats". "Iq"

La pièce importante est la PAR GROUPE partie. Comme vous pouvez le constater, nous utilisons la table des candidats pour obtenir leurs identifiants. Vous pouvez également observer, à partir de cet exemple simple, à quel point les versions Active Record en lecture et en écriture sont plus pratiques. Imaginez que vous le fassiez à la main sur des exemples plus extravagants. Bien sûr, parfois, vous devez le faire, mais tout le temps est clairement une douleur que nous pouvons éviter avec plaisir.

  • ayant

Nous pouvons spécifier encore plus ce groupe en utilisant AYANT-une sorte de filtre pour le groupe. Dans ce sens, ayant est une sorte de clause de GROUPE. En d'autres termes, ayant dépend de l'utilisation groupe.

Des rails

Recruit.having ('iq>?', 134) .group (: iq)

SQL

SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Iq" AYANT iq> '134'

Nous avons maintenant regroupé nos candidats dans des listes de personnes ayant un QI minimum de 135. Comptons-les pour obtenir des statistiques:

Des rails

Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1

SQL

SELECT COUNT (*) AS count_all, iq AS iq FROM "recrute" GROUP BY "recrute". "Iq" AYANT iq> '134'

Nous pourrions également les combiner et voir, par exemple, quels candidats ayant un QI supérieur à 140 sont liés ou non.. 

Des rails

Recruit.having ('iq>?', 140) .group (: état_famille)

SQL

SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Family_status" AYANT iq> '140'

Compter ces groupes est maintenant trop facile:

Des rails

Recruit.having ('iq>?', 140) .group (: statut_famille) .compte # => "marié" => 2, "célibataire" => 1

SQL

SELECT COUNT (*) AS count_all, family_status AS family_status FROM "recrute" GROUP BY "recrute". "Family_status" AYANT iq> '140'

Dernières pensées

J'espère que ce fut un premier aperçu utile de ce qu'Active Record a à offrir pour rendre vos efforts d'interrogation aussi lisibles et pratiques que possible. Globalement, je dirais que c'est un excellent wrapper qui vous empêche la plupart du temps d'écrire SQL à la main. 

Dans le prochain article, nous examinerons quelques chercheurs plus impliqués et développerons ce que nous avons appris jusqu'à présent..