Heim >Technologie-Peripheriegeräte >IT Industrie >Ecto -Abfragen von DSL: Jenseits der Grundlagen

Ecto -Abfragen von DSL: Jenseits der Grundlagen

Lisa Kudrow
Lisa KudrowOriginal
2025-02-18 10:53:09942Durchsuche

Ecto -Abfragen von DSL: Jenseits der Grundlagen

Ecto -Abfragen von DSL: Jenseits der Grundlagen

Dieser Artikel baut auf den Grundlagen von Ecto auf, die ich mit dem Verständnis von Elixirs ECTO -Abfrage DSL: Die Grundlagen behandelt habe. Ich werde jetzt die fortgeschritteneren Funktionen von ECTO untersuchen, einschließlich Abfragezusammensetzung, Zusammenstellungen und Assoziationen, SQL -Fragmentinjektion, explizites Gießen und dynamischer Feldzugriff.

erneut wird ein Grundkenntnis von Elixir sowie die Grundlagen von Ecto angenommen, die ich in einer Einführung in die ECTO -Bibliothek von Elixir behandelt habe.

Key Takeaways

  • ECTO ermöglicht die Abfragezusammensetzung in Elixir, sodass Entwickler wiederverwendbare Abfragen erstellen und für Trockner und mehr wartbarer Code kombinieren können. Dies kann entweder mit der Keywords -Abfrage -Syntax oder der Makrosyntax erreicht werden.
  • ecto bietet die Möglichkeit, Tabellenbeziehungen (Verbindungen und Assoziationen) in den Modellen zu handhaben. Assoziationen, die unter Verwendung der Has_one/3, Has_Many/3 definiert sind und zu tun haben, können Entwickler in den Modellen als Fremdschlüssel implementiert werden.
  • .
  • ecto unterstützt die SQL -Fragmentinjektion, eine Funktion, mit der SQL -Code direkt unter Verwendung der Fragment/1 -Funktion in eine Abfrage injiziert wird. Dies ist nützlich, wenn Entwickler für Operationen, die nicht von den Funktionen von Ecto abgedeckt sind, wieder in RAW SQL fallen müssen.
  • ecto unterstützt explizite Casting und dynamische Feldzugriff. Das explizite Casting ermöglicht es Entwicklern, den Typ anzugeben, an den ein Ausdruck gegossen werden sollte, während der dynamische Feldzugriff ein Feld aus einer bestimmten Tabelle durchsucht und Abfragen allgemeiner und vielseitiger macht.

Abfragezusammensetzung

separate Abfragen in ecto können miteinander kombiniert werden, sodass wiederverwendbare Abfragen erstellt werden können.

Lassen Sie uns beispielsweise sehen, wie wir drei separate Abfragen erstellen und sie miteinander kombinieren können, um Trockner und wiederverwendbarerer Code zu erreichen:

<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>

Die SQL -Version ist ziemlich wiederholend, aber die ECTO -Version ist dagegen ziemlich trocken. Die erste Abfrage (get_users_overview) ist nur eine generische Abfrage zum Abrufen grundlegender Benutzerinformationen. Die zweite Abfrage (Search_By_username) erstellt die erste, indem Benutzernamen gemäß einem Benutzernamen gefiltert werden, nach dem wir suchen. Die dritte Abfrage (Paginate_Query) baut die zweite auf, wo sie die Ergebnisse einschränkt und von einem bestimmten Versatz abbricht (um die Grundlage für die Paginierung zu liefern).

Es ist nicht schwer vorstellbar, dass alle oben genannten drei Abfragen zusammen verwendet werden können, um Suchergebnisse zu liefern, wenn ein bestimmter Benutzer gesucht wird. Jeder kann auch in Verbindung mit anderen Abfragen verwendet werden, um auch andere Anwendungsanforderungen auszuführen, ohne unnötig Teile der Abfrage in der gesamten Codebasis zu wiederholen.

schließt sich und Assoziationen

Joins sind beim Abfragen ziemlich grundlegend, und dennoch deckt wir sie gerade erst ab. Der Grund dafür ist, dass es nicht nützlich ist, sich über ECTO -Anschlüsse zu lernen: Wir müssen auch über Assoziationen wissen. Diese sind zwar nicht schwer zu lernen, aber sie sind nicht ganz so trivial wie die anderen bisher behandelten Themen.

