首頁 >科技週邊 >IT業界 >elixir octo查詢DSL:超越基礎知識

elixir octo查詢DSL:超越基礎知識

Lisa Kudrow
Lisa Kudrow原創
2025-02-18 10:53:09942瀏覽

elixir octo查詢DSL:超越基礎知識

elixir octo查詢DSL:超越基礎知識

>本文基於我在理解Ecto查詢DSL:基礎知識的Ecto的基礎上的基礎。現在,我將探索ECTO的更高級功能,包括查詢組成,加入和協會,SQL片段注入,顯式鑄造和動態場訪問。

>

再次,假定了長生不老藥的基本知識,以及ECTO的基礎知識,我在Ecto庫的簡介中介紹了這一點。

鑰匙要點

    ecto允許在長生不老藥中查詢構圖,使開發人員能夠創建可重複使用的查詢,並將它們合併為烘乾機和更可維護的代碼。可以使用關鍵字查詢語法或宏語法來實現這一點。
  • > ecto提供了處理模型中的表關係(聯接和關聯)的能力。相關,使用has_one/3,has_many/3和屬於/3宏來定義,允許開發人員處理模型中以外鍵實現的表格關係。
  • >
  • ecto支持SQL片段注入,該功能允許使用Fragment/1函數直接將SQL代碼直接注入查詢中。當開發人員需要將其放回原始SQL中以進行不受ECTO功能不涵蓋的操作時,這很有用。
  • > ECTO支持明確的鑄造和動態場訪問。顯式鑄造允許開發人員指定應該施放表達式的類型,而動態字段訪問可以從給定表中搜索任何字段,從而使查詢更加通用和通用。
  • QUERY組成
>可以將ECTO中的單獨查詢組合在一起,從而可以創建可重複使用的查詢。 例如,讓我們看看如何創建三個單獨的查詢並將它們組合在一起以實現烘乾機和更多可重複使用的代碼:>

SQL版本的重複性很高,但另一方面的ECTO版本非常乾燥。第一個查詢(get_users_overview)只是檢索基本用戶信息的通用查詢。第二個查詢(search_by_username)通過根據我們正在搜索的某些用戶名過濾用戶名來構建第一個查詢。第三個查詢(paginate_query)是在第二個構建的,它限制了結果並將它們從特定偏移量中獲取(為分頁提供基礎)。

>

>不難想像所有以上三個查詢都可以一起使用,以提供搜索特定用戶的搜索結果。每個都可以與其他查詢一起使用每個應用程序需求,而無需不必要地重複整個代碼庫中查詢的部分。 >>>>。

>加入和關聯

>在查詢時加入非常基本,但我們只是現在介紹它們。之所以這樣做,是因為僅學習加入ECTO並沒有用:我們也需要了解關聯。儘管這些並不難,但它們並不像到目前為止其他主題那麼小。 簡單地說,關聯使開發人員能夠在模型中處理表關係(以外鍵實現)。它們在每個模型的模式中使用has_one/3和has_many/3宏(對於包含其他模型的模型)和ander_to/3宏(對於其他模型與其他模型 - 具有外鍵的模型)定義了它們。 。

查看我們的ectoing應用程序,我們可以看到一個ectoing.user模型與ectoing.message模型之間的關聯的一個示例。 ectoing.user中定義的架構定義以下關聯:

我們可以看到一個用戶有許多消息(ectoing.message),我們正在調用此關聯:消息。

在ectoing.message模型中,我們定義以下關聯關係:>
<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>

>在這裡,我們說的是模型,ectoing.message,屬於ectoing.user模型。我們還將協會命名為:用戶。默認情況下,ECTO將把_ID附加到屬於關聯名稱上,並將其用作外鍵名(因此,在這裡是:user_id)。可以通過指定forefer_key選項手動指定外鍵名,可以覆蓋此默認行為。例如:

>現在讓我們看一下一個簡單的查詢,該查詢使用JOIN來獲取用戶及其消息:
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>
>

has_many <span>:messages, Ectoing.Message
</span>
返回的值:

belongs_to <span>:user, Ectoing.User
</span>
明顯,我們有許多卸載的關聯,包括:消息協會。加載此關聯可以通過兩種方式之一:從查詢的結果集或查詢本身內部進行。可以使用repo.preload函數來完成從結果集中的加載關聯:
<span># Ectoing.Message
</span>belongs_to <span>:user, Ectoing.User, foreign_key: some_other_fk_name
</span>

可以使用聯合和預加載函數的組合來完成查詢內的

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

現在,我們在結果中加載了消息關聯:
<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>

>關聯隱式地加入了我們的主密鑰和外鍵列,因此我們不必指定一個:on子句。從上面,我們還可以看到,在預加載協會方面,它們並不是懶惰的。如果需要,必須明確加載關聯。

>
<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>
由於本文專門針對ECTO的查詢DSL,因此我們不會在此處介紹關聯的插入,更新或刪除。有關此信息的更多信息,請查看與ECTO協會和嵌入的博客文章。SQL片段注入

> ecto為我們提供了很多功能,但它僅為SQL中的常見操作提供功能(它並不是要模仿整個SQL語言)。當我們需要放回原始SQL中時,我們可以使用片段/1函數,使SQL代碼直接注入查詢中。

例如,讓我們在用戶名字段上執行對案例敏感的搜索:>

(以上包含MySQL特定的SQL。如果您使用的是另一個數據庫,則對您不起作用。)
<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>
fragment/1函數將SQL代碼作為字符串,我們想將其註入第一個參數。它使列和值可以綁定到SQL代碼片段。這是通過字符串中的佔位符(作為問號)完成的,隨後的論點分別傳給了片段。

