Альтернатива Mysql group_concat для проекций критериев Grails

Допустим, у меня есть два простых класса предметной области:

class A {
    String name

    static hasMany = [bs: B]
}

class B {
    String title
}

Теперь я хочу создать список элементов, структурированных следующим образом:

// id of A instance, name of A instance, comma separated list of Bs titles associated to A istance
1, "A1", "B1, B2"
2, "A2", "B2, B5"
...

Это мои критерии, чтобы получить это:

def list = A.withCriteria {
    createAlias 'bs', 'bs', CriteriaSpecification.LEFT_JOIN

    projections {
        property 'id'
        property 'name'

        property 'bs.title' // this is the interesting line
    }
}

и это, очевидно, извлекает только первый заголовок элементов B, связанных с моим экземпляром A. Как это:

1, "A1", "B1"
2, "A2", "B2"
...

Теперь сценарий реальной жизни немного сложнее, я упростил, чтобы добраться до сути, а именно: как я могу получить тот же эффект mysql group_concat для заголовков bs?

Я пытаюсь сделать это по одному критерию, но если это невозможно, я буду рад обсудить другое решение.


person lucke84    schedule 26.06.2013    source источник
comment
Я забыл упомянуть, я ищу способ не использовать функцию mysql group_concat.   -  person lucke84    schedule 26.06.2013
comment
Я могу получить все ассоциации, используя базу данных в памяти, в виде списка. Какая-то дополнительная утилита для сбора даст мне то, что вы хотите. [[1, TestA1, TitleB1],[1, TestA1, TitleB2],[1, TestA1, TitleB3],[2, TestA2, TitleB2],[2, TestA2, TitleB4],[2, TestA2, TitleB5]]. Является ли это поведение характерным для MySql? Можете ли вы проверить то же самое в базе данных H2 в памяти?   -  person dmahapatro    schedule 26.06.2013
comment
@dmahapatro, как вы получаете эту информацию? Отдельным запросом? И как объединить два результата? Не стесняйтесь опубликовать ответ.   -  person lucke84    schedule 26.06.2013


Ответы (2)


Это та же реализация, что и у вас, с заказом.

    def list = A.withCriteria {
        createAlias 'bs', 'bs', CriteriaSpecification.LEFT_JOIN
        projections {
            property 'id'
            property 'name'
            property 'bs.title' // this is the interesting line
        }

        order "id", "asc"
        order "bs.title", "asc"
    }


    //Bootstrap
    def a = new A(name: "TestA1").save()
    def a1 = new A(name: "TestA2").save()

    def b1 = new B(title: "TitleB1")
    def b2 = new B(title: "TitleB2")
    def b3 = new B(title: "TitleB3")

    def b4 = new B(title: "TitleB4")
    def b5 = new B(title: "TitleB5")

    [b1, b2, b3].each{a.addToBs(it)}
    [b2, b4, b5].each{a1.addToBs(it)}

    [a, a1]*.save(flush: true, failOnError: true)

Вы можете groupBy получить пару ключ-значение для каждой комбинации.

//This can be optimized
list.groupBy({[it[0], it[1]]})

//Would give
[[1, TestA1]:[[1, TestA1, TitleB1], [1, TestA1, TitleB2], [1, TestA1, TitleB3]], 
 [2, TestA2]:[[2, TestA2, TitleB2], [2, TestA2, TitleB4], [2, TestA2, TitleB5]]
]
person dmahapatro    schedule 26.06.2013
comment
Спасибо, но вывод не в том формате, который мне нужен. Кроме того, это не единственная информация, которая мне нужна от A, поскольку я сказал, что критерии намного сложнее, а мой пример очень прост, а ваше решение создает структуру данных, гораздо более сложную, чем нужно. - person lucke84; 27.06.2013

Это альтернативный способ

class A {
    String name
    static hasMany = [bs: B]

    def childrenString() {
        B.findAllByParent(this).collect{ it.title }.join(',')
    }
}

class B {
    static belongsTo = A
    A parent
    String title
    static constraints = {
    }
}

A.list().each  { it ->
    println "${it.name}, ${it.childrenString()}"
}
person JavaDev    schedule 27.06.2013
comment
Это возможное решение, но мне не очень нравится идея сохранения некоторой логики представления внутри доменного класса. - person lucke84; 27.06.2013
comment
вы можете поместить его в сервис и вызвать его - person JavaDev; 28.06.2013