bitemyapp / bitemyapp/esqueleto
Query with variable number of joins
- Dominant language
- Haskell
- Stars
- 399
- Forks
- 107
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 1
Description
For example, let's say that I have a list representing a category path, like
> Tool > Screwdriver > Phillips
and I want to find the corresponding record in a table "category" with columns "parent_id" and "name".
In Ruby with ActiveRecord, I could do something like:
```ruby
category_path = ["Tool", "Screwdriver", "Phillips"]
leaf_cat_name, *parent_cat_names = category_path.reverse
query_init = Category.where(name: leaf_cat_name)
category = parent_cat_names.to_enum.with_index.reduce(query_init) do |query, (name, i)|
query.joins(<<~SQL)
join categories cat#{i + 1}
on #{i.zero? ? "categories" : "cat#{i}"}.parent_id = cat#{i + 1}.id
SQL
.where("cat#{i + 1}.name = :name", name: name)
end
.first!
```
That results in a query like:
```sql
select `categories`.*
from `categories`
join categories cat1
on categories.parent_id = cat1.id
join categories cat2
on cat1.parent_id = cat2.id
where `categories`.`name` = 'Phillips'
and (cat1.name = 'Screwdriver')
and (cat2.name = 'Tool')
order by `categories`.`id` asc
limit 1
```
Could something like that be done with Esqueleto? preferably without having to manually handle the aliasing. Right now, it seems Esqueleto's joins are limited to a static set based on the parameter of the function passed to `from`.
I was thinking of perhaps having functions like:
```haskell
leftJoinOn
:: PersistEntity a
=> (SqlExpr (Entity a) -> SqlExpr (Value Bool))
-> SqlQuery (SqlExpr (Entity a))
```
to be able to write the equivalent to that Ruby code, which could be like
```haskell
let catNames = ["Tool", "Screwdriver", "Phillips"]
leafCatName : parentCatNames = reverse catNames
select $ from $ \ leafCat -> do
where_ $ leafCat ^. CategoryName =. val leafCatName
-- Please excuse the free-point
rootCat <- ((.).(.)) flip flip foldlM leafCat parentCatNames $ \ subCat name -> do
supCat <- joinOn $ \ supCat ->
subCat ^. CategoryParentId =. just (supCat ^. CategoryId)
where_ $ supCat ^. CategoryName =. val name
return supCat
where_ $ rootCat ^. CategoryParentId =. nothing
return leafCat
```
I haven't read the source in detail, but I see that there's `State IdentState` in `SqlQuery`. If no aliasing is done automatically yet, perhaps the aliases could be kept track-of in there.
I could perhaps work on this next week, but I was wondering if there would be any complications this would imply or anything that I may have overlooked that someone here could advise me on.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.