Einfach ausgedrückt, Assoziationen ermöglichen es Entwicklern, in den Modellen Tabellenbeziehungen (als Fremdschlüssel) zu handhaben. Sie sind in den Schemas für jedes Modell mithilfe der Makros von Has_one/3 und Has_Many/3 definiert (für Modelle, die andere Modelle enthalten) und das Makro von zusammenhings_to/3 (für Modelle, die von anderen Modellen getrennt sind - jene mit Fremdschlüssel) .

Wenn wir unsere Ektoanwendung betrachten, können wir ein Beispiel für einen Zusammenhang zwischen dem Modell des Ectoing.Users und dem Ectoing.Message -Modell sehen. Das in Ectoing definierte Schema definiert die folgende Assoziation:

<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>

Wir können sehen, dass ein Benutzer viele Nachrichten (ectoing.message) hat, und wir nennen folgende Assoziation: Nachrichten.

Im Modell von Ectoing.Message definieren wir die folgende Assoziationsbeziehung:

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>

Hier sagen wir, dass das Modell Ectoing.Message zum Ectoing.User -Modell gehört. Wir haben auch die Vereinigung als: Benutzer benannt. Standardmäßig wird ECTO einen _ID an den Name der adocise_to -Association anhängen und dies als fremden Schlüsseldamen verwenden (hier wäre es: user_id). Dieses Standardverhalten kann überschrieben werden, indem der Fremdschlüsselname manuell angegeben wird, indem die Option fremd_key angegeben wird. Zum Beispiel:

has_many <span>:messages, Ectoing.Message
</span>

Schauen wir uns nun eine einfache Abfrage an, bei der ein Benutzer ein Benutzer und seine Nachrichten abrufen:

belongs_to <span>:user, Ectoing.User
</span>
<span># Ectoing.Message
</span>belongs_to <span>:user, Ectoing.User, foreign_key: some_other_fk_name
</span>

zurückgegebener Wert:

<span>SELECT * FROM users u INNER JOIN messages m ON u.id = m.user_id WHERE u.id = 4;
</span>

merklich, wir haben eine Reihe von entladenen Assoziationen, einschließlich der: Message Association. Das Laden dieser Assoziation kann auf eine von zwei Arten durchgeführt werden: aus dem Ergebnissatz einer Abfrage oder aus der Abfrage selbst. Das Laden von Assoziationen aus einem Ergebnissatz kann mit der Repo.Preload -Funktion erfolgen:

<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>

Laden von Assoziationen aus einer Abfrage können unter Verwendung einer Kombination aus Assoc- und Vorspannungsfunktionen durchgeführt werden:

<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>

Jetzt haben wir die Nachrichtenvereinigung im Ergebnis geladen:

<span>SELECT * FROM users u INNER JOIN messages m ON u.id = m.user_id WHERE u.id = 4;
</span>

Assoziationen verbinden uns implizit an den Primärschlüssel- und Fremdschlüsselspalten für uns, und daher müssen wir nicht angeben: in der Klausel. Aus diesem Grund können wir auch erkennen, dass sie bei Vorspannverbänden nicht faul geladen sind. Assoziationen müssen explizit geladen werden, wenn sie gesucht werden.

Da sich dieser Artikel speziell auf die Abfrage von ECTO -Abfragen von DSL konzentriert, werden wir hier nicht das Einsetzen, Aktualisieren oder Löschen von Assoziationen abdecken. Weitere Informationen hierzu finden Sie im Blog -Beitrag, der mit ECTO -Assoziationen und Einbettungen arbeitet.

SQL -Fragmentinjektion

Während ECTO uns viel Funktionalität bietet, bietet es nur Funktionen für gemeinsame Operationen in SQL (es zielt nicht darauf ab, die gesamte SQL -Sprache zu emulieren). Wenn wir wieder in RAW SQL fallen müssen, können wir die Fragment/1 -Funktion verwenden, um SQL -Code direkt in eine Abfrage injiziert zu werden.

Führen Sie beispielsweise eine Fall-sensitive Suche im Benutzernamenfeld durch:

<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>

