Grails 命名查询:查找 child A 和 child B 的 parents

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

我有这个:

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

class Child {
  String name
}

我想找到所有 parents 中有一个名为 "Tom" 的 child 和另一个名为 "Sam" 的 child。

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

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

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

在示例中,它将是 Ann。

我试过这个:

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

但通过这种方式,我只搜索了一个 child 名字中有 Tom 和 Sam 的人。在示例中,它将 return 什么都没有。

我曾尝试使用 'in' 'name', ["Tom","Sam"] 而不是 ilike 运算符,但现在我将使用名为 "Tom" 的 child 或名为 child 的所有 parents "Sam"。在示例中,它将 return Peter、Joe 和 Ann

有什么想法吗?

提前致谢!

所以我现在明白了,我重新创建并为你做了一个答案

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

所以你真的是 3 或 Ann 对吧?

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

你想要的是:

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

E2A Dec 2016 比所有早期版本都好得多的方法

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]) 

产生:

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

现在如果我们简单地改变:

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

正如您所看到的,比以前的方法复杂得多

替代上面但还是不如上面 您还可以使用 in elements 绑定到真正的 object 而不是像下面这样的连接:

(:children) in elements(p.children)

但即使使用这种方法,您也会遇到如下所述的问题。这个答案的第一个方法相当强大,使用计数你可以将它用作一种权重形式来查看有多少记录有 2 多少 1 多少 0 等等所以它有更多的用法 -

早期方法

我使用了 hql 和交叉连接:

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}"

但是因为所有的东西都被称为名字,如果你想使用 grails groupBy 来获得一个更紧凑的列表,那么管理起来会有点困难 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}"

像上面的 returns 这个到控制台:

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]]]

并最终确定这一点,这完全取决于您或给定的过程/复杂性,决定您是否认为该处理应该按原样进行(可能相当 db 费力 vs 更轻量级查询和来自 grails 的一些进一步查询轻查找)

// 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}"

这可能不会产生巨大的数据库查询交叉连接,但最终再次对我们感兴趣的每个 parent 的惰性列表进行了很多小查找(谁有超过 1 child)

这是场景 - 我想这取决于您的数据模型以及最适合您的数据模型。最初的 hql 可能是一个巨大的一次性查询,它会产生一个轻量级列表。带有大量轻量级查找的第二个轻量级查询。

第二个例子输出:

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

上面的 finalList 是为了举例说明如何手动完成,整个段可以转换为 1 衬里:

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

产生:

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

对比:

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

产生:

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

最后一个示例仅收集特定字段,因此最终 parents 列表不包含整个 child/parent objects,而是包含其名称的简单列表。

所以按需使用。