Easygrid icon indicating copy to clipboard operation
Easygrid copied to clipboard

In filterClosure, I'm trying to filter based on a hasMany association...

Open tombray opened this issue 11 years ago • 10 comments

I'm creating a search field that filters on different criteria depending on the nature of the input. The domainClass bound to the grid is Account. If the search string only contains numbers, I assume the user is trying to search by account id, so my filter closure returns:

eq('id', input)

An Account hasMany = [phoneNumbers:PhoneNumber]

If the user's input looks like a phone number, then I want to return accounts where one of the phoneNumbers matches the input.

Something like the following pseudo code:

filterClosure { Filter filter ->
    if ( filterParamValueLooksLikePhoneNumber() ) {
        phoneNumbers {
            value == theInput
        }
    }
}

I've tried several different approaches but haven't had any luck. Can I do this without modifying your GormDatasourceService.createWhereQuery method?

tombray avatar Dec 04 '13 00:12 tombray

Something like this should work:

                        { Filter filter ->
                            if (looksLikePhoneNumber(filter.paramValue)) {
                                phoneNumbers {
                                    eq('value', filter.paramValue)
                                }
                            } else {
                                eq('id', filter.ParamValue)
                            }
                        }

From your pseudo code - it looks like you are trying to use the "where query" syntax, which does not work for filterClosures

tudor-malene avatar Dec 04 '13 13:12 tudor-malene

Hmm, that's actually the syntax that I tried first. For some reason that doesn't work (the filter doesn't get applied, all Accounts returned), however manually using a DetachedCriteria from the console with that syntax works as expected:

//this works from the console
new BootStrap().init(ctx.servletContext)
def c = new DetachedCriteria(Account).build {
    phoneNumbers {
        eq('value','3105551212')
    }
}

c.list()

I tried putting a breakpoint inside the phoneNumbers closure (inside the filterClosure) and it doesn't get hit.

tombray avatar Dec 04 '13 17:12 tombray

Also, I discovered that if I put that in the initalCriteria closure, it works as expected:

            initialCriteria {
                phoneNumbers {
                    eq ( 'value', '3105551212')
                }
            }

Maybe it has to do with when you call build() on the criteria in GDS.createWhereQuery()?

tombray avatar Dec 04 '13 18:12 tombray

Looks more like the Filter object for that field is never created in the first place.

The filters for the grid columns are created in the 'filters' method of the gridImplService ( I presume you use JqueryGridService) The filters for the filter form are created in FilterFormService.filters.

So, I would set a breakpoint in the filter method.

Basically, what happens, is the parameter names are compared to the column names, and if they are the same a Filter is created.

tudor-malene avatar Dec 04 '13 20:12 tudor-malene

I've stepped through and a filter is definitely created, however in the sql log I see:

Hibernate: select count(*) as y0_ from account this_ where 1=1

when I try to use the phoneNumbers filter. Seems like it might be a grails bug, but I'm not sure how to narrow down the problem from here.

tombray avatar Dec 04 '13 22:12 tombray

Stepping into DetachedCriteria.handleJunction() I see that lastJunction.criteria is empty.

tombray avatar Dec 04 '13 22:12 tombray

I created a jira here: http://jira.grails.org/browse/GRAILS-10879 And attached a sample project there: http://jira.grails.org/secure/attachment/18749/foobar-bug-report-02122013.zip

Thanks again for your help.

tombray avatar Dec 04 '13 23:12 tombray

Thanks.

I'll look into it. Maybe I'll spot something. In the meanwhile, you can also try this with the 'globalFilterClosure', which you define on the grid level.

Something like this:

                globalFilterClosure { params ->
                    if (params.min && params.max) {
                        between("testIntProperty", params.min as int, params.max as int)
                    }
                }

(this example is from a unit test )

tudor-malene avatar Dec 05 '13 15:12 tudor-malene

globalFilterClosure works great! Thanks! I didn't know about it before, that's why I went with the filterForm approach.

tombray avatar Dec 05 '13 20:12 tombray

Great. The globalFilterClosure was created before the filter form support. Basically it does the same thing, but less expressive.

Thanks

tudor-malene avatar Dec 07 '13 10:12 tudor-malene