Skip to content

Commit 8a5e67e

Browse files
author
Komal Yadav
committed
updated
1 parent f5bcfc3 commit 8a5e67e

File tree

1 file changed

+46
-42
lines changed

1 file changed

+46
-42
lines changed

cdap-metadata-ext-spanner/src/main/java/io/cdap/cdap/metadata/spanner/SpannerMetadataStorage.java

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,6 @@ private SearchResponse performSpannerSearch(SearchRequest request,
533533
LOG.info("Executing Spanner SQL Template: {}", statement.getSql());
534534
LOG.info("With Parameters: {}", statement.getParameters());
535535

536-
// 3. Execute and process results.
537536
ResultSet resultSet = transaction.executeQuery(statement);
538537
List<MetadataRecord> results = new ArrayList<>();
539538
String nextActualCursor = null;
@@ -545,8 +544,7 @@ private SearchResponse performSpannerSearch(SearchRequest request,
545544

546545
LOG.info("Found {} results.", results.size());
547546

548-
// 4. Create the final response, including the next page's cursor.
549-
return createSpannerSearchResponse(request, results, nextActualCursor);
547+
return createSearchResponse(request, results, nextActualCursor);
550548

551549
}
552550
}
@@ -563,18 +561,16 @@ private QueryBuildResult buildSpannerQuery(SearchRequest request, @Nullable Curs
563561
List<String> sortColumns = sortDetails.getColumns();
564562
List<Sorting.Order> sortOrders = sortDetails.getOrders();
565563

566-
// Step 2: Add standard filter conditions (namespaces, types, etc.) to the WHERE clause.
564+
// Add standard filter conditions (namespaces, types, etc.) to the WHERE clause.
567565
appendFilterConditions(request, conditions, params);
568566

569-
// Step 3: Add the special WHERE condition for keyset pagination if a cursor exists.
567+
// Add the special WHERE condition for keyset pagination if a cursor exists.
570568
appendCursorCondition(requestCursor, sortColumns, sortOrders, conditions, params);
571-
572-
// Step 4: Assemble the final query string.
573569
if (!conditions.isEmpty()) {
574570
sql.append(" WHERE ").append(String.join(" AND ", conditions));
575571
}
576572

577-
// Step 5: Add the ORDER BY clause.
573+
// Add the ORDER BY clause.
578574
List<String> orderByClauses = new ArrayList<>();
579575
for (int i = 0; i < sortColumns.size(); i++) {
580576
orderByClauses.add(sortColumns.get(i) + " " + sortOrders.get(i).name());
@@ -613,7 +609,6 @@ private SortDetailsResult getSortDetails(SearchRequest request) {
613609
orders.add(Sorting.Order.ASC);
614610
}
615611

616-
// Return the new type-safe object
617612
return new SortDetailsResult(columns, orders);
618613
}
619614

@@ -719,7 +714,6 @@ private String convertToRegexpPattern(String sqlWildcardPattern) {
719714
}
720715

721716
/**
722-
* UPDATED HELPER METHOD
723717
* Builds the condition for a simple term search based on the provided scope.
724718
* Now uses the precise SEARCH_SUBSTRING syntax.
725719
*
@@ -751,46 +745,59 @@ private String buildScopedSearchCondition(String searchTerm, @Nullable MetadataS
751745
}
752746

753747
/**
754-
* NEW HELPER METHOD
755748
* Appends the complex WHERE condition for keyset pagination if a cursor is provided.
756749
* This isolates the most complicated part of the query construction.
757750
*/
758751
private void appendCursorCondition(@Nullable Cursor requestCursor, List<String> sortColumns,
759752
List<Sorting.Order> sortOrders, List<String> conditions,
760753
Map<String, Value> params) {
761-
if (requestCursor == null || requestCursor.getActualCursor() == null || requestCursor.getActualCursor().isEmpty()) {
762-
return;
754+
// 1. Validate the cursor
755+
if (requestCursor == null || requestCursor.getActualCursor() == null) {
756+
return; // No cursor, no pagination condition needed.
763757
}
764758

765759
String[] cursorValues = requestCursor.getActualCursor().split(",", -1);
766760
if (cursorValues.length != sortColumns.size()) {
767-
LOG.warn("Cursor values do not match sort columns. Ignoring cursor. Cursor: '{}', SortColumns: '{}'",
768-
requestCursor.getActualCursor(), sortColumns);
769-
return;
761+
LOG.warn("Cursor values count ({}) does not match sort columns count ({}). Ignoring cursor. " +
762+
"Cursor: '{}', SortColumns: '{}'",
763+
cursorValues.length, sortColumns.size(), requestCursor.getActualCursor(), sortColumns);
764+
return; // Invalid cursor format, cannot apply pagination.
770765
}
771766

772-
List<String> pageConditions = new ArrayList<>();
767+
List<String> combinedRowConditions = new ArrayList<>();
768+
List<String> prefixEqualityConditions = new ArrayList<>();
769+
770+
// Iterate through each column in the sort order to build the multi-column comparison
773771
for (int i = 0; i < sortColumns.size(); i++) {
774-
List<String> keyConditions = new ArrayList<>();
775-
String colName = sortColumns.get(i);
776-
777-
for (int j = 0; j < i; j++) {
778-
String priorCol = sortColumns.get(j);
779-
String paramName = "cursor_" + priorCol;
780-
keyConditions.add(String.format("%s = @%s", priorCol, paramName));
781-
params.put(paramName, Value.string(cursorValues[j]));
772+
String currentSortColumn = sortColumns.get(i);
773+
Sorting.Order currentSortOrder = sortOrders.get(i);
774+
String currentCursorValue = cursorValues[i];
775+
776+
// Add the equality condition for the *previous* column to the prefix list
777+
if (i > 0) {
778+
String priorColumn = sortColumns.get(i - 1);
779+
String priorCursorValue = cursorValues[i - 1];
780+
String paramName = "cursor_eq_" + priorColumn + "_" + (i - 1);
781+
params.put(paramName, Value.string(priorCursorValue));
782+
prefixEqualityConditions.add(String.format("%s = @%s", priorColumn, paramName));
782783
}
783784

784-
String operator = sortOrders.get(i) == Sorting.Order.ASC ? ">" : "<";
785-
String paramName = "cursor_" + colName;
786-
keyConditions.add(String.format("%s %s @%s", colName, operator, paramName));
787-
params.put(paramName, Value.string(cursorValues[i]));
785+
// Start building the conditions for the current OR clause
786+
List<String> currentClauseConditions = new ArrayList<>(prefixEqualityConditions);
787+
String operator = (currentSortOrder == Sorting.Order.ASC) ? ">" : "<";
788+
String paramName = "cursor_gtlt_" + currentSortColumn + "_" + i;
789+
params.put(paramName, Value.string(currentCursorValue));
790+
currentClauseConditions.add(String.format("%s %s @%s", currentSortColumn, operator, paramName));
791+
combinedRowConditions.add("(" + String.join(" AND ", currentClauseConditions) + ")");
792+
}
788793

789-
pageConditions.add("(" + String.join(" AND ", keyConditions) + ")");
794+
// 3. Add the final OR-ed condition to the main conditions list
795+
if (!combinedRowConditions.isEmpty()) {
796+
conditions.add("(" + String.join(" OR ", combinedRowConditions) + ")");
790797
}
791-
conditions.add("(" + String.join(" OR ", pageConditions) + ")");
792798
}
793799

800+
794801
/**
795802
* Creates the "actualCursor" part of the next cursor. This is the "bookmark" itself.
796803
* It's now based on the actual columns that were used for sorting.
@@ -807,7 +814,7 @@ private String createNextCursorKey(ResultSet resultSet, List<String> sortColumns
807814
/**
808815
* Creates the final SearchResponse, correctly packaging the next cursor string.
809816
*/
810-
private SearchResponse createSpannerSearchResponse(SearchRequest request, List<MetadataRecord> results,
817+
private SearchResponse createSearchResponse(SearchRequest request, List<MetadataRecord> results,
811818
String nextActualCursor) {
812819
String finalCursorString = (nextActualCursor != null) ?
813820
getCursor(request, results, nextActualCursor).toString() : null;
@@ -838,12 +845,9 @@ private Cursor getCursor(SearchRequest request, List<MetadataRecord> results, St
838845
private MetadataRecord mapSpannerResult(ResultSet resultSet) {
839846
String documentId = resultSet.getString(Tables.Metadata.METADATA_ID_FIELD);
840847
Struct row = resultSet.getCurrentRowAsStruct();
841-
LOG.info(row.toString());
842848
String metadataJson = row.getJson(7);
843849
Metadata metadata = GSON.fromJson(metadataJson, Metadata.class);
844850
MetadataEntity entity = toMetadataEntity(documentId);
845-
846-
847851
return new MetadataRecord(entity, metadata);
848852
}
849853

@@ -861,25 +865,25 @@ private static String mapSortKey(String key) {
861865
}
862866

863867
/**
864-
* Translate a document id in the index into a metadata entity.
868+
* Translate a metadata id in the index into a metadata entity.
865869
*/
866-
private static MetadataEntity toMetadataEntity(String documentId) {
867-
int index = documentId.indexOf(':');
870+
private static MetadataEntity toMetadataEntity(String metadataId) {
871+
int index = metadataId.indexOf(':');
868872
if (index < 0) {
869873
throw new IllegalArgumentException(
870-
"Document Id must be of the form 'type:k=v,...' but is " + documentId);
874+
"Metadata Id must be of the form 'type:k=v,...' but is " + metadataId);
871875
}
872-
String type = documentId.substring(0, index);
876+
String type = metadataId.substring(0, index);
873877
MetadataEntity.Builder builder = MetadataEntity.builder();
874-
for (String part : documentId.substring(index + 1).split(",")) {
878+
for (String part : metadataId.substring(index + 1).split(",")) {
875879
String[] parts = part.split("=", 2);
876880
if (parts[0].equals(type)) {
877881
builder.appendAsType(parts[0], parts[1]);
878882
} else {
879883
builder.append(parts[0], parts[1]);
880884
}
881885
}
882-
// TODO (CDAP-13597): Handle versioning of metadata entities in a better way
886+
883887
// if it is a versioned entity then add the default version
884888
return MetadataUtil.addVersionIfNeeded(builder.build());
885889
}

0 commit comments

Comments
 (0)