在 Elixir 中为 Ecto 的 Repo.preload 添加限制选项

Adding a Limit Option to Ecto's Repo.preload in Elixir

提问人:IvonaK 提问时间:11/15/2023 更新时间:11/16/2023 访问量:47

问:

我在 Elixir 中有一个使用 Ecto 的函数,可以预加载给定实体的关联。当前实现如下所示:

@spec get(integer() | String.t(), list()) :: t() | nil
def get(id, preloads \\ []) do
  Shop
  |> Repo.get(id)
  |> Repo.preload(preloads)
end

我是这样使用它的:

      Shop.get(id, [
        :coustomers,
        :products
      ])

有没有可能让它变成这样?

  Shop.get(id, [
    :coustomers,
    {:limit,:products,3}
  ])
PostgreSQL Elixir ecto

评论

0赞 Peaceful James 11/16/2023
我正在想象如何使用普通 SQL 为 Postgres 做到这一点。当我们执行 时,默认情况下,它将为每个找到的关联返回一行。如果您有 20 个产品,则将有 20 行。因此,能够以某种方式 .我只是不确定会是什么。它可以是 products 表中的实际列,也可以是 (subquery)[hexdocs.pm/ecto/aggregates-and-subqueries.html#subqueries] 的结果。joinwhere product.some_index < 3some_index

答:

0赞 zwippie 11/16/2023 #1

可以限制预加载记录的数量,但它需要不止一个行,并且可能不像您希望或期望的那样灵活:

products_query = from p in Product, limit: 3, order_by: p.inserted_at

Shop
|> Repo.get(id)
|> Repo.preload([products: products_query]) 

也许你可以创建一个函数,它接受一个模块和一个数字(用于限制)并返回与上述内容相同的内容,例如:products_query

def preload_query(module, limit) do
  from a in module, limit: ^limit
end

并以此作为以下参数:Shop.get/2

Shop.get(id, [:customers, products: preload_query(Product, 3)])

评论

0赞 Peaceful James 11/16/2023
文档说“预加载查询中的限制和偏移等操作将影响整个结果集,而不是每个关联”。hexdocs.pm/ecto/Ecto.Query.html#preload/3
0赞 zwippie 11/16/2023
是的,使用预加载时有很多陷阱。对于这个(简单的)案例,我认为这并不重要?
0赞 Peaceful James 11/16/2023
我认为这确实很重要。由于您的子查询在shop_id上没有,因此您将列出属于任何商店的 3 个最近插入的产品。where
1赞 zwippie 11/16/2023
啊,但这是,不是.:)它只会返回与给定商店关联的记录,所以这很好。并且还对预加载的关联运行单独的查询,因为我们只想对它们进行简单的限制,这应该也可以。只是为了确定,刚刚通过一些测试确认了它。为简洁起见,我的答案是使用 just 而不是 updated。Repo.preloadQuery.preloadRepo.preloadpreloadRepo.preload
0赞 Peaceful James 11/16/2023
你是对的。我的脑海里只是删掉了你回答的部分。对不起。Repo.
0赞 Peaceful James 11/16/2023 #2

我会做这样的事情:

defmodule Shops do
  def get_shop(nil), do: nil

  def get_shop(shop_id) do
    limited_products =
      Product
      |> where(shop_id: ^shop_id)
      |> limit(3)

    Shop
    |> from(as: :shop)
    |> join(:inner, [shop: shop], product in subquery(limited_products), on: true, as: :products)
    |> select([shop: shop, products: products], %{shop | products: products})
    |> Repo.one()
  end
end

我还没有测试过这个。它只是为了照明。