I have a record where users can provide an optional slug which is a unique_index to provide a friendly name for the URL, how can I go about using this slug if it’s defined otherwise falling back to the resource’s UUID?
live "/records/:id", RecordsLive.Show
IE: live/records/slug-is-defined or live/records//77?
Ecto will likely raise an error if you try to cast a plain string as UUID so I suggest to determine the datatype on the application level:
live "/records/:opaque_id", RecordsLive.Show
def get_record(opaque_id) do
Record
|> where(^opaque_id_to_query(opaque_id))
|> Repo.one()
end
defp opaque_id_to_query(opaque_id) do
cond do
match?({_, ""}, Integer.parse(opaque_id)) -> [id: opaque_id]
match?({:ok, _}, Ecto.UUID.cast(opaque_id)) -> [uuid: opaque_id]
true -> [slug: opaque_id]
end
end
Maybe my understanding is off but doesn’t using or on different fields cause index not to be used for the second field? Would it not be faster to do a lookup on one then the other?
Not necessarily, using or on different field with index usually result in query optimizer combine these two different index in bitwise or operation. Usually it’s not faster to do lookup one then the other because it cause two DB request network latency.
Oh interesting. What my my old colleague going on about then? lol. I tried an explain with an OR on the same column and it still gave me a sequence scan, so I guess I don’t understand like I thought I did and I have some more RTFMing to do.
Can you elaborate what you mean? Are you trying to have a route that contains many ids / slugs ? Or a list of different links each with its own id / slug ?
Sorry that wasn’t clear, I was just unsure of how your solution would translate into Routes.live_path(@socket, MyAppWeb.ThingsLive.Show, ??) that’s where I was confused if either the id or the slug would work as a param there.
Have a look at Phoenix.Param. You could do something like:
defimpl Phoenix.Param, for: YourApp.SomeContext.SomeSchema do
def to_param(schema) do
Map.get(schema, :slug, schema.id)
end
end
To offer some unsolicited advice, I think it’ll be far less of a headache in the future if you make slugs non-nullable fields and auto-generate them based off of some other field as to not force users to specify one. Take it or leave it, of course!