(Das obige enthält MySQL-spezifische SQL. Wenn Sie eine andere Datenbank verwenden, funktioniert dies nicht für Sie.)

Das Fragment/1 -Funktion nimmt den SQL -Code als Zeichenfolge, den wir als erster Parameter einfügen möchten. Es ermöglicht, dass Spalten und Werte an das SQL -Codefragment gebunden werden. Dies erfolgt über Platzhalter (als Fragezeichen) in der Zeichenfolge, wobei nachfolgende Argumente an Fragment übergeben werden, die an jeden Platzhalter gebunden sind.

explizites Casting

Eine andere Art und Weise, wie ECTO die Schemat -Definitionen der Modelle verwendet, besteht darin, interpolierte Ausdrücke automatisch in Abfragen auf die jeweiligen Feldtypen zu gießen, die im Schema definiert sind. Diese interpolierten Ausdrücke werden in den Typ des Feldes gegossen, mit dem sie verglichen werden. Wenn wir beispielsweise ein Abfragefragment wie U.Susername> ^Benutzername haben, wobei U.Susername als Feld definiert ist: Benutzername,: String im Schema, wird die Benutzername -Variable automatisch von Ecto an eine Zeichenfolge gegossen.

Manchmal möchten wir jedoch nicht immer, dass Ecto interpolierte Ausdrücke auf die definierten Feldtypen gießen. Und in anderen Fällen kann ECTO den Typ nicht schließen, um einen Ausdruck zu werfen (in der Regel sind Fragmente des SQL -Codes beteiligt). In beiden Fällen können wir die Funktion Typ/2 verwenden, um den Ausdruck und den Typ anzugeben, auf den er gegossen werden sollte.

Nehmen wir den ersten Fall, wenn wir einen Ausdruck in einen anderen Typen geben möchten, da dies das interessantere Szenario ist. In unserer Ectoing -Anwendung haben wir das Makro von Ecto.Schema.Timestamps verwendet, um jedem unserer Tabellen zwei zusätzliche Felder hinzuzufügen: updated_at und incised_at. Das Makro setzt standardmäßig die Art dieser Felder auf eine Art Ecto.Datetime fest. Wenn wir jetzt sehen möchten, wie viele Benutzer sich im aktuellen Monat registriert haben, können wir eine einfache Abfrage wie folgt verwenden:

has_many <span>:messages, Ectoing.Message
</span>

Dies wird uns jedoch einen Ecto.Casterror geben, da eine ecto.date -Struktur nicht auf eine Ecto.DateTime -Struktur gegossen werden kann ). In diesem Fall konnten wir entweder eine Ecto.Datetime -Struktur erstellen, oder wir könnten angeben, dass wir den Ausdruck auf ecto.date anstelle von ecto.datetime angeben möchten:

belongs_to <span>:user, Ectoing.User
</span>

Jetzt akzeptiert Ecto die Abfrage glücklich. Nach dem Gussbetrieb übersetzt es den interpolierten Ecto.Date -Ausdruck in den zugrunde liegenden: Datumstyp, wodurch die zugrunde liegende Datenbank (MySQL, in diesem Fall) den Vergleich zwischen einem Datum und einer DateTime -Zeit verhandelt.

.

dynamisches Feldzugriff

Kehren wir zu unserem Beispiel zurück, indem wir gemeinsam Abfragen komponieren, wo wir eine Benutzername -Suche durchgeführt haben:

<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>

Wie die danach gekommene Paginierungsabfrage können wir diese Abfrage auch verallgemeinern, damit sie jedes Feld aus einer bestimmten Tabelle durchsuchen kann. Dies kann durch Ausführen eines dynamischen Feldzugriffs erfolgen:

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>

Das Feld/2 -Funktion wird verwendet, wenn ein Feld dynamisch angegeben werden muss. Sein erstes Argument ist die Tabelle des Feldes, die zugegriffen werden muss, und das zweite Argument ist der als Atom angegebene Name des Feldes selbst. Wenn wir eine allgemeine Abfrage wie die oben genannten verwenden, können wir sie innerhalb einer Funktion zusammenschließen und Parameter verwenden, um nach einem bestimmten Feld aus der in der angegebenen Abfrage angegebenen Tabelle zu suchen.

Schlussfolgerung

