作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
星质 是用于编写查询和与数据库交互的领域特定语言吗 灵丹妙药的语言. 最新版本(2.0)支持PostgreSQL和MySQL. (对MSSQL、SQLite和MongoDB的支持将在未来提供). 如果你是新来的 长生不老药 或者没有经验的人,我建议你们读读Kleber Virgilio Correia的书 开始使用长生不老药编程语言.
星质由四个主要部分组成:
对于本教程,您将需要:
首先,让我们使用混合创建一个带有监控器的新应用程序. 混合 是长生不老药附带的构建工具,提供创建任务, 编译, 测试应用程序, 管理它的依赖关系等等.
混合新购物车-sup
这将创建一个包含初始项目文件的目录购物车:
*创建自述文件.md
*创建 .gitignore
*创造混合.练习
*创建配置
*创建配置/配置.练习
*创建lib
*创建lib/星质_tut.ex
*创建测试
*创建test/test_helper.练习
*创建test/星质_tut_test.练习
我们正在使用 ——吃晚饭
选项,因为我们需要一个管理树来保持与数据库的连接. 接下来,我们去 车
目录 cd购物车
然后打开文件 混合.练习
并替换其内容:
defmodule购物车.混合file做
使用混合.项目
defproject do
(应用程序::购物车,
版本:“0.0.1",
elixir: "~> 1.2",
build_embedded:混合.Env ==:
start_permanent:混合.Env ==:
deps deps):
结束
defapplication do
[applications: [:logger,:星质,:postgrex],
mod:{购物车,[]}]
结束
输入“混合 help deps”查看更多示例和选项
Defp deps可以
[{:postgrex, ">= 0.11.1"},
{:星质, "~> 2.0"}]
结束
结束
In defapplication do
我们必须添加应用程序 : postgrex:星质
这些可以在我们的应用程序中使用. 我们还需要把它们作为依赖项添加进来 Defp deps可以
postgrex (它是数据库适配器)和 星质. 编辑完文件后,在控制台中运行:
混合deps.得到
这将安装所有依赖项并创建一个文件 混合.锁
它存储已安装包的所有依赖项和子依赖项(类似于 Gemfile.锁
打包机).
现在我们来看看如何在应用程序中定义一个回购. 我们可以有多个回购,这意味着我们可以连接到多个数据库. 我们需要在文件中配置数据库 配置/配置.练习
:
使用混合.配置
config:购物车,星质_回购s:[购物车。.回购)
我们只是设置了最小值,所以我们可以运行下一个命令. 用线条 :购物车,车_回购s:[购物车.回购)
我们告诉星质我们正在使用哪个仓库. 这是一个很酷的功能,因为它允许我们有许多仓库.e. 我们可以连接到多个数据库.
现在运行以下命令:
混合星质.创.回购
==> connection
编译1文件(.ex)
生成连接应用程序
==> poolboy (compile)
src / poolboy_worker编译.话务量
src / poolboy_sup编译.话务量
src / poolboy编译.话务量
==> decimal
编译1文件(.ex)
生成十进制应用程序
==> db_connection
编译23个文件(.ex)
生成db_connection应用
==> postgrex
编译43个文件(.ex)
生成postgrex应用程序
==> 星质
编译68个文件(.ex)
生成星质应用程序
==> 车
*创建lib /车
*创建lib /车/回购.ex
*更新配置/配置.练习
不要忘记将新的回购添加到您的监督树中
(通常在lib /车中.ex):
主管(车.回购,[])
并将其添加到配置文件中的星质存储库列表中(以便星质任务按预期工作):
配置:购物车,
星质_回购s(购物车:.回购)
该命令生成回购. 如果你读了输出,它会告诉你在你的应用中添加一个supervisor和回购. 让我们从主管开始. 我们将编辑 lib /车.ex
:
defmodule购物车
使用应用程序
Def start(_type, _args
进口主管.Spec, warn: false
孩子= [
主管(车.回购,[])
]
opts = [strategy::one_for_one, 名字。: 车 ..主管)
主管.start_link(孩子,选择)
结束
结束
在这个文件中,我们定义了管理器 主管(车.回购,[])
并将其添加到子列表中(在长生不老药中,列表类似于数组)。. 我们用这个策略来定义受监督的孩子 策略:one_for_one
也就是说, 如果其中一个监督过程失败, 管理器只会将该进程重启到默认状态. 你可以了解更多关于主管的信息 在这里. 如果你看看 lib /车/回购.ex
您将看到这个文件已经创建,这意味着我们有一个 回购 对于我们的应用程序.
defmodule购物车.回购做
使用星质.回购,otp_app::购物车
结束
现在让我们编辑配置文件 配置/配置.练习
:
使用混合.配置
config:购物车,星质_回购s:[购物车。.回购)
配置:购物车,购物车.回购,
适配器:星质.适配器.Postgres、
数据库:“车_dev”,
用户名:“postgres”,
密码:“postgres”,
主机名:“localhost”
定义了数据库的所有配置后,我们现在可以通过运行:
混合星质.创建
该命令创建了数据库,至此,我们基本上完成了配置. 现在我们已经准备好开始编码,但让我们首先定义应用程序的范围.
对于我们的演示应用程序,我们将构建一个简单的发票工具. 对于变更集(模型),我们将拥有 发票, 项 和 发票项. 发票项 属于 发票 和 项. 下图显示了我们的模型是如何相互关联的:
这个图很简单. 我们有一张桌子 发票 有很多 发票_项目 我们在哪里存储所有的细节和一个表 项目 有很多 发票_项目. 你可以看到for的类型 发票_id 和 项_id in 发票_项目 表是UUID. 我们使用UUID是因为它有助于混淆路由, 如果你想通过API公开应用程序,并使其更容易同步,因为你不依赖于序列号. 现在让我们使用混合任务创建表.
迁移是用于修改数据库模式的文件. 星质.迁移 提供一组创建表的方法, 添加索引, 创建约束, 以及其他与图式相关的东西. 迁移确实有助于保持应用程序与数据库的同步. 让我们为第一个表创建一个迁移脚本:
混合星质.创.迁移创建_发票
这将生成一个类似于 priv /回购/迁移/ 20160614115844 _创建_发票.练习
我们将在哪里定义迁移. 打开生成的文件,将其内容修改如下:
defmodule购物车.回购.迁移.Create发票s做
使用星质.迁移
Def change do
创建表(:发票,primary_key:假
添加:id,:uuid, primary_key: true
添加:客户,:文本
添加:数量,:小数,精度:12,比例:2
添加:平衡,:小数,精度:12,比例:2
添加:date,:date
时间戳
结束
结束
结束
内部方法 Def change do
我们定义将为数据库生成SQL的模式. 创建表(:发票,primary_key:假
将创建表格 发票. 我们有 primary_key:假
但我们将添加一个ID字段的类型 UUID,客户字段为文本类型,日期字段为日期类型. 的 时间戳
方法将生成字段 插入ed_at
和 updated_at
星质会自动填充插入记录的时间和更新记录的时间, 分别. 现在转到控制台并运行迁移:
混合星质.迁移
我们已经创建了表 发票
S和所有已定义的字段. 让我们创建 项目 表:
混合星质.创.迁移创建_项目
现在编辑生成的迁移脚本:
defmodule购物车.回购.迁移.创建项做
使用星质.迁移
Def change do
创建表(:项目, primary_key:假
添加:id,:uuid, primary_key: true
添加:姓名,:文字
添加:价格,:小数,精度:12,比例:2
时间戳
结束
结束
结束
这里的新东西是十进制字段,它允许12位数的数字, 其中2个是小数部分. 让我们再次运行迁移:
混合星质.迁移
现在我们创建了 项目 表,最后创建 发票_项目 表:
混合星质.创.迁移创建_发票_项目
编辑迁移:
defmodule购物车.回购.迁移.Create发票项s做
使用星质.迁移
Def change do
创建表(:发票_项目, primary_key:假
添加:id,:uuid, primary_key: true
添加:发票_id,引用(:发票,类型::uuid, null: false)
添加: 项_id, references(:项目, type::uuid, null: false)
添加:价格,:小数,精度:12,比例:2
添加:数量,小数,精度:12,比例:2
添加:小计,:小数,精度:12,比例:2
时间戳
结束
创建索引(:发票_项目, [:发票_id])
创建索引(:发票_项目, [: 项_id])
结束
结束
正如您所看到的,这次迁移有一些新的部分. 首先你会注意到的是 添加:发票_id,引用(:发票,类型::uuid, null: false)
. 这将创建字段 发票_id 的数据库中的约束 发票 table. 我们有相同的模式 项_id 场. 另一个不同之处在于我们创建索引的方式: 创建索引(:发票_项目, [:发票_id])
创建索引 发票_项目_发票_id_index.
在星质, 星质.模型
已被弃用,改为使用 星质.模式
,所以我们将这些模块称为模式而不是模型. 让我们创建变更集. 我们将从最简单的变更集项开始,并创建该文件 lib /车/项.ex
:
defmodule购物车.项目做
使用星质.模式
进口星质.变更集
别名购物车.发票项
@primary_key {:id,:binary_id, auto创erate: true}
模式“项”可以
字段:名字。,:string
字段:价格,:十进制,精度:12,比例:2
has_many:发票_项目, 发票项
时间戳
结束
@字段 ~w(名称价格)
Def changesset (data, params \\ %{}
data
|> 铸造(params @字段)
|> 有效的ate_required([:名称、价格)
|> 有效的ate_number(:价格,greater_than_or_equal_to:十进制.新(0))
结束
结束
在顶部,我们使用 使用星质.模式
. 我们也在使用 进口星质.变更集
从 星质.变更集. 我们可以指定要导入哪些特定的方法,但让我们保持简单. 的 别名购物车.发票项
允许我们直接在变更集中进行编写 发票项,你马上就会看到.
的 @primary_key {:id,:binary_id, auto创erate: true}
指定主键将自动生成. 因为我们使用的是UUID类型,所以我们用 模式“项”可以
在块中,我们定义每个字段和关系. 我们定义 名字。 作为字符串和 价格 作为小数,非常类似于迁移. 接下来是宏 has_many:发票_项目, 发票项
之间的关系 项 和 发票项. 根据惯例,我们给这个字段命名 项_id 在 发票_项目 表中,我们不需要配置外键. 最后, 时间戳 方法将设置 插入ed_at 和 updated_at 字段.
的 Def changesset (data, params \\ %{}
函数接收带有参数的长生不老药结构体 管 通过不同的功能. 铸造(params @字段)
将值转换为正确的类型. 例如, 您只能在参数中传递字符串,这些字符串将被转换为模式中定义的正确类型. 有效的ate_required([:名称、价格)
验证 名字。 和 价格 字段是存在的, 有效的ate_number(:价格,greater_than_or_equal_to:十进制.新(0))
验证数字是否大于或等于0,在本例中为 小数.新(0)
.
这让我难以接受, 那么,让我们在控制台中通过示例来看看这个,以便您更好地掌握概念:
ix -S混合
这将加载控制台. - s混合
将当前项目加载到iex REPL中.
iex(0)> 项 = 车.项.变更集(%购物车.项目{},%{名称:"纸张",价格:.5"})
#星质.变更集},
errors: [], 数据:#车.项<>, 有效的?: true>
这将返回 星质.变更集
结构,该结构有效且没有错误. 现在保存它:
iex(1)> 项 = 车.回购.插入!(项)
%购物车.{__meta__: #星质物品.模式.Metadata<:loaded, "项目">,
id:“66 ab2ab7 - 966 d - 4 - b11 - b359 - 019 - a422328d7”,
插入ed_at: #星质.日期Time<2016-06-18 16:54:54>,
发票_项目: #星质.协会.NotLoaded,
名字。: "Paper", 价格: #小数<2.5>,
updated_at: #星质.日期Time<2016-06-18 16:54:54>}
为简洁起见,我们不展示SQL. 在本例中,它返回 车.项 struct的所有值都设置好了,你可以看到 插入ed_at 和 updated_at 包含它们的时间戳和 id 字段有一个UUID值. 让我们看一些其他的例子:
iex(3)> 项2 = 车.项.变更集(%购物车.项{价格:小数.新(20)}, %{名字。: "剪刀"})
#星质.变更集, 有效的?: true>
iex(4)> 车.回购.插入(第二条)
现在我们已经设置了 剪刀
项目以不同的方式,直接设置价格 %购物车.项{价格:小数.新(20)}
. 我们需要设置它的正确类型,不像在第一个项目中,我们只是传递了一个字符串作为价格. 我们可以传递一个浮点数,然后将其转换为小数类型. 例如,如果我们通过了 %购物车.项{价格:12.5}
,当插入项时,它会抛出一个异常,指出类型不匹配.
iex(4)> in有效的_项 = 车.项.变更集(%购物车.商品{},%{名称:"剪刀",价格:-1.5})
#星质.变更集},
错误:[价格:{"必须大于或等于%{number}",
[number: #小数<0>]}], 数据:#车.项<>, 有效的?: false>
如果要终止控制台,请按两次Ctrl+C. 可以看到验证在起作用价格必须大于等于0 (0). 如您所见,我们已经定义了所有模式 星质.模式 哪个部分与如何定义模块结构和更改集相关 星质.变更集 哪一个是所有的验证和强制转换. 让我们继续创建文件 lib /车/ 发票_项.ex
:
defmodule购物车.发票项做
使用星质.模式
进口星质.变更集
@primary_key {:id,:binary_id, auto创erate: true}
模式“发票_项目”可以
belongs_to:发票、购物车.发票,类型::binary_id
belongs_to:商品,购物车.项,类型::binary_id
字段数量:,:decimal, precision: 12, scale: 2
字段:价格,:十进制,精度:12,比例:2
字段:小计,:小数,精度:12,比例:2
时间戳
结束
@字段 ~w(项_id 价格 quantity)
@zero小数.新(0)
Def changesset (data, params \\ %{}
data
|> 铸造(params @字段)
|> 有效的ate_required([: 项_id, :价格, 数量:])
|> 有效的ate_number(:价格, greater_than_or_equal_to: @zero)
|> 有效的ate_number(数量:, greater_than_or_equal_to: @zero)
|> foreign_key_constraint(:发票_id,消息:"选择一个有效的发票")
|> foreign_key_constraint(: 项_id, message: "Select a 有效的 项")
|> set_subtotal
结束
Def set_subtotal(cs)
{(cs.变化[:价格]|| cs.data.价格),(cs.变化[:数量]|| cs.data.量)}
{_价格, nil} -> cs
{nil, _quantity} -> cs
{价格, quantity} ->
put_change(cs,:小计, 小数.乘(价格、数量)
结束
结束
结束
这个更改集更大,但您应该已经熟悉了其中的大部分. 在这里 belongs_to:发票、购物车.发票,类型::binary_id
定义“属于”的关系 车.发票 我们将很快创建的变更集. 下一个 belongs_to:条目
创建与项目表的关系. 我们已经定义了 @zero小数.新(0)
. 在这种情况下, @zero 是否像一个可以在模块内部访问的常量. 变更集函数有新的部分,其中之一是 foreign_key_constraint(:发票_id,消息:"选择一个有效的发票")
. 这将允许在约束未实现时生成错误消息,而不是生成异常. 最后是方法 set_subtotal 会计算小计吗. 如果同时拥有价格和数量,则传递更改集并返回一个新的更改集,其中包含已计算的小计.
现在,我们来创建 车.发票. 因此,创建并编辑该文件 lib /车/发票.ex
包含以下内容:
defmodule购物车.发票做
使用星质.模式
进口星质.变更集
别名购物车.{发票, 发票项, 回购}
@primary_key {:id,:binary_id, auto创erate: true}
模式“发票”可以
字段:customer,字符串
字段:amount,:decimal,精度:12,刻度:2
字段:balance,:decimal, precision: 12, scale: 2
字段:日期,日期.日期
has_many:发票_项目, 发票项, on_delete::delete_所有
时间戳
结束
@字段 ~w(客户金额余额日期)
Def changesset (data, params \\ %{}
data
|> 铸造(params @字段)
|> 有效的ate_required([:customer, :date])
结束
Def 创建(params
cs = changesset(%发票{},参数)
|> 有效的ate_项_count(params)
|> put_assoc(:发票_项目, 得到_项目(params))
如果计算机科学.有效的? do
回购.插入(cs)
其他的
cs
结束
结束
执行得到_项目(params)
项s = params[:发票_项目] || params["发票_项目"]
枚举.map(项目, fn(项)-> 发票项.变更集(%发票项{}, 项)结束
结束
执行有效的ate_项_count(cs, params)
项s = params[:发票_项目] || params["发票_项目"]
如果枚举.count(项目) <= 0 do
add_error(cs,:发票_项目, "项目数量无效")
其他的
cs
结束
结束
结束
车.发票 变更集有一些不同. 第一个在里面 模式: has_many:发票_项目, 发票项, on_delete::delete_所有
意味着当我们删除发票时,所有相关的 发票_项目 将被删除. 但是请记住,这不是在数据库中定义的约束.
让我们尝试在控制台中使用创建方法来更好地理解. 你可能已经创建了项目(“纸”,“剪刀”),我们将在这里使用:
iex(0)> 项_id = 枚举.地图(车.回购.(车.项), fn(项)-> 项.id结束)
iex(1)> {id1, id2} = {枚举.at(项_id, 0), 枚举.At (项_id, 1)}
我们取走了所有的物品 车.回购.所有 和 枚举.map 函数,我们得到 项.id
每个项目. 在第二行,我们只是赋值 id1
和 id2
分别使用第一个和第二个项_id:
iex(2)> inv_项目 = [%{项_id: id1, 价格: 2.5、数量:2};
%{项_id: id2,价格:20,数量:1}]
iex(3)> {:ok, inv} = 车.发票.创建(%{customer: "James Brown",日期:10月1日.日期.Utc, 发票_项目: inv_项目})
已经用它的发票_项目创建了发票,现在我们可以获取所有发票了.
iex(4)> 别名购物车.{回购、发票}
iex(5)> 回购.(发票)
你可以看到它返回 发票 但是我们也想看看 发票_项目:
iex(6)> 回购.(发票) |> 回购.预加载(发票_项目):
与 回购.预加载 函数,我们可以得到 发票_项目
. 注意,这可以并发地处理查询. 在我的例子中,查询看起来像这样:
iex(7)> 回购.得到(发票, "5d573153-b3d6-46bc-a2c0-6681102dd3ab") |> 回购.预加载(发票_项目):
到目前为止,我们已经展示了如何创建带有关系的新项目和新发票. 但是查询呢?? 让我给你介绍一下 星质.查询。 这将有助于我们对数据库进行查询,但首先我们需要更多的数据来更好地解释.
iex(1)> 别名购物车.{回购, 项, 发票, 发票项}
iex(2)> 回购.插入%商品{名称:“巧克力”,价格:十进制.新(“5”)})
iex(3)> 回购.插入%商品{名称:“口香糖”,价格:十进制.新(“2.5")})
iex(4)> 回购.插入(%商品{名称:"牛奶",价格:十进制.新(“1.5")})
iex(5)> 回购.插入(%商品{名称:"大米",价格:十进制.新(2)})
iex(6)> 回购.插入%商品{名称:“巧克力”,价格:十进制.新(10)})
我们现在应该有8件物品,并且有一个重复的“巧克力”. 我们可能想知道哪些项目是重复的. 那么让我们试试这个查询:
iex(7)> 进口星质.查询。
iex(8)> q = from(i in 项, select: %{名字。: i.姓名、计数.Name)}, group_by.名称)
iex(9)> 回购.(问)
19:12:15.739[调试]QUERY OK db=2.7ms
选择钱数.“名称”,计数(钱数.“名称”)从“项目”作为i0 GROUP BY i0.“名称”[]
[%{数:1,名字:“剪刀”},%{数:1,名字:“口香糖”},
%{数:2,名字:“巧克力”},%{数:1,名字:“纸”},
%{数:1,名字:“牛奶”},%{数:1,名字:“测试”},
%{count: 1, 名字。: "Rice"}]
您可以看到,在查询中,我们希望返回一个映射,其中包含项目名称及其在项目表中出现的次数. 另外, 虽然, 我们可能更感兴趣的是哪些是最畅销的产品. 因此,让我们创建一些发票. 首先,让我们通过创建一个访问an的地图来简化我们的工作 项_id
:
iex(10)> l = 回购.所有(from(i) in 项, select: {i.名字,我.id}))
iex(11)> 项目 = for {k, v} <- l, into: %{}, do: {k, v}
%{"Chocolates" => "8fde33d3-6e09-4926-baff-369b6d92013c",
"Gum" => "cb1c5a93-ecbf-4e4b-8588-cc40f7d12364",
"Milk" => "7f9da795-4d57-4b46-9b57-a40cd09cf67f",
"Paper" => "66ab2ab7-966d-4b11-b359-019a422328d7",
"Rice" => "ff0b14d2-1918-495e-9817-f3b08b3fa4a4",
"剪刀" => "397b0bb4-2b04-46df-84d6-d7b1360b6c72",
"Test" => "9f832a81-f477-4912-be2f-eac0ec4f8e8f"}
如您所见,我们已经使用 理解
iex(12)> line_项目 = [%{项_id: 项目["Chocolates"], quantity: 2}]
我们需要把价格加进去 发票_项目
参数创建发票, 但是最好只传递商品的id,然后自动填充价格. 我们会对 车.发票 模块来完成此操作:
defmodule购物车.发票做
使用星质.模式
进口星质.变更集
进口星质.查询#我们添加到查询
# ....
# schema, changeset和创建函数不会改变
这里的新函数是项目_with_价格s
执行得到_项目(params)
项s = 项目_with_价格s(params[:发票_项目] || params["发票_项目"])
枚举.map(项目, fn(项)-> 发票项.变更集(%发票项{}, 项)结束
结束
#新的功能来获取物品的价格
删除项目_with_价格s(项目)
项_id = 枚举.map(项目, fn(项) -> 项[: 项_id] || 项["项_id"] 结束)
q = from(i in 项, select: %{id: 1.Id, 价格: I.Price},其中.^项_id中的Id)
价格=回购.(问)
枚举.map(项目, fn(项) ->
项_id = 项[: 项_id] || 项[" 项_id "]
%{
项_id: 项_id,
数量:项[: Quantity] || 项[" Quantity "],
价格:枚举.find(价格s, fn(p) -> p[:id] == 项_id结束)[:价格] || 0
}
结束)
结束
您将注意到的第一件事是我们添加了 星质.查询。,这将允许我们查询数据库. 新函数是 删除项目_with_价格s(项目)
哪一个搜索项目,找到并设置每个项目的价格.
首先, 删除项目_with_价格s(项目)
接收一个列表作为参数. 与 项_id = 枚举.map(项目, fn(项) -> 项[: 项_id] || 项["项_id"] 结束)
,迭代所有项,只得到 项_id. 如您所见,我们使用atom访问它们 : 项_id
或者字符串“项_id”,因为映射可以使用其中任何一个作为键. 查询 q = from(i in 项, select: %{id: 1.Id, 价格: I.Price},其中.^项_id中的Id)
会找到所有在里面的项目吗 项_id
并返回一个地图 项.id
和 项.价格
. 然后我们可以运行查询 价格=回购.(问)
哪个返回映射列表. 然后,我们需要遍历项目并创建一个将添加价格的新列表. 的 枚举.map(项目, fn(项) ->
遍历每个项目,找到价格 枚举.find(价格s, fn(p) -> p[:id] == 项_id结束)[:价格] || 0
,并创建一个新的列表 项_id
数量和价格. 这样,就不再需要在每个 发票_项目
.
大家记得,前面我们创建了一个地图 项目 这使我们能够访问 id 使用I的项目名称.e 项目(“口香糖”)
“cb1c5a93 ecbf - 4 e4b - 8588 cc40f7d12364”. 这使得它易于创建 发票_项目. 让我们创建更多的发票. 再次启动控制台并运行:
ix -S混合
iex(1)> 回购.delete_所有(发票项); 回购.delete_所有(发票)
我们删除所有 发票_项目 和发票有一个空白的石板;
iex(2)> li = [%{项_id: 项目(“口香糖”), quantity: 2}, %{项_id: 项目["Milk"], 数量:1}]
iex(3)> 发票.创建(%{customer: "Mary Jane",日期:10月1日.日期.{{{}}}
iex(4)> li2 = [%{项_id: 项目["Chocolates"], quantity: 2}| li]
iex(5)> 发票.创建(%{customer: "Mary Jane",日期:10月1日.日期.Utc, 发票_项目: li2})
iex(5)> li3 = li2 ++ [%{项_id: 项目["Paper"], 数量:3}, % {项_id:项目(“大米”), 数量:1}, % {项_id:项目(“剪刀”), 数量:1}]
iex(6)> 发票.创建(%{customer: "Juan Perez",日期:10月1日.日期.Utc, 发票_项目: li3})
Now we have 3 发票; the first one with 2 项目, 第二个有3个项目, 第三个有6个项目. 我们现在想知道哪些产品是最畅销的? 为了回答这个问题, 我们将创建一个查询,根据数量和小计(价格x数量)查找最畅销的商品。.
defmodule购物车.项目做
使用星质.模式
进口星质.变更集
进口星质.查询。
别名购物车.{发票项, 项, 回购}
# schema和changesset不变
# ...
def 项目_by_quantity, do: 回购.所有项目_by(数量):
def 项目_by_subtotal, do: 回购.所有项目_by(小计)
删除项目_by(type)
从项中的i,
join: ii in 发票项, on: ii.项_id == I.id,
选择:%{id: 1.Id, 名字。: I.Name, total: sum(场(ii, ^type))},
group_by:我.id,
Order_by: [desc: sum(场(ii, ^type))]]
结束
结束
我们进口 星质.查询。 然后我们 别名购物车.{发票项, 项, 回购}
因此我们不需要在每个模块的开头添加车. 第一个函数 项目_by_quantity 调用 项目_by
函数,将 数量:
参数,并调用 回购.所有 执行查询. 这个函数 项目_by_subtotal 类似于前一个函数,但传递 :小计
参数. 现在我们来解释一下 项目_by:
from i in 项
,此宏选择项模块join: ii in 发票项, on: ii.项_id == I.id
,在条件“项目”上创建连接.Id = 发票_项目.项_id”选择:%{id: 1.Id, 名字。: I.Name, total: sum(场(ii, ^type))}
, 我们正在生成一个包含我们想要的所有字段的映射,首先我们从项中选择id和名称,然后进行运算符求和. 字段(ii, ^type)使用宏字段来动态访问字段group_by:我.id
我们按项目分组.idOrder_by: [desc: sum(场(ii, ^type))]]
最后按降序求和到目前为止,我们已经用列表样式编写了查询,但我们可以用宏样式重写它:
删除项目_by(type)
项
|> join(:inner, [i], ii in 发票项, ii.项_id == I.id)
|> select([i, ii], %{id: i.Id, 名字。: I.Name, total: sum(场(ii, ^type))})
|> group_by([i, _], i.id)
|> order_by([_, ii], [desc: sum(场(ii, ^type))])
结束
我更喜欢用列表形式编写查询,因为我发现它更容易读懂.
我们已经介绍了使用星质可以在应用程序中做什么. 当然,还有很多东西你可以从 星质文档. 与星质, 你可以创建并发, 由于Erlang虚拟机,可以轻松扩展的容错应用程序. 星质为长生不老药应用程序中的存储提供了基础,并提供了函数和宏来轻松管理数据.
在本教程中,我们研究了 星质.模式, 星质.变更集, 星质.迁移, 星质.查询。, 星质.回购. 这些模块中的每一个都在应用程序的不同部分为您提供帮助,并使代码更显式,更易于维护和理解.
如果您想查看教程的代码,您可以找到它 在这里 GitHub上.
如果您喜欢本教程并对更多信息感兴趣,我建议您 凤凰城 (这里有一系列很棒的项目), 很棒的灵丹妙药, 这个演讲 比较ActiveRecord和星质.
作为一个企业家, Boris了解与客户和用户密切沟通的重要性,以便更好地根据实际需求塑造应用程序.
14
世界级的文章,每周发一次.
世界级的文章,每周发一次.