Как заставить Groovy/Grails возвращать список объектов вместо списка списков объектов?

У меня есть такой класс:

class Foo {
    static hasMany = [bars: Bar]
}

Когда я пишу:

Foo.getAll()

Я получаю список Foo объектов вроде этого:

[ Foo1, Foo2, Foo3 ]

Когда я пишу:

Foo.getAll().bars

Я получаю список списков объекта Bar следующим образом:

[ [ Bar1, Bar2 ], [ Bar2, Bar3 ], [ Bar1, Bar4 ] ] 

Но мне нужен уникальный список объектов Bar вроде этого:

[ Bar1, Bar2, Bar3, Bar4 ]

Моя конечная цель — получить уникальный список идентификаторов объекта Bar в списке выше, например:

[ 1, 2, 3, 4 ]

Я пробовал варианты метода collect. и я также попробовал оператор spread, но мне не повезло.


person ubiquibacon    schedule 20.06.2012    source источник


Ответы (3)


Для общих классов groovy (т. е. классов, отличных от GORM) Дэвид прав, говоря, что лучше всего использовать flatten и unique.

Однако в вашем примере похоже, что вы используете объекты домена GORM в отношениях «многие ко многим» (в противном случае вам не понадобилось бы ограничение уникальности).

Для доменного класса лучше всего использовать либо HQL, либо критерии, чтобы сделать это за один шаг. Дополнительным преимуществом является то, что сгенерированный для него SQL намного эффективнее.

Вот HQL для получения уникальных идентификаторов Bar, связанных с любым Foo в отношениях "многие ко многим":

Bar.executeQuery("select distinct b.id from Foo f join f.bars b")

Критерии для этого будут выглядеть так:

Foo.withCriteria {
    bars {
        projections {
          distinct("id")
        }
    }
}

Одна «проблема» с использованием такого метода заключается в том, что HQL не поддерживается в модульных тестах (и, вероятно, никогда не будет) и критерии запросы с проекциями таблицы соединения не работают в модульных тестах версии 2.0.4. Таким образом, любые тесты вокруг этого кода нужно либо имитировать, либо использовать интеграционный тест.

person Ted Naleid    schedule 21.06.2012
comment
Спасибо за все ваши ответы, ребята. Хотел бы я проголосовать за вас всех больше :) Я хотел избежать HQL, но, почти завершив свой квест с использованием критериев, теперь я считаю нумерацию страниц... хлопотной. Если я не могу заставить нумерацию страниц играть хорошо, используя критерии, я думаю, мне придется перейти на HQL. - person ubiquibacon; 21.06.2012

Существует Collection#collectMany метод, который работает так же, как вызов collect, а затем flatten. Чтобы получить список идентификаторов баров, вы можете сделать:

def barIds = Foo.getAll().collectMany { it.bars*.id }

Замыкание { it.bars*.id } возвращает список с идентификаторами баров Foo (мне нравятся эти имена-заполнители, хе-хе), а затем collectMany объединяет эти списки в один список. Если вам нужно, чтобы идентификаторы были уникальными (возможно, Bar может принадлежать более чем одному Foo), вы можете добавить .unique() этому выражению :)

person epidemian    schedule 21.06.2012

Возможно, есть лучший способ, но вы можете использовать сглаживание и уникальный .

Что-то типа:

def allBars = Foo.getAll().bars.flatten().unique()

Редактировать:
Я только что перечитал и вижу, что в конце вы ищете список идентификаторов, а не баров. Как вы предложили, вы должны использовать collect{ it.id } для получения идентификаторов. Я бы сделал это, прежде чем сделать список уникальным, по соображениям производительности.

person David    schedule 21.06.2012