【问题标题】:Query something on the other end of a has many through association查询 a 另一端的东西有很多通过关联
【发布时间】:2017-09-27 23:18:42
【问题描述】:

我想通过关联查询来过滤一个有很多的结果。我必须使用 Elixir 来完成这项工作,还是可以使用 Ecto 在数据库中完成这项工作?

设置

mix phx.new shop
cd shop
mix echo.create
mix phx.gen.html Accounting Invoice invoices number issues_on:date
mix phx.gen.html Accounting Product products name category
mix phx.gen.html Accounting LineItem line_items 
                                     invoice_id:references:invoices 
                                     product_id:references:products
mix ecto.migrate

priv/repo/seeds.exs

{:ok, invoice1} = Shop.Accounting.create_invoice(%{number: "1", issued_on: "2017-01-01"})
{:ok, invoice2} = Shop.Accounting.create_invoice(%{number: "2", issued_on: "2017-01-01"})
{:ok, invoice3} = Shop.Accounting.create_invoice(%{number: "3", issued_on: "2017-01-02"})

{:ok, banana} = Shop.Accounting.create_product(%{name: "Banana", category: "Fruits"})
{:ok, potato} = Shop.Accounting.create_product(%{name: "Potato", category: "Vegetables"})

Shop.Accounting.create_line_item(%{invoice_id: invoice1.id, product_id: banana.id})
Shop.Accounting.create_line_item(%{invoice_id: invoice2.id, product_id: banana.id})
Shop.Accounting.create_line_item(%{invoice_id: invoice2.id, product_id: potato.id})
Shop.Accounting.create_line_item(%{invoice_id: invoice3.id, product_id: potato.id})

架构

lib/shop/accounting/invoice.ex

schema "invoices" do
  field :issued_on, :date
  field :number, :string
  has_many :line_items, Shop.Accounting.LineItem
  has_many :products, through: [:line_items, :product]

  timestamps()
end

lib/shop/accounting/line_item.ex

schema "line_items" do
  belongs_to :invoice, Shop.Accounting.Invoice
  belongs_to :product, Shop.Accounting.Product

  timestamps()
end

查询预装蔬菜的发票

我如何查询从 1 月 1 日到 1 月 5 日的所有 invoices,包括 products,其中 Vegetables 的值为 category。 如果他们也有line_items 的水果也没关系。我只是想确保没有invoice 只有水果没有蔬菜。有了这个种子,它就是发票 2 和 3。

我知道如何使用 Elixir 过滤 invoices。但我想知道我是否可以用 Ecto 更快地在数据库中解决这个问题。有没有办法用 Ecto 过滤它?

import Ecto.Query
alias Shop.Accounting.Invoice
alias Shop.Repo

{:ok, starts_on} = Date.from_erl({2017, 1, 1})
{:ok, ends_on} = Date.from_erl({2017, 1, 5})


query = from i in Invoice, where: i.issued_on >= ^starts_on,
                           where: i.issued_on <= ^ends_on,
                           preload: [:products]
invoices = Repo.all(query)

# Here I would loop through invoices to filter the once 
# with vegetables.

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    使用Ecto.Queryjoin:https://hexdocs.pm/ecto/Ecto.Query.html#join/5

    在你的情况下,它将是:

    query = from i in Invoice, where: i.issued_on >= ^starts_on,
                               where: i.issued_on <= ^ends_on,
                               join: p in assoc(i, :products),
                               where: p ...,
                               preload: [:products]
    

    在你想要的条件下工作。

    另外,我在学习 Ecto 时经常使用这些示例:https://elixirnation.io/references/ecto-query-examples

    它们有点过时,但总体上还不错。

    【讨论】:

    • 你测试了吗?我感觉它比这更复杂,可能需要嵌套查询。
    • 这样的简单连接将复制符合p 条件的每个产品的发票where,例如如果它是p.category == "Vegetables" 并且发票中有多个蔬菜产品,则返回的列表将包含该发票的多个副本。可能还需要 group_by 或其他东西。
    • 或不同的查询。
    猜你喜欢
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 1970-01-01
    相关资源
    最近更新 更多