如何以Elixir的Ecto查询格式表示此查询?

如何以Elixir的Ecto查询格式表示此查询?,elixir,ecto,Elixir,Ecto,将以下SQL查询转换为有效的EXTO查询时遇到问题: 挑选* 从文件单元模块 文件名在哪里= 选择f.file\u id 从文件中选择f 在f.file\u id=fm.file\u id上以fm形式左键连接文件\u模块 其中f.storage_id='20:1:0:86d:1591:c89c:512:de52'和fm.name='bruteforce' 按f.version DESC订购 限制1 我认为应该把这个问题一分为二。以下是我尝试过的: q1= 文件 |>其中[f],f.storage

将以下SQL查询转换为有效的EXTO查询时遇到问题:

挑选* 从文件单元模块 文件名在哪里= 选择f.file\u id 从文件中选择f 在f.file\u id=fm.file\u id上以fm形式左键连接文件\u模块 其中f.storage_id='20:1:0:86d:1591:c89c:512:de52'和fm.name='bruteforce' 按f.version DESC订购 限制1 我认为应该把这个问题一分为二。以下是我尝试过的:

q1= 文件 |>其中[f],f.storage\u id==^storage\u id |>join:left[f],fm在FileModule中,f.file\u id==fm.file\u id |>其中[…,fm],fm.name==^module\u name |>订购人[…,fm],说明:fm.version |>选择[fm],fm.file\u id |>限制1 q1工作并返回预期的文件\u id 问题2= q1 |>子查询 |>其中[fm,fm2],fm.file\u id==fm2.file\u id这里是我卡住的地方 |>预加载[…,m],[模块:m] q1具有以下外部查询结果:

#Ecto.Query<from f0 in Helix.Software.Model.File,
 left_join: f1 in Helix.Software.Model.FileModule, on: f0.file_id == f1.file_id,
 where: f0.storage_id == ^(storage_id),
 where: f1.name == ^:bruteforce, order_by: [desc: f1.version], limit: 1,
 select: f0.file_id>
正如Jablanović在评论中提到的,ORM抽象有时可能会失败。在这种情况下,是否可以在以下情况下使用上述原始sql:

1是否强制转换File.ID和Storage.ID类型,以避免难看的字符串连接

2返回结果后,是否将此结果预加载或保存到架构中

我心目中的界面类似于:

q = "SELECT * FROM files WHERE file_id = $1", ^file_id
file = Repo.get(q) |> Repo.preload()

file.file_id  # Returns file id
根据的示例,看起来我可以达到1。2点怎么样

我的最终目标是使用上面的嵌套SQL查询,同时安全地强制转换文件id和存储id,并正确使用%file{}模式,并在file.modules处加载关联


请注意,存储过程或视图将为我提供更好的接口,我可以使用fragment正确地转换接口,但我觉得在将数据预加载到模式中时仍然存在问题。

我不打算回答我自己的问题,但嘿,我提出了一个替代解决方案。如问题所述,如果我能找到一个好的界面,让我:

使用原始SQL查询 同时仍然利用了外胚型铸造的优势 以及能够将关联数据预加载到模式中。 通过混合使用Repo.load和exto.Adapters.SQL.query,我可以做到这一点。如果它对其他人有帮助,我将共享接口和代码

sql = "
  SELECT *
  FROM file_modules 
  WHERE file_id =
    (SELECT f.file_id
    FROM files AS f
    LEFT JOIN file_modules AS fm ON f.file_id = fm.file_id
    WHERE f.storage_id = ##1::storage_id AND fm.name = ##2::module_name
    ORDER BY f.version DESC
    LIMIT 1)"

caster = fn type, value -> 
  case type do
    :storage_id ->
      Storage.ID.cast!(value) && to_string(value)
    :module_name ->
      Software.Module.exists?(value) && value || {:error, :bad_value}
    _ ->
      Hector.std_caster(type, value)
  end
end

query = Hector.query(sql, [storage_id, module_name], caster)

loader = fn repo, {columns, rows} ->
  rows
  |> Enum.map(fn row ->
    repo
    |> apply(:load, [File, {columns, row}])
    |> File.format()
  end)
end

{:ok, entries} = Hector.get(Repo, query, loader)

# Where `entries` is a list of %File{}, a valid Ecto Schema.
Hector是处理此接口的库。上面的例子是最复杂的情况:我们需要一个定制的施法器和一个定制的加载器。在大多数情况下,Hector的默认加载程序可以很好地工作,并且在任何存在潜在不安全输入的时候都需要自定义施法器

可以找到代码,在上还有一些额外的示例

当然,这远不是最好的解决方案,即使我能够为这个或那个查询找出正确的EXTO语法,但为原始查询提供一个好的抽象/接口总是很方便的

我没有对其进行测试,但以下查询应生成与原始SQL相同的结果:

q1 = ... # Same as in the question.
q2 =
  FileModule
  |> join(:left, [fm], fid in subquery(q1), fm.file_id == fid)
  |> select([fm], fm)

为什么不分别执行这两个查询?你没有损失太多。这就是我现在正在做的,作为一个解决办法。。但是由于无法处理子查询,我最终会受到极大的限制。我相信星外支持,所以我要弄清楚它是如何工作的。但是到目前为止,即使在阅读了几篇关于EXTO子查询的文章和指南之后,我仍然很难理解它。不幸的是,在过去使用多个ORM之后,我可以说每个人迟早都会尝试将SQL查询转换为ORM表达式。有时候,保持SQL的原样,不管是字符串还是某种形式的数据库视图,都可以为您和未来的维护人员节省大量浪费的时间。老实说,我也一样,我更喜欢SQL而不是ORMs。在这种情况下,有没有合理的方法来混合原始sql查询,同时从Ecto的强制转换中获得某种类型安全性,再加上能够将返回的数据预加载到模式中?我已经编辑了底部的问题