Help with using functions opts and Ecto query


I’m creating a function with opts which I don’t really know how to use properly so I hope someone can help me. So, here is a function that queries songs from an album, it can lists all the songs but if a limit is added, it should limit the number of songs loaded.

def list_songs(album, opts \\ []) do
   limit = Keyword.get(opts, :limit)
   query = from s in Songs, where: == ^, limit: ??


The question is, how to I add limit to the query only if limit exists and is not a nil. Do I have to use some kind of conditional to check if limit exists and then based on that have different queries or is there a way to put it all inside a single query?

There’s not, the conditional needs to be around creating or modifying the query, e.g.:

base = from s in Songs, where: == ^

query = 
  if limit = Keyword.get(opts, :limit) do
    limit(base, ^limit)

Thanks a lot!

How would that work if I wanted to add preloads to the query? For example, if album has multiple artists and I want to preload artist with every song. One way I can think of is using cond with multiple checks

cond do
   limit and preloads -> 
      # query with limit and preloads
   limit ->
      # query with limit
   preloads ->
      # query with preloads
   true ->
      # base query without limit or preloads

… but is this the right way to do it? The reason I don’t really like this approach is because it would work with limit and preloads, but if I wanted to add a third or fourth parameter, it would become quite big and ugly.

You would normally encapsulate those in functions instead of making a big conditional. Something like this:

|> maybe_limit(limit)
|> maybe_preload(preload)

def maybe_limit(query, nil), do: query
def maybe_limit(query, limit), do: limit(query, ^limit)

def maybe_preload(query, nil), do: query
def maybe_preload(query, preloads), do: preload(query, ^preloads)