linkgress-orm 0.4.13 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -479,6 +479,7 @@ class SelectQueryBuilder {
479
479
  this.manualJoins = [];
480
480
  this.joinCounter = 0;
481
481
  this.isDistinct = false;
482
+ this._includeCountOver = false;
482
483
  this.ctes = []; // Track CTEs attached to this query
483
484
  this.schema = schema;
484
485
  this.client = client;
@@ -1053,6 +1054,57 @@ class SelectQueryBuilder {
1053
1054
  : await this.client.query(sql, params);
1054
1055
  return parseInt(result.rows[0]?.count ?? '0', 10);
1055
1056
  }
1057
+ /**
1058
+ * Execute query and return results with total count using COUNT(*) OVER().
1059
+ * Useful for pagination - gets data and total count in a single query.
1060
+ */
1061
+ async countOver() {
1062
+ this._includeCountOver = true;
1063
+ try {
1064
+ const options = this.executor?.getOptions();
1065
+ const tracer = new db_context_1.TimeTracer(options?.traceTime ?? false, options?.logger);
1066
+ tracer.startPhase('queryBuild');
1067
+ const context = tracer.trace('createContext', () => ({
1068
+ ctes: new Map(),
1069
+ cteCounter: 0,
1070
+ paramCounter: 1,
1071
+ allParams: [],
1072
+ collectionStrategy: this.collectionStrategy,
1073
+ executor: this.executor,
1074
+ }));
1075
+ const mockRow = tracer.trace('createMockRow', () => this._createMockRow());
1076
+ const selectionResult = tracer.trace('evaluateSelector', () => this.selector(mockRow));
1077
+ const { sql, params, nestedPaths } = tracer.trace('buildQuery', () => this.buildQuery(selectionResult, context));
1078
+ tracer.endPhase();
1079
+ tracer.startPhase('queryExecution');
1080
+ const result = await tracer.traceAsync('executeQuery', async () => this.executor
1081
+ ? await this.executor.query(sql, params)
1082
+ : await this.client.query(sql, params), { rowCount: 'pending' });
1083
+ tracer.endPhase();
1084
+ // Extract totalCount from first raw row before transformation
1085
+ // PostgreSQL COUNT() OVER() returns bigint (string in pg driver)
1086
+ const totalCount = result.rows.length > 0 ? parseInt(result.rows[0].__countOver ?? '0', 10) : 0;
1087
+ // Strip __countOver from raw rows before processing
1088
+ for (const row of result.rows) {
1089
+ delete row.__countOver;
1090
+ }
1091
+ if (this.executor?.getOptions().rawResult) {
1092
+ return { data: result.rows, totalCount };
1093
+ }
1094
+ tracer.startPhase('resultProcessing');
1095
+ let rows = result.rows;
1096
+ if (nestedPaths.size > 0) {
1097
+ rows = tracer.trace('reconstructNestedObjects', () => rows.map(row => this.reconstructNestedObjects(row, nestedPaths)), { rowCount: rows.length });
1098
+ }
1099
+ const data = tracer.trace('transformResults', () => this.transformResults(rows, selectionResult), { rowCount: rows.length });
1100
+ tracer.endPhase();
1101
+ tracer.logSummary(data.length);
1102
+ return { data: data, totalCount };
1103
+ }
1104
+ finally {
1105
+ this._includeCountOver = false;
1106
+ }
1107
+ }
1056
1108
  /**
1057
1109
  * Check if any rows match the query
1058
1110
  * More efficient than count() > 0 as it can stop after finding one row
@@ -3620,6 +3672,10 @@ ${joinClauses.join('\n')}`;
3620
3672
  fromClause += `\n${joinClause}`;
3621
3673
  }
3622
3674
  }
3675
+ // Add COUNT(*) OVER() for countOver() support
3676
+ if (this._includeCountOver) {
3677
+ selectParts.push('COUNT(*) OVER() as "__countOver"');
3678
+ }
3623
3679
  // Add DISTINCT if needed
3624
3680
  const distinctClause = this.isDistinct ? 'DISTINCT ' : '';
3625
3681
  finalQuery += `SELECT ${distinctClause}${selectParts.join(', ')}\n${fromClause}\n${whereClause}\n${orderByClause}\n${limitClause}`.trim();