>顯式鑄造

ecto使用模型的架構定義的另一種方式是,將查詢中的插值表達式自動施放到架構中定義的相應字段類型中。這些插值表達式被施放在與之比較的場的類型上。例如,如果我們有一個查詢片段,例如u.username> ^用戶名,其中u.username定義為字段:用戶名,架構中的字符串,則用戶名變量將被ecto自動施加到字符串。 > 但是,有時,我們並不總是希望eTo將插值表達式施加給定義的字段類型。在其他時候,ECTO將無法推斷出表達式的類型(通常是涉及SQL代碼的片段時)。在這兩種情況下,我們都可以使用類型/2函數來指定表達式及其應施放的類型。

>讓我們以第一個想要將表達式施放到另一種類型的情況下,因為這是更有趣的場景。在我們的ectoing應用程序中,我們使用了ecto.schema.timestamps宏來向我們的每個表添加兩個額外的字段:updated_at and inserted_at。默認情況下,宏將這些字段的類型設置為具有ECTO.DATETIME的類型。現在,如果我們想查看本月註冊了多少用戶,我們可以使用以下簡單查詢:

>

但是,這將為我們提供一個ecto.casterror,因為無法將ecto.date struct施加到ecto.dateTime struct(因為我們正在比較interpolated ecto.ddate表達式與ecto.dateTime類型的字段)。在這種情況下,我們可以構建一個ecto.datetime struct,或者我們可以指定要將表達式施加到ecto.date而不是ecto.datetime:>

現在,Ecto愉快地接受了查詢。在鑄造操作後,然後將插值的ecto.date表達式轉換為基礎:日期類型,然後讓基礎數據庫(在這種情況下為MySQL)處理日期和dateTime之間的比較。

>動態字段訪問

>讓我們從一起編寫查詢的示例,我們在其中執行了用戶名搜索:

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

>當需要動態指定字段時,使用字段/2函數。它的第一個參數是要訪問的字段表,第二個參數是該字段的名稱本身,被指定為原子。使用以上的常規查詢,我們可以將其封裝在函數中,並使用參數從給定查詢中指定的表中搜索任何給定字段。

結論

在這本書和我的上一篇有關ECTO查詢DSL的文章中,我們涵蓋了很多它的功能。提到的功能應涵蓋應用程序中使用ECTO時遇到的絕大多數案例。但是仍然有一些尚未涵蓋的主題(例如查詢前綴)。 ECTO備受期待的2.0版本中還有所有即將推出的新功能,包括子查詢,聚合查詢和多一對一的關聯。這些以及其他不具體特定的ECTO查詢DSL的功能將在以後的文章中介紹 - 請繼續關注!

>

經常詢問有關長生素的ecto查詢dsl

的問題(常見問題解答)

> elixir的ecto查詢DSL是什麼,為什麼重要?它提供了一種在靠近SQL的語法中編寫查詢的方法,但具有編譯時安全性的額外好處,與Elixir代碼更好地集成以及抽象和代碼重複使用的潛力。這很重要,因為它允許開發人員以更可讀和可維護的方式編寫複雜的查詢,從而降低了錯誤的可能性,並使代碼更易於理解和修改。

> ecto如何處理表之間的關聯? ecto提供了一種使用has_many,has_one和alters_to宏來定義表之間關聯的方法。這些關聯使您可以以方便有效的方式查詢相關數據。例如,如果您有一個用戶架構並且每個用戶都有許多帖子,則可以使用簡單查詢的用戶檢索所有帖子。

我可以使用eTeco執行涉及加入,子查詢和聚合的複雜查詢是的,是的,ecto支持廣泛的查詢操作,包括加入,子查詢和聚合。您可以使用JOIN關鍵字加入表,從關鍵字創建子征服,以及諸如SUM,AVG,MIN和MAX之類的功能來執行聚合。這使ECTO成為以復雜方式查詢數據的強大工具。

> ecto如何處理交易?

ecto提供repo.Transaction函數,使您可以在單個事務中執行多個操作。如果任何操作失敗,則交易中所做的所有更改都會回滾。這樣可以確保數據的一致性和完整性。

>我可以將ECTO與postgresql?

​​

以外的數據庫一起使用,而ECTO最初是設計用於與PostgreSQL合作的,現在它也支持其他數據庫,包括MySQL和MySQL和MySQL和MySQL和sqlite。您可以在設置ECTO存儲庫時指定數據庫類型。

>

> ecto如何處理遷移?

ecto提供了一個可靠的遷移系統,允許您創建,修改和刪除數據庫表中的數據庫表一種受控和可逆的方式。您可以使用混合任務生成遷移文件,然後使用ECTO的DSL來定義遷移文件中的更改。

>我可以在插入或在數據庫中插入或更新數據之前使用ECTO驗證數據嗎?是的,ECTO提供了一個更改功能,該功能允許您在數據庫中插入或更新數據之前驗證數據。您可以在架構中定義驗證規則,然後使用更改功能將這些規則應用於數據。

>

> eTOTO如何處理數據庫連接?

連接。這允許它有效處理多個並發查詢,確保您的應用程序即使在重負載下也保持響應。

>

>我可以使用ECTO執行RAW SQL查詢嗎?高級,抽象的編寫查詢方式,您也可以使用ecto.adapters.sql.query函數在需要時執行RAW SQL查詢。

> ecto如何處理schemaless詢​​問? ECTO提供了一個ecto.query.api.Dynamic函數,可讓您動態構建查詢,而無需預定義的架構。當您需要根據用戶輸入或其他運行時數據構建查詢時,這可能很有用。

以上是elixir octo查詢DSL:超越基礎知識的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn