无法使用postgres数组正确构建查询包含运算符通过JOOQ

时间:2018-01-25 16:50:24

标签: postgresql kotlin jooq

我无法将此查询转换为jooq(它是更大查询的一部分)

想法是仅选择具有所有指定属性的产品

WITH "products" as (

    SELECT
      p.id,
      (
        SELECT coalesce(
            array_agg(attrs),
            ARRAY ['NO_RELATIONS']
        )
        FROM unnest(ARRAY [
                    tts.v,
                    ingredient.v,
                    variation.v
                    ]) AS "attrs"
        WHERE attrs IS NOT NULL
      ) AS "attributes"
    FROM t_product p
      LEFT JOIN LATERAL (SELECT 'PRODUCED' :: TEXT AS v
                         FROM t_tech_spech AS tts
                         WHERE tts.product_id = p.id
                         LIMIT 1) AS tts ON TRUE
      LEFT JOIN LATERAL (SELECT 'INGREDIENT' :: TEXT AS v
                         FROM t_tech_spech_item AS ingredient
                         WHERE ingredient.product_id = p.id
                         LIMIT 1) AS ingredient ON TRUE
      LEFT JOIN LATERAL (SELECT 'FOR_SALE' :: TEXT AS v
                         FROM t_product_variation AS variation
                         WHERE variation.catalog_product_id = p.id
                         LIMIT 1) AS variation ON TRUE
) SELECT id, "attributes"
FROM products
WHERE attributes @> ARRAY ['PRODUCED', 'INGREDIENT'];

我已经完成了大部分工作,没有问题

val tts =
    dsl.select(inline("PRODUCED").cast(TEXT).`as`("value"))
        .from(T_TECH_SPECH)
        .where(T_TECH_SPECH.PRODUCT_ID.equal(product.ID))
        .limit(1)
        .asTable("tts")

val ingredient =
    dsl.select(inline("INGREDIENT").cast(TEXT).`as`("value"))
        .from(T_TECH_SPECH_ITEM)
        .where(T_TECH_SPECH_ITEM.PRODUCT_ID.equal(product.ID))
        .limit(1)
        .asTable("ingredient")

val variation =
    dsl.select(inline("FOR_SALE").cast(TEXT).`as`("value"))
        .from(T_PRODUCT_VARIATION)
        .where(T_PRODUCT_VARIATION.CATALOG_PRODUCT_ID.equal(product.ID))
        .and(T_PRODUCT_VARIATION.IS_DELETED.eq(false))
        .limit(1)
        .asTable("variation")

val attributes =
            dsl.select(coalesce(arrayAgg(field("attrs")), field(array("NO_RELATIONS"))))
                .from(
                    unnest(
                        array(
                            field("tts.value"),
                            field("ingredient.value"),
                            field("variation.value")
                        )
                    ).`as`("attrs")
                )
                .where(field("attrs").isNotNull).asField<Array<String>>("attributes")

dsl.with("products")
            .`as`(
                select(
                    product.ID,
                    field(attributes)
                ).from(product)
                    .leftJoin(lateral(tts)).on(trueCondition())
                    .leftJoin(lateral(ingredient)).on(trueCondition())
                    .leftJoin(lateral(variation)).on(trueCondition())
            )
            .with("products_filtered")
            .`as`(
                select(
                    product.ID,
                    field("products.attributes").`as`("attributes")
                ).from("products")
                    .join(product).on(product.ID.eq(field("products.id", UUID::class.java)))
                    .where(trueCondition())
                    .apply { attributesFilter(pAttributes) }
            )

private fun <T : Record> SelectConditionStep<T>.attributesFilter(pAttributes: List<ProductAttribute>) {
    if (pAttributes.isNotEmpty()) {
        val filter = pAttributes.joinToString(",") { StringUtils.wrap(it.toString(), "'") }
        and("attributes @> ARRAY [$filter]")
    }
}

,最后一部分是函数attributesFilter,它只是连接值并将其粘贴为原始sql

List<ProductAttribute>是枚举值列表(例如FOR_SALE, INGREDIENT, PRODUCED

我缺少可以做的功能&#39;数组包含&#39;在jooq中的操作

如何使用来自attributes @> ARRAY ['PRODUCED', 'INGREDIENT']的实际值?

来编写List<ProductAttribute>

修改1

尝试了Lukas提出的功能,但它对我不起作用

这就是我现在attributesFilter中的内容

val field = this.field("products.attributes", TEXT.arrayDataType) 
and(field.contains(productAttributes.map { it.toString() }.toTypedArray())) 

这个实现给了我这个sql,这是不对的:

"alias_74630789"."products.attributes" @> cast('{"INGREDIENT","FOR_SALE"}' as varchar[]) 

1 个答案:

答案 0 :(得分:1)

我有个好消息。请参阅Field.contains(T)的Javadoc:

  

如果您正在使用SQLDialect.POSTGRES,那么您也可以使用此方法来表达&#34; ARRAY包含&#34;运营商。例如:

     
main.js

关于不受欢迎的// Use this expression val(new Integer[] { 1, 2, 3 }).contains(new Integer[] { 1, 2 }) // ... to render this SQL ARRAY[1, 2, 3] @> ARRAY[1, 2] 演员:

注意,您似乎遇到了https://github.com/jOOQ/jOOQ/issues/4754。解决方法是使用纯SQL: https://www.jooq.org/doc/latest/manual/sql-building/plain-sql