Not the best issue but let me know if you encounter any problem: https://github.com/apache/grails-core/issues/15589
Gianluca Sartori -- https://dueuno.com On Tue, 21 Apr 2026 at 13:48, James Fredley <[email protected]> wrote: > Yes, please create an issue with test project. > > James > > On 2026/04/21 08:01:32 Gianluca Sartori wrote: > > Hi James, > > > > I'm trying the alias because the code below was not working: > > > > if (filterParams.containsKey('workPackage.code')) query = query.where > > { workPackage.code == filterParams.'workPackage.code' } > > > > This neither: > > > > if (filterParams.containsKey('workPackage.code')) query = query.where { > > workPackage { > > code == filterParams.'workPackage.code' > > } > > } > > > > > > This is the exception: > > > > Caused by: org.hibernate.QueryException: could not resolve property: > > workPackage.code of: momentum.work.TWorkAssignment > > at > > > org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:78) > > at > > > org.hibernate.persister.entity.AbstractPropertyMapping.toColumns(AbstractPropertyMapping.java:93) > > at > > > org.hibernate.persister.entity.BasicEntityPropertyMapping.toColumns(BasicEntityPropertyMapping.java:43) > > at > > > org.hibernate.persister.entity.AbstractEntityPersister.toColumns(AbstractEntityPersister.java:2023) > > at > > > org.hibernate.loader.criteria.CriteriaQueryTranslator.getColumns(CriteriaQueryTranslator.java:566) > > at > > > org.hibernate.loader.criteria.CriteriaQueryTranslator.getColumnsUsingProjection(CriteriaQueryTranslator.java:495) > > at org.hibernate.criterion.Order.toSqlString(Order.java:109) > > at > > > org.hibernate.loader.criteria.CriteriaQueryTranslator.getOrderBy(CriteriaQueryTranslator.java:440) > > at > > > org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:106) > > at > > > org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:75) > > at > > > org.hibernate.loader.criteria.CriteriaLoader.<init>(CriteriaLoader.java:80) > > at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1908) > > at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:370) > > at > > > org.grails.orm.hibernate.query.AbstractHibernateQuery.listForCriteria(AbstractHibernateQuery.java:827) > > at > > > org.grails.orm.hibernate.query.AbstractHibernateQuery.list(AbstractHibernateQuery.java:817) > > at grails.gorm.PagedResultList.<init>(PagedResultList.java:50) > > at > > > grails.gorm.DetachedCriteria$_list_closure2.doCall(DetachedCriteria.groovy:141) > > ... > > > > I can provide a test project to reproduce the case if you need it > > > > Gianluca > > > > > > Gianluca Sartori > > -- > > https://dueuno.com > > > > > > On Mon, 20 Apr 2026 at 22:22, James Fredley <[email protected]> > wrote: > > > > > Hi Gianluca, > > > Thank you - this is exactly the pattern I was hoping to see laid out > > > end-to-end. A few thoughts and one follow-up question. > > > > > > 1) Your shape is effectively the real-world case for PR #15447 > > > (apache/grails-core): > > > def query = TWorkAssignment.where {} > > > if (filterParams.containsKey('id')) query = query.where { id == > > > filterParams.id } > > > if (filterParams.containsKey('workPackage')) query = query.where { > > > workPackage.id == filterParams.workPackage } > > > > > > Prior to that fix, DetachedCriteriaTransformer only visited > > > DeclarationExpression, so every query = query.where { ... } > re-assignment > > > was silently skipped by the AST transform. At runtime the untransformed > > > closures evaluated to non-mutating clones and buildQuery would have > > > returned every TWorkAssignment regardless of filterParams. Adding > > > visitBinaryExpression fixed that. Your reply confirms the fix covers > the > > > canonical "empty initial where + chained reassignment under if-guards" > > > idiom, which is good to see. > > > 2) One specific question on the alias line: > > > if (filterParams.containsKey('workPackage.code')) query = > query.where { > > > def wp = workPackage > > > wp.code == filterParams.'workPackage.code' > > > } > > > The GORM reference documents the explicit-alias idiom (def wp = > > > workPackage) primarily for sorting/projection (.list(sort: > 'wp.code')), but > > > you are using it here purely for a filter. Is there a specific case > where > > > the direct form: > > > workPackage.code == filterParams.'workPackage.code' > > > misbehaves, and the aliased form rescues it? Or is this stylistic / > > > defensive? > > > If you have hit a real transformer or scope issue with the direct form, > > > would you be willing to open an issue on apache/grails-core (or a PR > if you > > > already have a fix in mind)? It would be useful to track it alongside > > > #15447/#15448 rather than leave it as tribal knowledge. > > > If it is stylistic rather than a bug, would you consider contributing > this > > > pattern directly to the Grails documentation - specifically the GORM > "Where > > > Queries" section ( > > > https://grails.apache.org/docs/latest/guide/GORM.html#whereQueries)? > The > > > existing docs cover basic property comparison and associations but do > not > > > describe composing conditional filters via chained re-assignment, the > > > flat-map-with-dotted-keys convention, or the aliased form for > > > nested-property filtering. From your Medium article and this reply, the > > > rules that emerge are: > > > - @CompileStatic on the class, @CompileDynamic on the buildQuery > method > > > - Accept a Map filterParams; do not name local variables after domain > > > properties > > > - Start with an empty where {} and chain conditional query = > query.where > > > { ... } re-assignments > > > - Flat-map-with-dotted-keys for nested filters: > > > filterParams.'workPackage.code' > > > - FK shortcut for simple association filters: workPackage.id == > > > filterParams.workPackage > > > - Aliased association access for nested-property filters: def wp = > > > workPackage; wp.code == ... > > > > > > > > > James > > > > > > On 2026/04/20 10:42:29 Gianluca Sartori wrote: > > > > Hi James, > > > > > > > > this is the query: > > > > > > > > @CompileDynamic > > > > private DetachedCriteria<TWorkAssignment> buildQuery(Map > > > filterParams) { > > > > def query = TWorkAssignment.where {} > > > > > > > > if (filterParams.containsKey('id')) query = query.where { id > > > > == filterParams.id } > > > > if (filterParams.containsKey('workPackage')) query = > > > > query.where { workPackage.id == filterParams.workPackage } > > > > if (filterParams.containsKey('workPackage.code')) query = > > > query.where { > > > > def wp = workPackage > > > > wp.code == filterParams.'workPackage.code' > > > > } > > > > > > > > return query > > > > } > > > > > > > > > > > > Thank you for your time, > > > > Gianluca > > > > > > > > Gianluca Sartori > > > > -- > > > > https://dueuno.com > > > > > > > > > > > > On Sat, 18 Apr 2026 at 02:43, James Fredley <[email protected] > > > > > wrote: > > > > > > > > > Can you share buildQuery? The def o1 = owner AST trick requires the > > > > > literal Domain.where { ... } form; if your builder composes > criteria > > > > > programmatically, the alias won't be registered and the sort will > > > silently > > > > > drop. > > > > > > > > > > James > > > > > > > > > > On 2026/04/16 13:31:10 Gianluca Sartori wrote: > > > > > > Hi folks, > > > > > > > > > > > > do you have evidence that the where query aliases are working for > > > > > sorting? > > > > > > It does not seem to work in my code. > > > > > > > > > > > > The only difference I see is that I sort multiple columns with > > > something > > > > > > like: > > > > > > > > > > > > def query = buildQuery(filterParams) > > > > > > return query.list(sort: ['o1.firstName': 'desc']) > > > > > > > > > > > > > > > > > > > > > > > > >>> > > > > > > 7.4.6. Query Aliases and Sorting > > > > > > > > > > > > If you define a query for an association an alias is > automatically > > > > > > generated for the query. For example the following query: > > > > > > > > > > > > def query = Pet.where { > > > > > > owner.firstName == "Fred" > > > > > > } > > > > > > > > > > > > Will generate an alias for the owner association such as > > > owner_alias_0. > > > > > > These generated aliases are fine for most cases, but are not > useful > > > if > > > > > you > > > > > > want to later sort or use a projection on the results. For > example > > > the > > > > > > following query will fail: > > > > > > > > > > > > // fails because a dynamic alias is used > > > > > > Pet.where { > > > > > > owner.firstName == "Fred" > > > > > > }.list(sort:"owner.lastName") > > > > > > > > > > > > If you plan to sort the results then an explicit alias should be > > > used and > > > > > > these can be defined by simply declaring a variable in the where > > > query: > > > > > > > > > > > > def query = Pet.where { > > > > > > def o1 = owner > > > > > > o1.firstName == "Fred" > > > > > > }.list(sort:'o1.lastName') > > > > > > > > > > > > Define an alias called o1 > > > > > > Use the alias in the query itself > > > > > > Use the alias to sort the results > > > > > > > > > > > > By assigning the name of an association to a local variable it > will > > > > > > automatically become an alias usable within the query itself and > > > also for > > > > > > the purposes of sorting or projecting the results. > > > > > > > > > > > > > > > > > > > > > > > > Gianluca Sartori > > > > > > -- > > > > > > https://dueuno.com > > > > > > > > > > > > > > > > > > > > >
