Критерии связи между сущностями - Grails

У меня есть приложение со следующими объектами:

Тема:

class Topic {

   UUID id
   String description
   String name
   boolean visibility = true

   // Relation
   static hasMany = [tests:Test]
   ...
}

Тест:

class Test {

    UUID id
    boolean active = true
    String description
    ...

    static hasMany = [evaluationsTest: Evaluation]
    static belongsTo = [topic: Topic, catalog: Catalog]
}

Когда я показываю пользователю все видимые темы, я запрашиваю запрос:

def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])

Этот запрос возвращает меня, например: [['English'], ['Spanish']]. Затем я могу показать пользователю полную информацию о каждой теме.

Но я также хочу указать пользователю количество активных тестов в каждой видимой теме.

Например:

English topic has 2 active test.
Spanish topic has a total of 2 test. One is active and the other is not.
German topic has not any active test.

Затем мне нужен запрос, результатом которого будет: def activeTotalEachTopic = [[2],[1],[0]], и я могу передать переменную activeTotalEachTopic в представление (.gsp).

Решение:

Из первого запроса, где я могу получить все видимые темы, я получаю номер активного теста.

def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])

def numberActiveTest = []

activeTopics.each { topic ->
    def result = Test.findAllByTopicAndActive(topic, true).size()
    numberActiveTest.push(result)
}

И я передаю в представление обе переменные.

render view: 'home', model: [activeTopics: activeTopics, numberActiveTest: numberActiveTest]

person Jesús Iglesias    schedule 16.05.2016    source источник


Ответы (1)


Чего вам не хватает, так это группировки, чтобы вы получали количество для каждой группы, а не общее количество.

Вам также необходимо изменить тип соединения с внутреннего соединения по умолчанию на внешнее соединение, чтобы темы без активного теста возвращали 0. Однако побочным эффектом этого является изменение способа ссылки на свойства ассоциации из-за псевдонима это создано объединением. Что-то вроде этого:

import static org.hibernate.sql.JoinType.*

def activeTotalEachTopic =  Topic.createCriteria().list() {
    createAlias('tests', 't', LEFT_OUTER_JOIN)

    eq 'visibility', true

    or {
        eq 't.active', true
        isNull 't.id'
    }

    projections {
       groupProperty 'name'
       count()
    }        
    order ("name", "asc")
}

Теперь еще одна проблема, которую необходимо решить, заключается в том, что вывод будет примерно таким из-за группировки: [['English', 2],['Spanish', 1],['German', 0]]. Итак, что вы можете сделать, это собрать второй элемент в каждом подсписке:

activeTotalEachTopic*.getAt(1)

// Which is the equivalent of...

activeTotalEachTopic.collect { it[1] }
person Emmanuel Rosa    schedule 17.05.2016
comment
Спасибо. Это не работает идеально. Он показывает только темы с активным тестом, поэтому: [['English', 2],['Spanish', 1]]. Немецкая тема отсутствует в списке. - person Jesús Iglesias; 17.05.2016
comment
Хорошо, я исправил нулевую проблему. Проблема в том, что когда нет связанного теста, t.active становится нулевым, что приводит к сбою условия. Таким образом, нулевая проверка исправит это. - person Emmanuel Rosa; 17.05.2016
comment
Проблема продолжается. Темы без активного теста не отображаются. Если раздел прогнозов опущен, в нем отображаются только две темы; третий не появляется (немецкий). - person Jesús Iglesias; 17.05.2016
comment
Просто поиграй с ним! Примечание. Вам нужны проекции для подсчета, любого типа или группировки. А знание SQL очень полезно. docs.grails.org/2.2.x/api/grails/ форма/ - person Emmanuel Rosa; 17.05.2016
comment
Да, это я знаю, что нужны проекции. Это был только пример, чтобы проверить, работает ли запрос хорошо или нет. В этом случае это не работает, потому что темы без активного теста не получаются. И это почему? - person Jesús Iglesias; 17.05.2016
comment
Да, это означает, что левое внешнее соединение не происходит. Я не знаю, почему; Я знаю, что использование псевдонима работает. Вы можете попробовать HQL. Что-то вроде этого: def activeTotalEachTopic = Topic.executeQuery('SELECT size(b) FROM Topic AS a LEFT OUTER JOIN a.tests AS b WHERE a.visibility = true AND (b.active = true OR b.id IS NULL) ORDER BY a.name ASC') - person Emmanuel Rosa; 17.05.2016
comment
Вы забыли GROUP BY a.name. Результат тот же. Не появляется немецкий. LEFT OUTER JOIN не работает. - person Jesús Iglesias; 17.05.2016
comment
Нет, я не делал. size() не является функцией группировки. Ок, я сдаюсь. Я предлагаю написать запрос SQL, а затем перевести его в запрос критериев или HQL. Вы обнаружите, что действительно необходимо левое внешнее соединение. - person Emmanuel Rosa; 17.05.2016