In diesem und in meinem vorherigen Artikel über Ectos Abfrage -DSL haben wir eine Menge von dem behandelt, wozu es fähig ist. Die genannten Merkmale sollten die überwiegende Mehrheit der Fälle abdecken, die bei der Verwendung von ECTO innerhalb von Anwendungen auftreten. Es gibt jedoch immer noch einige Themen, die nicht behandelt wurden (z. B. das Präfixieren von Abfragen). Es gibt auch alle neuen bevorstehenden Funktionen in der mit Spannung erwarteten 2.0-Veröffentlichung von ECTO, einschließlich Unterfragen, Aggregationsabfragen und vielen zu vielen Verbänden. Diese sowie andere Funktionen, die nicht spezifisch für die Abfrage von Ecto dSL sind, werden in zukünftigen Artikeln behandelt. Bleiben Sie also dran!

häufig gestellte Fragen (FAQs) zu Elixirs Ecto -Abfrage DSL

Was ist ECTO -Abfrage von Elixir DSL und warum ist es wichtig? Es bietet eine Möglichkeit, Abfragen in eine Syntax zu schreiben, die sich in der Nähe von SQL befindet. Mit den zusätzlichen Vorteilen der Kompilierungszeitsicherheit, einer besseren Integration mit Elixiercode und dem Potenzial für Abstraktion und Code-Wiederverwendung. Es ist wichtig, weil es Entwicklern ermöglicht, komplexe Abfragen auf lesbarere und wartbare Weise zu schreiben, die Wahrscheinlichkeit von Fehlern zu verringern und den Code leichter zu verstehen und zu ändern.

ecto bietet eine Möglichkeit, Assoziationen zwischen Tabellen mithilfe der Makros von Has_Many, Has_one zu definieren. Diese Assoziationen ermöglichen es Ihnen, verwandte Daten bequem und effizient abzufragen. Wenn Sie beispielsweise ein Benutzerschema haben und jeder Benutzer viele Beiträge hat, können Sie alle Beiträge für einen Benutzer mit einer einfachen Abfrage abrufen. ? Sie können das Schlüsselwort Join verwenden, um Tabellen zu verbinden, das aus dem Schlüsselwort aus dem Schlüsselwort zu erstellen, um Unterabfragen zu erstellen, und Funktionen wie Summe, AVG, Min und Max, um Aggregationen durchzuführen. Dies macht Ecto zu einem leistungsstarken Werkzeug zum Abfragen von Daten auf komplexe Weise.

Wie handelt es sich bei ECTO -Transaktionen mit Transaktionen? Wenn ein Betrieb fehlschlägt, werden alle Änderungen innerhalb der Transaktion zurückgerollt. Dies stellt die Datenkonsistenz und Integrität sicher. Sqlite. Sie können den Datenbanktyp angeben, wenn Sie Ihr ECTO -Repository einrichten. ein kontrollierter und reversibler Weg. Sie können Migrationsdateien mithilfe von Mix -Aufgaben generieren und dann die Änderungen in der Migrationsdatei mithilfe der DSL von ECTO definieren. Ja, ECTO bietet eine Änderungsset -Funktion, mit der Sie Daten validieren können, bevor sie in der Datenbank eingefügt oder aktualisiert werden. Sie können Validierungsregeln in Ihrem Schema definieren und dann die Änderungsset -Funktion verwenden, um diese Regeln auf die Daten anzuwenden. Verbindungen. Dies ermöglicht es ihm, mehrere gleichzeitige Abfragen effizient zu behandeln und sicherzustellen, dass Ihre Anwendung auch unter starker Belastung reagiert. High-Level, abstrahierter Weg zum Schreiben von Abfragen können Sie auch die Funktion ecto.adapters.sql.Query verwenden, um bei Bedarf rohe SQL-Abfragen auszuführen. ECTO bietet eine Ecto.query.api.Dynamische Funktion, mit der Sie ohne vordefinierte Schema Abfragen dynamisch erstellen können. Dies kann nützlich sein, wenn Sie Abfragen basierend auf Benutzereingaben oder anderen Laufzeitdaten erstellen müssen.

Das obige ist der detaillierte Inhalt vonEcto -Abfragen von DSL: Jenseits der Grundlagen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn