首页 > 科技周边 > IT业界 > elixir octo查询DSL:超越基础知识

elixir octo查询DSL:超越基础知识

Lisa Kudrow
发布: 2025-02-18 10:53:09
原创
931 人浏览过

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
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板