From 2b3e6865b56b95521673acc1d818a335ce6db480 Mon Sep 17 00:00:00 2001 From: "p.zahnen" Date: Mon, 1 Jun 2026 13:20:36 +0200 Subject: [PATCH 1/2] handle sortKeyUnique --- .../sql/app/FeatureQueryEncoderSql.java | 8 +-- .../features/sql/app/QuerySchemaDeriver.java | 3 ++ .../features/sql/app/SqlMappingDeriver.java | 2 + .../sql/app/SqlQueryTemplatesDeriver.java | 53 +++++++++++-------- .../features/sql/domain/SqlPathParser.java | 6 +++ .../features/sql/domain/SqlRelation.java | 18 ++++++- 6 files changed, 63 insertions(+), 27 deletions(-) diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/FeatureQueryEncoderSql.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/FeatureQueryEncoderSql.java index c09885fbc..2db99d644 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/FeatureQueryEncoderSql.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/FeatureQueryEncoderSql.java @@ -182,6 +182,7 @@ private SqlQuerySet createQuerySet( boolean skipMetaQuery) { List sortKeys = transformSortKeys(typeQuery.getSortKeys(), queryTemplates.getMapping()); + boolean useMinMaxKeys = queryTemplates.getMapping().getMainTable().isSortKeyUnique(); BiFunction> metaQuery = (maxLimit, skipped) -> @@ -212,9 +213,10 @@ private SqlQuerySet createQuerySet( sortKeys, typeQuery.getFilter(), typeQuery.forceSimpleFeatureGeometry(), - ((Objects.nonNull(metaResult.getMinKey()) - && Objects.nonNull(metaResult.getMaxKey())) - || metaResult.getNumberReturned() == 0) + (useMinMaxKeys + && ((Objects.nonNull(metaResult.getMinKey()) + && Objects.nonNull(metaResult.getMaxKey())) + || metaResult.getNumberReturned() == 0)) ? Optional.of( Tuple.of(metaResult.getMinKey(), metaResult.getMaxKey())) : Optional.empty(), diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/QuerySchemaDeriver.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/QuerySchemaDeriver.java index 25d0692b4..765f79279 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/QuerySchemaDeriver.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/QuerySchemaDeriver.java @@ -343,6 +343,9 @@ private List createTableParents( newParentPath.get(newParentPath.size() - 1) + "{sortKey=" + entry.getValue().get(0).getSortKey().get() + + "}" + + "{sortKeyUnique=" + + entry.getValue().get(0).getSortKeyUnique() + "}") : pathParser.parseTablePath(newParentPath.get(newParentPath.size() - 1)); diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlMappingDeriver.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlMappingDeriver.java index eb4db7271..43eff66f6 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlMappingDeriver.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlMappingDeriver.java @@ -390,6 +390,7 @@ private SqlQuerySchema derive( .name(sqlPath.getName()) .pathSegment(sqlPath.asPath()) .sortKey(sqlPath.getSortKey()) + .sortKeyUnique(sqlPath.getSortKeyUnique()) .primaryKey(sqlPath.getPrimaryKey()) .filter(sqlPath.getFilter().map(expr -> (Operation) expr)) .columns(columns.stream().map(column -> getColumn(schema, column)).toList()) @@ -426,6 +427,7 @@ private static List getJoins(SqlPath path, List> prev .pathSegment(parentTable.asPath()) .sourceField(childTable.getJoin().get().first()) .sortKey(parentTable.getSortKey()) + .sortKeyUnique(parentTable.getSortKeyUnique()) .filter(parentTable.getFilter().map(expr -> (Operation) expr)) .target(childTable.getName()) .targetField(childTable.getJoin().get().second()) diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java index dd53f9a9c..0886a0375 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java @@ -115,7 +115,7 @@ MetaQueryTemplate createMetaQueryTemplate(SqlQuerySchema schema, SqlQueryMapping columns += getSortColumn("A", sortKey, i) + ", "; } - columns += String.format("A.%s AS " + SKEY, schema.getSortKey()); + columns += getSkeyColumn("A", schema, "", false); String orderBy = getOrderBy(additionalSortKeys); String minMaxColumns = getMinMaxColumns(additionalSortKeys); @@ -180,8 +180,9 @@ ValueQueryTemplate createValueQueryTemplate(SqlQuerySchema schema, SqlQueryMappi ? sqlFilter : toWhereClause( aliases.get(0), main.getSortKey(), additionalSortKeys, minMaxKeys, sqlFilter); + boolean useRangePaging = additionalSortKeys.isEmpty() && minMaxKeys.isPresent(); Optional pagingClause = - additionalSortKeys.isEmpty() || (limit == 0 && offset == 0) + useRangePaging || (limit == 0 && offset == 0) ? Optional.empty() : Optional.of( String.format( @@ -348,11 +349,35 @@ private String formatLiteral(Object literal) { return String.format("'%s'", sqlDialect.escapeString(literalString)); } + private String getSkeyExpression( + String alias, SqlQueryTable table, boolean useRowNumberForNonUnique) { + if (table.isSortKeyUnique()) { + return String.format("%s.%s", alias, table.getSortKey()); + } + + if (!useRowNumberForNonUnique) { + return String.format("%s.%s", alias, table.getSortKey()); + } + + if (Objects.equals(table.getSortKey(), table.getPrimaryKey())) { + return String.format("ROW_NUMBER() OVER (ORDER BY %s.%s)", alias, table.getSortKey()); + } + + return String.format( + "ROW_NUMBER() OVER (ORDER BY %s.%s, %s.%s)", + alias, table.getSortKey(), alias, table.getPrimaryKey()); + } + + private String getSkeyColumn( + String alias, SqlQueryTable table, String suffix, boolean useRowNumberForNonUnique) { + return String.format( + "%s AS SKEY%s", getSkeyExpression(alias, table, useRowNumberForNonUnique), suffix); + } + private List getSortFields( SqlQuerySchema schema, List aliases, List additionalSortKeys) { final int[] i = {0}; - final int[] j = {0}; Stream customSortKeys = additionalSortKeys.stream().map(sortKey -> getSortColumn(aliases.get(0), sortKey, i[0]++)); @@ -370,14 +395,7 @@ private List getSortFields( .collect(Collectors.toList()); } else { return Stream.concat( - customSortKeys, - Stream.of( - String.format( - schema.isSortKeyUnique() - ? "%s.%s AS SKEY" - : "ROW_NUMBER() OVER (ORDER BY %s.%s) AS SKEY", - aliases.get(0), - schema.getSortKey()))) + customSortKeys, Stream.of(getSkeyColumn(aliases.get(0), schema, "", true))) .collect(Collectors.toList()); } } @@ -445,17 +463,10 @@ private List getSortKeys( ImmutableList.Builder keys = ImmutableList.builder(); int keyIndex = keyIndexStart; - SqlQueryTable previousRelation = null; if (!onlyRelations) { // add key for value table - keys.add( - String.format( - tables.get(0).isSortKeyUnique() - ? "%s.%s AS SKEY" - : "ROW_NUMBER() OVER (ORDER BY %s.%s) AS SKEY", - aliasesIterator.next(), - tables.get(0).getSortKey())); + keys.add(getSkeyColumn(aliasesIterator.next(), tables.get(0), "", true)); keyIndex++; } @@ -465,11 +476,9 @@ private List getSortKeys( if (!(relation instanceof SqlQueryJoin) || !((SqlQueryJoin) relation).isJunction()) { String suffix = keyIndex > 0 ? "_" + keyIndex : ""; - keys.add(String.format("%s.%s AS SKEY%s", alias, relation.getSortKey(), suffix)); + keys.add(getSkeyColumn(alias, relation, suffix, true)); keyIndex++; } - - previousRelation = relation; } return keys.build(); diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlPathParser.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlPathParser.java index 3791db656..cb7fec142 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlPathParser.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlPathParser.java @@ -666,9 +666,12 @@ private SqlRelation toRelation(SqlPath source, SqlPath target) { .sourceField(sourceField) .sourcePrimaryKey(source.getPrimaryKey()) .sourceSortKey(source.getSortKey()) + .sourceSortKeyUnique(source.getSortKeyUnique()) .sourceFilter(source.getFilterString()) .targetContainer(target.getName()) .targetField(targetField) + .targetSortKey(target.getSortKey()) + .targetSortKeyUnique(target.getSortKeyUnique()) .targetFilter(target.getFilterString()) .joinType(target.getJoinType().orElse(JoinType.INNER)) .build(); @@ -690,6 +693,7 @@ private SqlRelation toRelation(SqlPath source, SqlPath link, SqlPath target) { .sourceField(sourceField) .sourcePrimaryKey(source.getPrimaryKey()) .sourceSortKey(source.getSortKey()) + .sourceSortKeyUnique(source.getSortKeyUnique()) .sourceFilter(source.getFilterString()) .junctionSource(junctionSourceField) .junction(link.getName()) @@ -697,6 +701,8 @@ private SqlRelation toRelation(SqlPath source, SqlPath link, SqlPath target) { .junctionFilter(link.getFilterString()) .targetContainer(target.getName()) .targetField(targetField) + .targetSortKey(target.getSortKey()) + .targetSortKeyUnique(target.getSortKeyUnique()) .targetFilter(target.getFilterString()) .joinType(target.getJoinType().orElse(JoinType.INNER)) .build(); diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlRelation.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlRelation.java index a9cd4c35e..814220d24 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlRelation.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SqlRelation.java @@ -37,12 +37,18 @@ enum CARDINALITY { Optional getSourceSortKey(); + Optional getSourceSortKeyUnique(); + Optional getSourceFilter(); String getTargetContainer(); String getTargetField(); + Optional getTargetSortKey(); + + Optional getTargetSortKeyUnique(); + Optional getTargetFilter(); Optional getJunction(); @@ -96,19 +102,27 @@ default List asPath() { getJunction().get(), getJunctionFilter().map(filter -> "{filter=" + filter + "}").orElse("")), String.format( - "[%s=%s]%s%s", + "[%s=%s]%s%s%s%s", getJunctionTarget().get(), getTargetField(), getTargetContainer(), + getTargetSortKey().map(sortKey -> "{sortKey=" + sortKey + "}").orElse(""), + getTargetSortKeyUnique() + .map(sortKeyUnique -> "{sortKeyUnique=" + sortKeyUnique + "}") + .orElse(""), getTargetFilter().map(filter -> "{filter=" + filter + "}").orElse(""))); } return ImmutableList.of( String.format( - "[%s=%s]%s%s", + "[%s=%s]%s%s%s%s", getSourceField(), getTargetField(), getTargetContainer(), + getTargetSortKey().map(sortKey -> "{sortKey=" + sortKey + "}").orElse(""), + getTargetSortKeyUnique() + .map(sortKeyUnique -> "{sortKeyUnique=" + sortKeyUnique + "}") + .orElse(""), getTargetFilter().map(filter -> "{filter=" + filter + "}").orElse(""))); } } From 2df04ac3b3f7f26e48f82192b16d8b73b6be443b Mon Sep 17 00:00:00 2001 From: "p.zahnen" Date: Tue, 2 Jun 2026 11:01:04 +0200 Subject: [PATCH 2/2] simplify ROW_NUMBER ORDER BY to use sort key --- .../features/sql/app/SqlQueryTemplatesDeriver.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java index 0886a0375..44772eb71 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/app/SqlQueryTemplatesDeriver.java @@ -359,13 +359,7 @@ private String getSkeyExpression( return String.format("%s.%s", alias, table.getSortKey()); } - if (Objects.equals(table.getSortKey(), table.getPrimaryKey())) { - return String.format("ROW_NUMBER() OVER (ORDER BY %s.%s)", alias, table.getSortKey()); - } - - return String.format( - "ROW_NUMBER() OVER (ORDER BY %s.%s, %s.%s)", - alias, table.getSortKey(), alias, table.getPrimaryKey()); + return String.format("ROW_NUMBER() OVER (ORDER BY %s.%s)", alias, table.getSortKey()); } private String getSkeyColumn(