Создайте собственную функцию агрегирования с помощью jOOQ - PullRequest
0 голосов
/ 21 октября 2018

Context

Я работаю с jOOQ против базы данных PostgreSQL .
Я хочу использовать jsonb_object_agg(name, value) в наборе результатов LEFT OUTER JOIN.

Проблема

Объединение - это OUTER соединение, иногда компонент name функции агрегирования просто null: это не может работать.Тогда я бы сказал:

COALESCE(
    json_object_agg(table.name, table.value) FILTER (WHERE table.name IS NOT NULL),
    '{}'
)::json

На данный момент код, который я использую для вызова jsonb_object_agg, (не точно, но сводится к следующему) следующий:

public static Field<?> jsonbObjectAgg(final Field<?> key, final Select<?> select) {
    return DSL.field("jsonb_object_agg({0}, ({1}))::jsonb", JSON_TYPE, key, select);
}

... где JSON_TYPE - это:

private static final DataType<JsonNode> JSON_TYPE = SQLDataType.VARCHAR.asConvertedDataType(/* a custom Converter */);

Неполное решение

Я хотел бы использовать интерфейс jOOQ AggregateFilterStep и, в частности,быть в состоянии использовать его AggregateFilterStep#filterWhere(Condition... conditions).

Однако класс org.jooq.impl.Function, который implements AggregateFilterStep (косвенно через AgregateFunction и ArrayAggOrderByStep) ограничен в видимости его package, поэтому я не могу просто слепо перерабатывать реализацию DSL#ArrayAggOrderByStep:

public static <T> ArrayAggOrderByStep<T[]> arrayAgg(Field<T> field) {
    return new org.jooq.impl.Function<T[]>(Term.ARRAY_AGG, field.getDataType().getArrayDataType(), nullSafe(field));
}

Попытки

Самое близкое, что я получил к чему-то разумному, это ...создание моей собственной функции coalesceAggregation, которая специально объединяет агрегированные поля:

//                                  Can't quite use AggregateFunction there
//                                                   v   v
public static <T> Field<T> coalesceAggregation(final Field<T> agg, final Condition coalesceWhen, @NonNull final T coalesceTo) {
    return DSL.coalesce(DSL.field("{0} FILTER (WHERE {1})", agg.getType(), agg, coalesceWhen), coalesceTo);
}

public static <T> Field<T> coalesceAggregation(final Field<T> agg, @NonNull final T coalesceTo) {
    return coalesceAggregation(agg, agg.isNotNull(), coalesceTo);
}

... Но затем я столкнулся с проблемами с моим типом T, равным JsonNode, где DSL#coalesce кажется CAST моим coalesceTo до varchar.

Или, вы знаете:

DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)

Но это было бы самым последним средством: эточувствую, что я просто в шаге отПользователь вводит любой SQL-запрос в мою базу данных ?

Короче говоря

Есть ли способ в jOOQ"должным образом" реализовать собственную агрегатную функцию как фактическуюorg.jooq.AgregateFunction?
Я бы хотел, чтобы он не генерировался как можно больше jooq-codegen (не то, чтобы мне это не нравилось - просто ужасен наш конвейер).

1 Ответ

0 голосов
/ 25 октября 2018

В API jOOQ DSL отсутствует функция, которая заключается в создании агрегатных функций простого SQL .Причина, по которой это еще не доступно (по состоянию на jOOQ 3.11), заключается в том, что существует множество тонких внутренних элементов для указания функции агрегирования агрегатов, поддерживающей все поставщики, включая:

  • FILTER (WHERE ...) предложение (как вы упомянули в вопросе), которое нужно эмулировать с помощью предложения CASE
  • OVER (...), чтобы превратить агрегатную функцию в оконную функцию
  • WITHIN GROUP (ORDER BY ...)предложение поддержки агрегатных функций упорядоченного набора
  • DISTINCT предложение, где поддерживается
  • Другие, специфичные для поставщика расширения агрегатных функций

Простой обходной путь в вашем конкретном случаедело в том, чтобы использовать обычный SQL-шаблон полностью так, как вы упомянули в своем вопросе:

DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)

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

... Но затем я столкнулся с проблемами с моим T-типом, являющимся JsonNode, где DSL # coalesce, кажется, CAST мой coalesceTo varchar.

Это, вероятно, потому что вы использовали agg.getType(), который возвращает Class<?> вместо agg.getDataType(), который возвращает DataType<?>.

Но это было бы самым последним средством: я чувствовал бы, что я всего в одном шаге от того, чтобы позволить пользователю вводить любой SQL, который он хочет, в мою базу данных

Я не уверен, почему это проблема здесь.Вы по-прежнему сможете самостоятельно контролировать использование простого SQL API, и пользователи не смогут вводить произвольные вещи в key и select, поскольку вы также управляете этими элементами.

...