linkgress-orm 0.4.12 → 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.
- package/dist/entity/db-context.d.ts +24 -0
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +16 -0
- package/dist/entity/db-context.js.map +1 -1
- package/dist/query/query-builder.d.ts +9 -0
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +72 -7
- package/dist/query/query-builder.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
@@ -2712,14 +2764,22 @@ ${joinClauses.join('\n')}`;
|
|
|
2712
2764
|
// If the value is already a FieldRef
|
|
2713
2765
|
if (value && typeof value === 'object' && '__fieldName' in value && '__dbColumnName' in value) {
|
|
2714
2766
|
if (preserveOriginal) {
|
|
2715
|
-
// For WHERE: preserve original column name, table alias, and
|
|
2767
|
+
// For WHERE: preserve original column name, table alias, mapper, and navigation aliases
|
|
2716
2768
|
// This ensures WHERE references the actual database column with proper type conversion
|
|
2717
|
-
|
|
2769
|
+
// and can resolve intermediate JOINs for multi-level navigation
|
|
2770
|
+
const fieldRef = {
|
|
2718
2771
|
__fieldName: prop,
|
|
2719
2772
|
__dbColumnName: value.__dbColumnName,
|
|
2720
2773
|
__tableAlias: value.__tableAlias,
|
|
2721
2774
|
__mapper: value.__mapper, // Preserve mapper for toDriver in conditions
|
|
2722
2775
|
};
|
|
2776
|
+
if (value.__navigationAliases) {
|
|
2777
|
+
fieldRef.__navigationAliases = value.__navigationAliases;
|
|
2778
|
+
}
|
|
2779
|
+
if (value.__sourceTable) {
|
|
2780
|
+
fieldRef.__sourceTable = value.__sourceTable;
|
|
2781
|
+
}
|
|
2782
|
+
return fieldRef;
|
|
2723
2783
|
}
|
|
2724
2784
|
else {
|
|
2725
2785
|
// For ORDER BY: use the alias (property name) as the column name
|
|
@@ -3009,7 +3069,7 @@ ${joinClauses.join('\n')}`;
|
|
|
3009
3069
|
if (tableAlias && tableAlias !== this.schema.name) {
|
|
3010
3070
|
allTableAliases.add(tableAlias);
|
|
3011
3071
|
}
|
|
3012
|
-
// Also collect intermediate navigation aliases for multi-level navigation
|
|
3072
|
+
// Also collect intermediate navigation aliases for multi-level navigation (e.g., task.level.name)
|
|
3013
3073
|
if ('__navigationAliases' in value && Array.isArray(value.__navigationAliases)) {
|
|
3014
3074
|
for (const navAlias of value.__navigationAliases) {
|
|
3015
3075
|
if (navAlias && navAlias !== this.schema.name) {
|
|
@@ -3028,6 +3088,7 @@ ${joinClauses.join('\n')}`;
|
|
|
3028
3088
|
allTableAliases.add(tableAlias);
|
|
3029
3089
|
}
|
|
3030
3090
|
}
|
|
3091
|
+
// Also collect intermediate navigation aliases for multi-level navigation
|
|
3031
3092
|
if ('__navigationAliases' in fieldRef && Array.isArray(fieldRef.__navigationAliases)) {
|
|
3032
3093
|
for (const navAlias of fieldRef.__navigationAliases) {
|
|
3033
3094
|
if (navAlias && navAlias !== this.schema.name) {
|
|
@@ -3611,6 +3672,10 @@ ${joinClauses.join('\n')}`;
|
|
|
3611
3672
|
fromClause += `\n${joinClause}`;
|
|
3612
3673
|
}
|
|
3613
3674
|
}
|
|
3675
|
+
// Add COUNT(*) OVER() for countOver() support
|
|
3676
|
+
if (this._includeCountOver) {
|
|
3677
|
+
selectParts.push('COUNT(*) OVER() as "__countOver"');
|
|
3678
|
+
}
|
|
3614
3679
|
// Add DISTINCT if needed
|
|
3615
3680
|
const distinctClause = this.isDistinct ? 'DISTINCT ' : '';
|
|
3616
3681
|
finalQuery += `SELECT ${distinctClause}${selectParts.join(', ')}\n${fromClause}\n${whereClause}\n${orderByClause}\n${limitClause}`.trim();
|
|
@@ -3967,10 +4032,10 @@ ${joinClauses.join('\n')}`;
|
|
|
3967
4032
|
fieldConfigs.push({ key, type: 7 /* FieldType.FIELD_REF_NO_MAPPER */, value });
|
|
3968
4033
|
}
|
|
3969
4034
|
else {
|
|
3970
|
-
// Not in
|
|
3971
|
-
const
|
|
3972
|
-
if (
|
|
3973
|
-
fieldConfigs.push({ key, type: 6 /* FieldType.FIELD_REF_MAPPER */, value, mapper:
|
|
4035
|
+
// Not in root schema cache — check FieldRef's own mapper (from navigation properties)
|
|
4036
|
+
const fieldMapper = value.__mapper;
|
|
4037
|
+
if (fieldMapper && typeof fieldMapper.fromDriver === 'function') {
|
|
4038
|
+
fieldConfigs.push({ key, type: 6 /* FieldType.FIELD_REF_MAPPER */, value, mapper: fieldMapper });
|
|
3974
4039
|
}
|
|
3975
4040
|
else {
|
|
3976
4041
|
fieldConfigs.push({ key, type: 8 /* FieldType.SIMPLE */, value });
|