使用 Coalesce 时为空对象,加入时使用重复值

Null objects while using Coalesce and duplicate values while joining

[
  {
    "permissions": [
      {
        "name": "CREATE",
        "id": 1
      },
      {
        "name": "DELETE",
        "id": 4
      }
    ],
    "roles": [
      {
        "name": "ADMIN",
        "permission": [
          {
            "name": "CREATE",
            "id": 1
          },
          {
            "name": "UPDATE",
            "id": 2
          },
          {
            "name": "GET",
            "id": 3
          },
          {
            "name": "DELETE",
            "id": 4
          }
        ],
        "id": 1
      },
      {
        "name": "ADMIN",
        "permission": [
          {
            "name": "CREATE",
            "id": 1
          },
          {
            "name": "UPDATE",
            "id": 2
          },
          {
            "name": "GET",
            "id": 3
          },
          {
            "name": "DELETE",
            "id": 4
          }
        ],
        "id": 1
      }
    ],
    "id": 1,
    "username": "raj@100"
  },
  {
    "permissions": [
      {
        "name": null,
        "id": null
      }
    ],
    "roles": [
      {
        "name": "USER",
        "permission": [
          {
            "name": "GET",
            "id": 3
          }
        ],
        "id": 3
      }
    ],
    "id": 2,
    "username": "ram145"
  }
]

正如您从上面的输出中看到的,在角色中 ADMIN 重复了两次,在第二个用户中没有权限,所以他应该有一个空数组,但输出是权限对象,其所有值都是空的

这是执行的 jooq 语句:

public Object findAllUsers(String role, String permission) {
        SelectOnConditionStep<Record1<JSON>> query = dslContext.select(
                jsonObject(
                    key("id").value(USER.ID),
                    key("fullName").value(USER.FULL_NAME),
                    key("username").value(USER.USERNAME),
                    key("email").value(USER.EMAIL),
                    key("mobile").value(USER.MOBILE),
                    key("isActive").value(USER.IS_ACTIVE),
                    key("lastLoggedIn").value(USER.LAST_LOGGED_IN),
                    key("profileImage").value(USER.PROFILE_IMAGE),
                    key("roles").value(
                        coalesce(
                            jsonArrayAgg(
                                jsonObject(
                                    key("id").value(ROLE.ID),
                                    key("name").value(ROLE.NAME),
                                    key("permission").value(
                                        coalesce(
                                            select(
                                                jsonArrayAgg(
                                                    jsonObject(
                                                        key("id").value(PERMISSION.ID),
                                                        key("name").value(PERMISSION.NAME)
                                                    )
                                                )
                                            ).from(ROLE_PERMISSION)
                                                .leftJoin(PERMISSION)
                                                .on(PERMISSION.ID.eq(ROLE_PERMISSION.PERMISSION_ID))
                                                .where(ROLE_PERMISSION.ROLE_ID.eq(ROLE.ID))
                                                .orderBy(PERMISSION.NAME.asc()),
                                            jsonArray()
                                        )
                                    )
                                )
                            ),
                            jsonArray()
                        )
                    ),
                    key("permissions").value(
                        coalesce(
                            jsonArrayAgg(
                                jsonObject(
                                    key("id").value(PERMISSION.ID),
                                    key("name").value(PERMISSION.NAME)
                                )
                            ),
                            jsonArray()
                        )
                    )
                )
            ).from(USER)
            .leftJoin(USER_ROLE).on(USER.ID.eq(USER_ROLE.USER_ID))
            .leftJoin(ROLE).on(USER_ROLE.ROLE_ID.eq(ROLE.ID))
            .leftJoin(USER_PERMISSION).on(USER.ID.eq(USER_PERMISSION.USER_ID))
            .leftJoin(PERMISSION).on(USER_PERMISSION.PERMISSION_ID.eq(PERMISSION.ID));
        if (role != null) {
            query.where(ROLE.NAME.eq(role));
        }
        if (permission != null) {
            query.where(PERMISSION.NAME.eq(role));
        }
        return query.groupBy(USER.ID)
            .orderBy(USER.ID.asc())
            .fetch().into(JSONObject.class);
    }

有什么办法可以解决这个问题吗?

为什么重复?

您的连接图在两个“嵌套集合”ROLEPERMISSION 之间创建笛卡尔积。您不能单独使用 GROUP BY 删除该笛卡尔积,只有在您加入单个对多关系时才有效。

相反,您可以像这样编写子查询(您已经为 ROLE_PERMISSION 关系正确地执行了此操作):

dslContext.select(jsonObject(
    key("id").value(USER.ID),
    key("username").value(USER.USERNAME),
    key("roles").value(coalesce(field(
        select(jsonArrayAgg(jsonObject(
            key("id").value(ROLE.ID),
            key("name").value(ROLE.NAME),
            key("permission").value(coalesce(field(
                select(coalesce(jsonArrayAgg(jsonObject(
                    key("id").value(PERMISSION.ID),
                    key("name").value(PERMISSION.NAME)
                )), jsonArray()))
                .from(ROLE_PERMISSION)
                .join(PERMISSION)
                .on(PERMISSION.ID.eq(ROLE_PERMISSION.PERMISSION_ID))
                .where(ROLE_PERMISSION.ROLE_ID.eq(ROLE.ID))
                .orderBy(PERMISSION.NAME.asc())
            ), jsonArray()))
        )))
        .from(USER_ROLE)
        .join(ROLE)
        .on(USER_ROLE.ROLE_ID.eq(ROLE.ID))
        .where(USER_ROLE.USER_ID.eq(USER.ID))
    ), jsonArray())),
    key("permissions").value(coalesce(field(
        select(coalesce(jsonArrayAgg(jsonObject(
            key("id").value(PERMISSION.ID),
            key("name").value(PERMISSION.NAME)
        )))
        .from(USER_PERMISSION)
        .join(PERMISSION)
        .on(USER_PERMISSION.PERMISSION_ID.eq(PERMISSION.ID))
        .where(USER_PERMISSION.USER_ID.eq(USER.ID))
    ), jsonArray()))
))
.from(USER)
.orderBy(USER.ID.asc())
.fetch().into(JSONObject.class);

加入与半加入

在您将问题编辑为稍微不同的问题之后,您要表达的意思是您希望通过 ROLE 或 [USER table =13=] 他们必须有。你不能单独使用 JOIN 来实现这一点(除非你对重复项感到满意)。我给出的答案没有改变。如果您要加入多个对多关系,您将获得笛卡尔积。

那么,为什么不semi join them呢?使用 jOOQ 的合成 SEMI JOIN 语法,或手动使用 EXISTSIN,例如

.where(role != null
    ? exists(selectOne()
          .from(USER_ROLE)
          .where(USER_ROLE.role().NAME.eq(role))
      )
    : noCondition()
)
.and(permission != null
    ? exists(selectOne()
          .from(USER_PERMISSION)
          .where(USER_PERMISSION.permission().NAME.eq(permission))
      )
    : noCondition()
)

这是使用 implicit join syntax,这是可选的,但我认为它确实简化了您的查询。