Building a dynamic search by passing in Module to the query

Hi,

I’m building a LiveView app that has a catalogue of components.
Instead of making a separate function for each return_components_for_vendor_name I would like to build a more dynamic function where I can pass the component in question.

This function works, but is not dynamic:

# Return Component Vendors that are associated with a display,
 # and where we have at least one display.
  def component_vendors_for_displays() do
    query =
      from d in Display,
        join: v in assoc(d, :component_vendor),
        select: v.id

    component_vendor_ids =
      Repo.all(query)
      |> Enum.uniq()

    Enum.map(component_vendor_ids, fn id -> ComponentVendors.get_component_vendor!(id) end)
  end

I would love to get something like this to work, but I’m not sure how to accomplish it:

  def return_components_for_vendor(component_vendor_id, component) do
    query =
      from d in component,
        join: v in assoc(d, :component_vendor),
        select: v.id

    component_vendor_ids =
      Repo.all(query)
      |> Enum.uniq()

    Enum.map(component_vendor_ids, fn id -> ComponentVendors.get_component_vendor!(id) end)
  end

Here’s my test:

test "find components by component vendor" do
    component_vendor = component_vendor_fixture()
    camera = camera_fixture(%{component_vendor_id: component_vendor.id})

    component = Components.return_components_for_vendor(component_vendor.id, Camera)
    assert component == [camera]
  end

The test fails here:

     ** (Protocol.UndefinedError) protocol Ecto.Queryable not implemented for EasySolutions.Components.Camera of type Atom, the given module does not exist. This protocol is implemented for the following type(s): Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple
     code: component = Components.return_components_for_vendor(component_vendor.id, Camera)
     stacktrace:

I tried to alias Camera in to the test, but that made no difference.

Two questions:

  • What’s the full name of Display from the working code?
  • What’s the full name of the Camera schema?

Something seems misaligned, but it’s hard to spot without the rest of the files.


General note: unless get_component_vendor! does something very unusual, it’s almost always better to replace the sequence “query for IDs, query for struct for each ID” with “query for structs”.