Grails named queries: find parents of child A and child B

落爺英雄遲暮 提交于 2020-01-07 06:42:12

问题


I have this:

class Parent {
  String name
  static hasMany = [children: Child]
}

class Child {
  String name
}

And I want to find all the parents that have a child named "Tom" and another one named "Sam".

id | parentName
---------------
1  | Peter 
2  | Joe
3  | Ann

id | childName
---------------
1  | Tom 
2  | Sam

idParent | idChild
---------------
1        | 1 
2        | 2
3        | 1
3        | 2

In the example, it will be Ann.

I've tried this:

static namedQueries = {
    findParents{nameList ->
        children{
            nameList.each{childName->
                and{
                     ilike('nombre', '%'+childName+'%')                 
                    }
            }           
     }

But this way I am searching only one child with Tom and Sam in his name. In the example, it will return nothing.

I have tried with 'in' 'name', ["Tom","Sam"] instead ilike operator, but now I will get all parents with a child named "Tom" or a child named "Sam". In the example, it will return Peter, Joe and Ann

Any idea?

Thanks in advance!


回答1:


So I now understand and I recreated and made an answer for you

idParent | idChild
---------------
1        | 1 
2        | 2
*3        | 1
*3        | 2

So it is really 3 or Ann that you are after right ?

Peter [test.Child : 1]
Joe [test.Child : 2]
Ann [test.Child : 1, test.Child : 2]

What you want is :

list is [[test.Parent : 3, test.Child : 2, test.Parent : 3, test.Child : 1]]

E2A Dec 2016 A much better way than all of the earlier versions

String query="""select new map(p.name as parentName,
            case when (select count(*) from p.children as pc where pc.name in (:children)) >= 2 then 'true' 
            else 'false' end as childMatch) from Parent p
            """
        def inputs=[:]
        inputs.children=['Tom','Sam']
        def listing = Parent.executeQuery(query,inputs,[readonly:true]) 

Produces:

println "list is $listing"
list is [[childMatch:false, parentName:Peter], 
[childMatch:false, parentName:Joe], 
[childMatch:true, parentName:Ann]]

Now if we simply changed:

def listing = Parent.executeQuery(query,inputs,[readonly:true])?\
.findAll{it.childMatch=='true'}
    println "list is $listing"
list is [[childMatch:true, parentName:Ann]]

As you can see much less complication than earlier methods

Alternative to above but still not as good as above You can also use in elements which binds to real object rather than your join like below:

(:children) in elements(p.children)

But even with this method you will hit the issues like discussed below. The very first method on this answer is rather powerful, using the count you could use it as a form of weight to see how many records have 2 how many 1 how many 0 etc so lots more usages from it -

Earlier methods

I used hql and cross joined:

String query="""from Parent p1 left join p1.children pc1 , 
        Parent p left join p.children pc where 
        pc.name =:input1 and pc1.name= :input2 and 
        p1.id=p.id group by p.name"""

def inputs=[:]
inputs.input1='Tom'
inputs.input2='Sam'
def list = Parent.executeQuery(query,inputs,[readonly:true])

println "list is ${list}"

But because everything is called name it makes things a little more difficult to manage if you want to use the grails groupBy to get a more compact list per parentName:

String query="""select new map(p1.name as parentName, pc1.name as childName, pc.name as childName2) from Parent p1 left join p1.children pc1 , 
Parent p left join p.children pc where pc.name =:input1 and pc1.name= :input2 and p1.id=p.id """ //group by p1.name, p.name"""
def inputs=[:]
inputs.input1='Tom'
inputs.input2='Sam'
def list = Parent.executeQuery(query,inputs,[readonly:true])
def list1=list.groupBy{it.parentName}
println "list is ${list} vs ${list1}"

like above which returns this to the console:

Joe [test.Child : 2]
Ann [test.Child : 2, test.Child : 1]
list is [[childName:Sam, childName2:Tom, parentName:Ann]] vs [Ann:[[childName:Sam, childName2:Tom, parentName:Ann]]]

And to finalise on this point, it is entirely upto you or the given process / complexity on whether you feel this processing should be as it is (maybe quite db strenuous vs more light weight query and some further query from grails with light lookups)

// Find all the parents that have 1 of our matches 
// Tom but all that also have children greater than 1 
def parents = Parent.findAll{children.name=='Tom' && children.size()>1}
//This already returns our only other option 3
println "found ${parents}"


// Now to confirm that the childrens the 
// parent has contains our other scenario:

// Lets build our own list from it
def finalList=[]
//Iteration through our listing of parents
parents?.each { Parent p ->
  // If the child name matches other case
  if (p.children.name.contains('Sam')) {
    // add this to our finalList
    finalList << p
  }             
}
// We have correct results in this list
println "we now have ${finalList}"

That probably did not generate huge db queries cross joining but in the end did lots of small lookups agains the lazy list of each parent that we had an interest in (who had more than 1 child)

This is the scenarios - I guess it is down to your data model and what works best for you. The initial hql maybe a huge one off query that produces a light weight list. 2nd light weight query with lots of light weight lookups.

The second example outputs:

found [test.Parent : 3]
we now have [test.Parent : 3]

The above finalList was to give an example of how you would do it manually, that entire segment could be converted to a 1 liner:

def parents = Parent.findAll{children.name=='Tom' && children.size()>1}.collect{[name:it.name, children:it.children]}.findAll{it.children.name.contains('Sam')}

which produces:

found [[name:Ann, children:[test.Child : 1, test.Child : 2]]]

VS:

def parents = Parent.findAll{children.name=='Tom' && children.size()>1}.collect{[name:it.name, children:it.children.name]}.findAll{it.children.contains('Sam')}

which produces:

found [[name:Ann, children:[Tom, Sam]]]

The very last example collects only specific fields so the final parents listing does not contain entire child/parent objects but a light list containing their names.

So use as required.



来源:https://stackoverflow.com/questions/40219196/grails-named-queries-find-parents-of-child-a-and-child-b

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!