mathieuprog
I’ll start right with an example ![]()
User
|> QueryBuilder.where(firstname: "John", city: "Anytown")
|> QueryBuilder.where({:age, :gt, 30})
|> QueryBuilder.order_by(lastname: :asc)
|> QueryBuilder.preload([:role, authored_articles: :comments])
|> Repo.all()
With associations:
User
|> QueryBuilder.where([role: :permissions], name@permissions: "delete")
|> Repo.all()
Query Builder allows to build and compose Ecto queries based on data.
Concise, no need to deal with bindings and macros.
Its primary goal is to allow Context functions to receive a set of filters and options:
# in a Controller
Blog.list_articles(preload: [:comments], order_by: [title: :asc])
Blog.list_articles(preload: [:category, comments: :user])
This avoids having to create many different functions in the Context for every combination of filters and options, or to create one general function that does too much to satisfy all the consumers.
The calling code (e.g. the Controllers), can now retrieve the list of articles with different options. In some part of the application, the category is needed; in other parts it is not; sometimes the articles must be sorted based on their title; other times it doesn’t matter, etc.
The options may be added to the query as shown below:
# in the Blog context
def get_article_by_id(id, opts \\ []) do
QueryBuilder.where(Article, id: id)
|> QueryBuilder.from_list(opts)
|> Repo.one!()
end
Inspired by the libraries token_operator and ecto_filter.
More examples are available in the doc:
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 21 to 12- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
mathieuprog
QueryBuilder v1.0.0 has been released!
New features:
Extensionmodule allowing to easily extend QueryBuilder with user’s own functions:<field_name>@selfvs:<atom_value>Thanks to @onomated that did all of the work, of high quality!
Because QueryBuilder is considered stable and there have been breaking changes, it has been bumped to 1.0.
Breaking changes:
.1.
QueryBuilder.order_by(User, lastname: :asc)becomesQueryBuilder.order_by(User, asc: :lastname).2.
QueryBuilder.where(User, {:name, :eq, :nickname})becomesQueryBuilder.where(User, {:name, :eq, :nickname@self})(concerns a comparison of two fields of the root schema)
onomated
Ah, don’t know why I didn’t grok the extension approach you suggested at first. I like it! That should work well. I’ll give it a shot after we work out the resolution to Atom values in where args treated as schema fields · Issue #2 · mathieuprog/query_builder · GitHub
mathieuprog
I thought rather something like:
and in
QueryBuilder.Extensionhave__using__injecting thedefdelegates andfrom_list.Except if I’m missing something
Note that it really is for the
from_listfunction, otherwise I would have suggested to not extendQueryBuilderat all with macros, and just do something like:However the user might want to use
from_listwith the new functionality.I prefer reflection because I think it’s not expensive at all, as I think the values from these functions for reflection are calculated at compile time.
onomated
Ah, so the thought is allow clients create an extension module that are passed in to
QueryBuilderlike so?MyApp.QueryBuilderThe clients can use just query builder as currently documented. No
defdelegates required and nofrom_listoverrides needed. Its worth a shot, though injecting macros can be complex if the extension macros have other macro operations such as setting module attributes which my extension module does. It’s worth a try to see how that plays out in my case.That’s one approach, or introducing
:selfas a sentinel for the current schema to resolve to a field. So:Resolves to the current functionality as today, and:
works like Ecto does today, while modeling the current association syntax you have.
Thoughts? Either way, it’s worth creating issues, and I can take a stab at working on the functionality suggested in a new branch or just fork the repo and PR back in after implementing. Definitely need to address the atom field referencing to use this lib
mathieuprog
The idea is to simply inject all those
defdelegates and thefrom_listfunction into the custom module. Except if I missed something?useallows you to inject code.Nice, obviously I didn’t think about that case:) I think it’s easily solved with some reflection:
As a note to self, I think I’d also like at some point to implement
select, because QueryBuilder is limited to fetching full entities right now.onomated
Interesting. Can you highlight a bit more about what you’re thinking here? This is a pattern I haven’t seen. Not as much of a power user of Elixir just yet, so it’ll be an interesting learning experience.
Great point. Yes this does align with the Ecto Query API arg scheme.
One issue I have run into is that QueryBuilder assumes atom values are fields. So queries like this:
is resolving to the following sql and error:
This is problematic as I use enums (with GitHub - gjaldon/ecto_enum: Ecto extension to support enums in models · GitHub) somewhat extensively in my data model, so attempting to “know enum fields” and programmatically convert them to strings first will be tedious. Is there a way to be explicit about values actually being db columns and only then should they be auto-binded as fields? I can file an issue if there is no workaround for this at the moment. This was caught in my regression tests as I’m trying to incorporate
QueryBuilder.to_listwhere this worked fine prior:mathieuprog
It’s a good idea to offer a module to allow extension! I don’t see how a behaviour would help here as you want to extend the QueryBuilder module with your own custom functions with arbitrary function names.
What we could offer though is some code injection:
use QueryBuilder.Extensionand this will allow to inject thefrom_listas well to accept the new custom functions.If you want to change your codebase already you could make a PR, or you can just wait until I’ll get back into it:)
The lib is in alpha so I’m allowed to break it?
But what I will do I think is make a stable version 1.x and it won’t bother anyone. Having e.g.
asc: :lastnamealso follows the Ecto Query API option.onomated
@mathieuprog Tested out the custom sql functionality for
order_bys and it works great. I’ve updated the gist above with my QueryBuilder extension module that utilizes dynamic bindings inwhereandorder_bymethods. Looking forward to the next release with these features.Thanks once again for supporting the ask. Cheers!
onomated
@mathieuprog, here’s a gist where I started expanding the QueryBuilder functionality:
It’s currently work in progress as I haven’t added the necessary logic to utilize your changes to
order_byin your master branch. Maybe makingQueryBuildera behavior with default implementations will make extending a lot more straightforward i.e. remove the need todefdelegatefunctions.An option to avoid breaking your
order_byinterface would be to support a map of the order by spec, and just check if its a keyword list or map. So supporting something along the lines of:Just a thought.
I’ll expand my search functionality to also utilize your current changes for order_by as well and update the gist afterwards
onomated
Thanks @mathieuprog! That’s great. I’ll try out the master branch and share my implementation with you.