Maison >Périphériques technologiques >Industrie informatique >Ecto interroge ECTO à requête DSL: au-delà des bases
Cet article s'appuie sur les principes fondamentaux de l'ECTO que j'ai couverts dans la compréhension de Ecto de requête Ecto de DSL: les bases. Je vais maintenant explorer les fonctionnalités les plus avancées d'ECTO, y compris la composition de requête, les jointures et les associations, l'injection de fragments SQL, le casting explicite et l'accès au terrain dynamique.
Encore une fois, une connaissance de base de l'élixir est supposée, ainsi que les bases d'Ecto, que j'ai couvertes dans une introduction à la bibliothèque Ecto d'Elixir.
Les requêtes séparées dans Ecto peuvent être combinées ensemble, permettant de créer des requêtes réutilisables.
Par exemple, voyons comment nous pouvons créer trois requêtes distinctes et les combiner pour atteindre le sèche-linge et le code plus réutilisable:
<span>SELECT id, username FROM users; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%"; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%" LIMIT 10, 0; </span>
offset <span>= 0 </span>username <span>= <span>"%tp%"</span> </span> <span># Keywords query syntax </span>get_users_overview <span>= from u in Ectoing.User, </span> <span>select: [u.id, u.username] </span> search_by_username <span>= from u in get_users_overview, </span> <span>where: like(u.username, ^username) </span> paginate_query <span>= from search_by_username, </span> <span>limit: 10, </span> <span>offset: ^offset </span> <span># Macro syntax </span>get_users_overview <span>= (Ectoing.User </span><span>|> select([u], [u.id, u.username])) </span> search_by_username <span>= (get_users_overview </span><span>|> where([u], like(u.username, ^username))) </span> paginate_query <span>= (search_by_username </span><span>|> limit(10) </span><span>|> offset(^offset)) </span> Ectoing<span>.Repo.all paginate_query </span>
La version SQL est assez répétitive, mais la version Ecto en revanche est assez sèche. La première requête (get_users_overview) n'est qu'une requête générique pour récupérer les informations utilisateur de base. La deuxième requête (Search_By_Username) construit le premier en filtrant les noms d'utilisateur en fonction du nom d'utilisateur que nous recherchons. La troisième requête (Paginate_Query) se construit par la seconde, où elle limite les résultats et les récupére à partir d'un décalage particulier (pour fournir la base de la pagination).
Il n'est pas difficile d'imaginer que toutes les trois requêtes ci-dessus pourraient être utilisées ensemble pour fournir des résultats de recherche lorsqu'un utilisateur particulier est recherché. Chacun peut également être utilisé en conjonction avec d'autres requêtes pour répondre également à d'autres besoins d'application, le tout sans répéter inutilement des parties de la requête tout au long de la base de code.
Les jointures sont assez fondamentales lors de l'interrogation, et pourtant nous ne venons que les couvrant maintenant. La raison en est que l'apprentissage des jointures dans Ecto seul n'est pas utile: nous devons également connaître les associations. Bien que ceux-ci ne soient pas difficiles à apprendre, ils ne sont pas aussi triviaux que les autres sujets traités jusqu'à présent.
En termes simples, les associations permettent aux développeurs de gérer les relations de table (implémentées sous forme de clés étrangères) dans les modèles. Ils sont définis dans les schémas pour chaque modèle en utilisant les macros Has_one / 3 et Has_Many / 3 (pour les modèles contenant d'autres modèles), et la macro appartenance_to / 3 (pour les modèles qui sont en dehors d'autres modèles - ceux qui ont les clés étrangères) .
En examinant notre application ectoing, nous pouvons voir un exemple d'association entre le modèle ectoing.User et le modèle Ectoing.Message. Le schéma défini dans ectoing.User définit l'association suivante:
<span>SELECT id, username FROM users; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%"; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%" LIMIT 10, 0; </span>
Nous pouvons voir qu'un utilisateur a de nombreux messages (ectoing.sessage), et nous appelons cette association: messages.
Dans le modèle Ectoing.Message, nous définissons la relation d'association suivante:
offset <span>= 0 </span>username <span>= <span>"%tp%"</span> </span> <span># Keywords query syntax </span>get_users_overview <span>= from u in Ectoing.User, </span> <span>select: [u.id, u.username] </span> search_by_username <span>= from u in get_users_overview, </span> <span>where: like(u.username, ^username) </span> paginate_query <span>= from search_by_username, </span> <span>limit: 10, </span> <span>offset: ^offset </span> <span># Macro syntax </span>get_users_overview <span>= (Ectoing.User </span><span>|> select([u], [u.id, u.username])) </span> search_by_username <span>= (get_users_overview </span><span>|> where([u], like(u.username, ^username))) </span> paginate_query <span>= (search_by_username </span><span>|> limit(10) </span><span>|> offset(^offset)) </span> Ectoing<span>.Repo.all paginate_query </span>
Ici, nous disons que le modèle, ectoing.sessage, appartient au modèle ectoing.User. Nous avons également nommé l'association comme: utilisateur. Par défaut, Ecto ajoutera un _id sur le nom d'appartenance à l'association et l'utilisera comme nom de clé étrangère (donc ici, ce serait: user_id). Ce comportement par défaut peut être remplacé en spécifiant manuellement le nom de la clé étrangère en spécifiant l'option étrangère. Par exemple:
has_many <span>:messages, Ectoing.Message </span>
Voyons maintenant une requête simple qui utilise une jointure pour aller chercher un utilisateur et ses messages:
belongs_to <span>:user, Ectoing.User </span>
<span># Ectoing.Message </span>belongs_to <span>:user, Ectoing.User, foreign_key: some_other_fk_name </span>
Valeur renvoyée:
<span>SELECT * FROM users u INNER JOIN messages m ON u.id = m.user_id WHERE u.id = 4; </span>
sensiblement, nous avons un certain nombre d'associations non chargées, y compris la: Association des messages. Le chargement de cette association peut être effectué de deux manières: à partir de l'ensemble de résultat d'une requête ou à l'intérieur de la requête elle-même. Les associations de chargement à partir d'un ensemble de résultats peuvent être effectuées avec la fonction Repo.preload:
<span># Keywords query syntax </span>query <span>= from u in Ectoing.User, </span> <span>join: m in Ectoing.Message, on: u.id == m.user_id, </span> <span>where: u.id == 4 </span> <span># Macro syntax </span>query <span>= (Ectoing.User </span><span>|> join(:inner, [u], m in Ectoing.Message, u.id == m.user_id) </span><span>|> where([u], u.id == 4)) </span> Ectoing<span>.Repo.all query </span>
Les associations de chargement à partir d'une requête peuvent être effectuées en utilisant une combinaison des fonctions d'assocation et de précharge:
<span>[%Ectoing.User{__meta__: #Ecto.Schema.Metadata<:loaded>, </span> <span>firstname: <span>"Jane"</span>, </span> <span>friends_of: #Ecto.Association.NotLoaded<association :friends_of is not loaded>, </span> <span>friends_with: #Ecto.Association.NotLoaded<association :friends_with is not loaded>, </span> <span>id: 4, </span> <span>inserted_at: #Ecto.DateTime<2016-05-15T20:23:58Z>, </span> <span>messages: #Ecto.Association.NotLoaded<association :messages is not loaded>, </span> <span>surname: <span>"Doe"</span>, </span> <span>updated_at: #Ecto.DateTime<2016-05-15T20:23:58Z>, </span> <span>username: <span>"jane_doe"</span>}, </span> <span>%Ectoing.User{__meta__: #Ecto.Schema.Metadata<:loaded>, </span> <span>firstname: <span>"Jane"</span>, </span> <span>friends_of: #Ecto.Association.NotLoaded<association :friends_of is not loaded>, </span> <span>friends_with: #Ecto.Association.NotLoaded<association :friends_with is not loaded>, </span> <span>id: 4, </span> <span>inserted_at: #Ecto.DateTime<2016-05-15T20:23:58Z>, </span> <span>messages: #Ecto.Association.NotLoaded<association :messages is not loaded>, </span> <span>surname: <span>"Doe"</span>, </span> <span>updated_at: #Ecto.DateTime<2016-05-15T20:23:58Z>, </span> <span>username: <span>"jane_doe"</span>}] </span>
results <span>= Ectoing.Repo.all query </span>Ectoing<span>.Repo.preload results, :messages </span>
Maintenant, nous avons l'association des messages chargée dans le résultat:
<span>SELECT * FROM users u INNER JOIN messages m ON u.id = m.user_id WHERE u.id = 4; </span>
Les associations se joignent implicitement à la clé primaire et aux colonnes de clé étrangère pour nous, et nous n'avons donc pas à spécifier un: sur la clause. À partir de ce qui précède, nous pouvons également voir qu'en ce qui concerne les associations de préchargement, ils ne sont pas chargés paresseusement. Les associations doivent être explicitement chargées si elles sont recherchées.
Étant donné que cet article se concentre spécifiquement sur la question de l'interrogation d'Ecto, nous ne couvrirons pas l'insertion, la mise à jour ou la suppression des associations ici. Pour plus d'informations à ce sujet, consultez le billet de blog en travaillant avec les associations et intégres Ecto.
Alors que l'Ecto nous offre beaucoup de fonctionnalités, il fournit uniquement des fonctions pour les opérations communes dans SQL (elle ne vise pas à imiter le langage SQL entier). Lorsque nous devons redescendre dans SQL brut, nous pouvons utiliser la fonction Fragment / 1, permettant à l'injection directement du code SQL dans une requête.
Par exemple, effectuons une recherche sensible à la casse sur le champ du nom d'utilisateur:
<span>SELECT id, username FROM users; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%"; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%" LIMIT 10, 0; </span>
offset <span>= 0 </span>username <span>= <span>"%tp%"</span> </span> <span># Keywords query syntax </span>get_users_overview <span>= from u in Ectoing.User, </span> <span>select: [u.id, u.username] </span> search_by_username <span>= from u in get_users_overview, </span> <span>where: like(u.username, ^username) </span> paginate_query <span>= from search_by_username, </span> <span>limit: 10, </span> <span>offset: ^offset </span> <span># Macro syntax </span>get_users_overview <span>= (Ectoing.User </span><span>|> select([u], [u.id, u.username])) </span> search_by_username <span>= (get_users_overview </span><span>|> where([u], like(u.username, ^username))) </span> paginate_query <span>= (search_by_username </span><span>|> limit(10) </span><span>|> offset(^offset)) </span> Ectoing<span>.Repo.all paginate_query </span>
(Ce qui précède contient SQL spécifique à MySQL. Si vous utilisez une autre base de données, cela ne fonctionnera pas pour vous.)
La fonction fragment / 1 prend le code SQL comme une chaîne que nous aimerions injecter comme premier paramètre. Il permet aux colonnes et aux valeurs d'être liées au fragment de code SQL. Cela se fait via des espaces réservés (en tant que points d'interrogation) dans la chaîne, avec des arguments ultérieurs transmis à un fragment lié à chaque espace réservé respectivement.
Une autre façon dont Ecto utilise les définitions de schéma des modèles est de lancer automatiquement les expressions interpolées dans les requêtes aux types de champ respectifs définis dans le schéma. Ces expressions interpolées sont coulées au type de champ auquel elles sont comparées. Par exemple, si nous avons un fragment de requête tel que U.Username> ^ nom d'utilisateur, où U.Username est défini comme un champ: nom d'utilisateur ,: chaîne dans le schéma, la variable de nom d'utilisateur sera automatiquement jetée à une chaîne par ecto.
Parfois, cependant, nous ne voulons pas toujours que Ecto jette des expressions interpolées aux types de champ définis. Et d'autres fois, Ecto ne pourra pas déduire le type pour lancer une expression (généralement, c'est à ce moment que des fragments de code SQL sont impliqués). Dans les deux cas, nous pouvons utiliser la fonction Type / 2 pour spécifier l'expression et le type pour lequel il doit être coulé.
Prenons le premier cas de vouloir lancer une expression à un autre type, car c'est le scénario le plus intéressant. Dans notre application ectoing, nous avons utilisé la macro ecto.schema.timestamps pour ajouter deux champs supplémentaires à chacune de nos tableaux: Updated_at et INSERTED_AT. La macro, par défaut, définit le type de ces champs pour avoir un type d'Ecto.DateTime. Maintenant, si nous aimerions voir combien d'utilisateurs se sont inscrits au cours du mois en cours, nous pourrions utiliser une question simple comme celle suivante:
has_many <span>:messages, Ectoing.Message </span>
Cela nous donnera cependant un ecto.casterror, car une structure ecto.date ne peut pas être coulée à une structure ecto.datetime (puisque nous comparons les expressions Ecto.Date interpolées à un champ de type ecto.datetime ). Dans ce cas, nous pourrions soit construire une structure ecto.datetime, soit nous pourrions spécifier à ecto que nous aimerions lancer l'expression à ecto.Date au lieu d'Ecto.Datetime:
belongs_to <span>:user, Ectoing.User </span>
Maintenant, Ecto accepte joyeusement la requête. Après l'opération de distribution, il traduit ensuite l'expression Ecto.Date interpolée par le type de date sous-jacent: qui permet ensuite à la base de données sous-jacente (MySQL, dans ce cas) de gérer la comparaison entre une date et une datetime.
Revenons à notre exemple de la composition des requêtes ensemble, où nous avons effectué une recherche de nom d'utilisateur:
<span>SELECT id, username FROM users; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%"; </span><span>SELECT id, username FROM users WHERE username LIKE "%tp%" LIMIT 10, 0; </span>
Comme la requête de pagination qui est venue après, nous pouvons également généraliser cette requête, afin qu'elle puisse rechercher n'importe quel champ à partir d'une table donnée. Cela peut être fait en effectuant un accès dynamique sur le terrain:
offset <span>= 0 </span>username <span>= <span>"%tp%"</span> </span> <span># Keywords query syntax </span>get_users_overview <span>= from u in Ectoing.User, </span> <span>select: [u.id, u.username] </span> search_by_username <span>= from u in get_users_overview, </span> <span>where: like(u.username, ^username) </span> paginate_query <span>= from search_by_username, </span> <span>limit: 10, </span> <span>offset: ^offset </span> <span># Macro syntax </span>get_users_overview <span>= (Ectoing.User </span><span>|> select([u], [u.id, u.username])) </span> search_by_username <span>= (get_users_overview </span><span>|> where([u], like(u.username, ^username))) </span> paginate_query <span>= (search_by_username </span><span>|> limit(10) </span><span>|> offset(^offset)) </span> Ectoing<span>.Repo.all paginate_query </span>
La fonction Field / 2 est utilisée pour le moment où un champ doit être spécifié dynamiquement. Son premier argument est le tableau du champ à accéder, et le deuxième argument est le nom du champ lui-même, spécifié comme atome. À l'aide d'une requête générale comme celle ci-dessus, nous pouvons le résumer dans une fonction et utiliser des paramètres pour rechercher un champ donné à partir du tableau spécifié dans la requête donnée.
Dans cet article et mon article précédent sur la question de l'interrogation d'Ecto, nous avons couvert beaucoup de ce dont il est capable. Les fonctionnalités mentionnées doivent couvrir la grande majorité des cas rencontrés lors de l'utilisation de l'ECTO dans les applications. Mais il y a encore des sujets qui n'ont pas été couverts (comme la préfixe de requête). Il y a aussi toutes les nouvelles fonctionnalités à venir dans la version 2.0 très attendue d'ECTO, y compris les sous-requêtes, les requêtes d'agrégation et les associations de plusieurs à plusieurs. Ceux-ci, ainsi que d'autres fonctionnalités non spécifiques à la question de l'interrogation d'Ecto, seront couvertes dans les futurs articles - alors restez à l'écoute!
ECTO D'ECTO DSL (Langue spécifique au domaine) est un outil puissant pour interagir avec les bases de données. Il fournit un moyen d'écrire des requêtes dans une syntaxe proche de SQL, mais avec les avantages supplémentaires de la sécurité en temps de compilation, une meilleure intégration avec le code d'élixir et un potentiel d'abstraction et de réutilisation du code. C'est important car il permet aux développeurs d'écrire des requêtes complexes d'une manière plus lisible et maintenable, réduisant la probabilité d'erreurs et facilitant le code et la modification.
ecto fournit un moyen de définir des associations entre les tables en utilisant les macros has_many, has_one et appartiennent. Ces associations vous permettent d'interroger des données connexes de manière pratique et efficace. Par exemple, si vous avez un schéma utilisateur et que chaque utilisateur dispose de nombreux messages, vous pouvez récupérer tous les messages pour un utilisateur avec une question simple.Oui, Ecto prend en charge un large éventail d'opérations de requête, y compris les jointures, les sous-questionnaires et les agrégations. Vous pouvez utiliser le mot-clé de jointure pour rejoindre les tables, le mot clé à partir des sous-requêtes et des fonctions comme SUM, AVG, MIN et MAX pour effectuer des agrégations. Cela fait d'Ecto un outil puissant pour interroger les données de manière complexe.
ECTO fournit une fonction Repo.Transaction qui vous permet d'exécuter plusieurs opérations en une seule transaction. Si une opération échoue, toutes les modifications apportées dans la transaction sont annulées. Cela garantit la cohérence et l'intégrité des données.
Comment gère Ecto les migrations?
d'Ecto. Oui, ECTO fournit une fonction de EngageSet qui vous permet de valider les données avant qu'elle ne soit insérée ou mise à jour dans la base de données. Vous pouvez définir des règles de validation dans votre schéma, puis utiliser la fonction de changement de modification pour appliquer ces règles aux données.
Ecto utilise un pool de connexion pour gérer la base de données relations. Cela lui permet de gérer efficacement plusieurs requêtes simultanées, garantissant que votre application reste réactive même sous une charge lourde.
Oui, tandis que le DSL d'Ecto fournit un May de haut niveau et abstrait pour écrire des requêtes, vous pouvez également utiliser la fonction ecto.adapters.sql.Query pour exécuter des requêtes SQL brutes si nécessaire.
Ecto fournit une fonction ecto.query.api.dynamic qui vous permet de construire des requêtes dynamiquement, sans schéma prédéfini. Cela peut être utile lorsque vous devez créer des requêtes en fonction de l'entrée utilisateur ou d'autres données d'exécution.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!