postgresdk 0.18.15 → 0.18.17
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/cli.js +58 -18
- package/dist/index.js +58 -18
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2740,8 +2740,8 @@ async function introspect(connectionString, schema) {
|
|
|
2740
2740
|
c.column_default,
|
|
2741
2741
|
a.atttypmod
|
|
2742
2742
|
FROM information_schema.columns c
|
|
2743
|
-
LEFT JOIN pg_catalog.
|
|
2744
|
-
LEFT JOIN pg_catalog.
|
|
2743
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = c.table_schema
|
|
2744
|
+
LEFT JOIN pg_catalog.pg_class cl ON cl.relname = c.table_name AND cl.relnamespace = n.oid
|
|
2745
2745
|
LEFT JOIN pg_catalog.pg_attribute a ON a.attrelid = cl.oid AND a.attname = c.column_name
|
|
2746
2746
|
WHERE c.table_schema = $1
|
|
2747
2747
|
ORDER BY c.table_name, c.ordinal_position
|
|
@@ -3128,8 +3128,9 @@ export const ${Type}ListParamsSchema = z.object({
|
|
|
3128
3128
|
offset: z.number().int().nonnegative().optional(),
|
|
3129
3129
|
where: z.any().optional(),
|
|
3130
3130
|
vector: VectorSearchParamsSchema.optional(),
|
|
3131
|
-
orderBy: z.enum([${columnNames}]).optional(),
|
|
3132
|
-
order: z.enum(["asc", "desc"]).optional()
|
|
3131
|
+
orderBy: z.union([z.enum([${columnNames}]), z.array(z.enum([${columnNames}]))]).optional(),
|
|
3132
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
3133
|
+
distinctOn: z.union([z.enum([${columnNames}]), z.array(z.enum([${columnNames}]))]).optional()
|
|
3133
3134
|
}).strict();
|
|
3134
3135
|
|
|
3135
3136
|
// Schema for ordering parameters
|
|
@@ -3263,7 +3264,8 @@ const listSchema = z.object({
|
|
|
3263
3264
|
limit: z.number().int().positive().max(1000).optional(),
|
|
3264
3265
|
offset: z.number().int().min(0).optional(),
|
|
3265
3266
|
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
3266
|
-
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional()
|
|
3267
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
3268
|
+
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional(),${hasVectorColumns ? `
|
|
3267
3269
|
vector: z.object({
|
|
3268
3270
|
field: z.string(),
|
|
3269
3271
|
query: z.array(z.number()),
|
|
@@ -3939,6 +3941,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3939
3941
|
};` : ""}
|
|
3940
3942
|
orderBy?: string | string[];
|
|
3941
3943
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3944
|
+
distinctOn?: string | string[];
|
|
3942
3945
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3943
3946
|
/**
|
|
3944
3947
|
* List ${table.name} records with field exclusion
|
|
@@ -3959,6 +3962,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3959
3962
|
};` : ""}
|
|
3960
3963
|
orderBy?: string | string[];
|
|
3961
3964
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3965
|
+
distinctOn?: string | string[];
|
|
3962
3966
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3963
3967
|
/**
|
|
3964
3968
|
* List ${table.name} records with pagination and filtering
|
|
@@ -3990,6 +3994,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3990
3994
|
};` : ""}
|
|
3991
3995
|
orderBy?: string | string[];
|
|
3992
3996
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3997
|
+
distinctOn?: string | string[];
|
|
3993
3998
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3994
3999
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3995
4000
|
include?: ${Type}IncludeSpec;
|
|
@@ -4006,6 +4011,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4006
4011
|
};` : ""}
|
|
4007
4012
|
orderBy?: string | string[];
|
|
4008
4013
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4014
|
+
distinctOn?: string | string[];
|
|
4009
4015
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>>>> {
|
|
4010
4016
|
return this.post<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
4011
4017
|
}` : ` /**
|
|
@@ -4027,6 +4033,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4027
4033
|
};` : ""}
|
|
4028
4034
|
orderBy?: string | string[];
|
|
4029
4035
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4036
|
+
distinctOn?: string | string[];
|
|
4030
4037
|
}): Promise<PaginatedResponse<Partial<Select${Type}>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4031
4038
|
/**
|
|
4032
4039
|
* List ${table.name} records with field exclusion
|
|
@@ -4047,6 +4054,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4047
4054
|
};` : ""}
|
|
4048
4055
|
orderBy?: string | string[];
|
|
4049
4056
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4057
|
+
distinctOn?: string | string[];
|
|
4050
4058
|
}): Promise<PaginatedResponse<Partial<Select${Type}>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4051
4059
|
/**
|
|
4052
4060
|
* List ${table.name} records with pagination and filtering
|
|
@@ -4072,6 +4080,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4072
4080
|
};` : ""}
|
|
4073
4081
|
orderBy?: string | string[];
|
|
4074
4082
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4083
|
+
distinctOn?: string | string[];
|
|
4075
4084
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4076
4085
|
async list(params?: {
|
|
4077
4086
|
include?: ${Type}IncludeSpec;
|
|
@@ -4088,6 +4097,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4088
4097
|
};` : ""}
|
|
4089
4098
|
orderBy?: string | string[];
|
|
4090
4099
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4100
|
+
distinctOn?: string | string[];
|
|
4091
4101
|
}): Promise<PaginatedResponse<Select${Type} | Partial<Select${Type}>>> {
|
|
4092
4102
|
return this.post<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
4093
4103
|
}`}
|
|
@@ -6218,6 +6228,7 @@ export async function listRecords(
|
|
|
6218
6228
|
include?: any;
|
|
6219
6229
|
orderBy?: string | string[];
|
|
6220
6230
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
6231
|
+
distinctOn?: string | string[];
|
|
6221
6232
|
vector?: {
|
|
6222
6233
|
field: string;
|
|
6223
6234
|
query: number[];
|
|
@@ -6227,7 +6238,11 @@ export async function listRecords(
|
|
|
6227
6238
|
}
|
|
6228
6239
|
): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
6229
6240
|
try {
|
|
6230
|
-
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector } = params;
|
|
6241
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, distinctOn } = params;
|
|
6242
|
+
|
|
6243
|
+
// DISTINCT ON support
|
|
6244
|
+
const distinctCols: string[] | null = distinctOn ? (Array.isArray(distinctOn) ? distinctOn : [distinctOn]) : null;
|
|
6245
|
+
const _distinctOnColsSQL = distinctCols ? distinctCols.map(c => '"' + c + '"').join(', ') : '';
|
|
6231
6246
|
|
|
6232
6247
|
// Get distance operator if vector search
|
|
6233
6248
|
const distanceOp = vector ? getVectorDistanceOperator(vector.metric) : "";
|
|
@@ -6286,26 +6301,49 @@ export async function listRecords(
|
|
|
6286
6301
|
}
|
|
6287
6302
|
|
|
6288
6303
|
// Build SELECT clause
|
|
6304
|
+
const _distinctOnPrefix = distinctCols ? 'DISTINCT ON (' + _distinctOnColsSQL + ') ' : '';
|
|
6289
6305
|
const baseColumns = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
6290
6306
|
const selectClause = vector
|
|
6291
|
-
? \`\${baseColumns}, ("\${vector.field}" \${distanceOp} ($1)::vector) AS _distance\`
|
|
6292
|
-
: baseColumns
|
|
6307
|
+
? \`\${_distinctOnPrefix}\${baseColumns}, ("\${vector.field}" \${distanceOp} ($1)::vector) AS _distance\`
|
|
6308
|
+
: \`\${_distinctOnPrefix}\${baseColumns}\`;
|
|
6293
6309
|
|
|
6294
6310
|
// Build ORDER BY clause
|
|
6295
6311
|
let orderBySQL = "";
|
|
6296
6312
|
if (vector) {
|
|
6297
6313
|
// For vector search, always order by distance
|
|
6298
6314
|
orderBySQL = \`ORDER BY "\${vector.field}" \${distanceOp} ($1)::vector\`;
|
|
6299
|
-
} else
|
|
6300
|
-
const
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6315
|
+
} else {
|
|
6316
|
+
const userCols = orderBy ? (Array.isArray(orderBy) ? orderBy : [orderBy]) : [];
|
|
6317
|
+
const userDirs: ("asc" | "desc")[] = orderBy
|
|
6318
|
+
? (Array.isArray(order) ? order : (order ? Array(userCols.length).fill(order) : Array(userCols.length).fill("asc")))
|
|
6319
|
+
: [];
|
|
6320
|
+
|
|
6321
|
+
const finalCols: string[] = [];
|
|
6322
|
+
const finalDirs: string[] = [];
|
|
6323
|
+
|
|
6324
|
+
if (distinctCols) {
|
|
6325
|
+
// DISTINCT ON requires its columns to be the leftmost ORDER BY prefix
|
|
6326
|
+
for (const col of distinctCols) {
|
|
6327
|
+
const userIdx = userCols.indexOf(col);
|
|
6328
|
+
finalCols.push(col);
|
|
6329
|
+
finalDirs.push(userIdx >= 0 ? (userDirs[userIdx] || "asc") : "asc");
|
|
6330
|
+
}
|
|
6331
|
+
// Append remaining user-specified cols not already covered by distinctOn
|
|
6332
|
+
for (let i = 0; i < userCols.length; i++) {
|
|
6333
|
+
if (!distinctCols.includes(userCols[i]!)) {
|
|
6334
|
+
finalCols.push(userCols[i]!);
|
|
6335
|
+
finalDirs.push(userDirs[i] || "asc");
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
6338
|
+
} else {
|
|
6339
|
+
finalCols.push(...userCols);
|
|
6340
|
+
finalDirs.push(...userDirs.map(d => d || "asc"));
|
|
6341
|
+
}
|
|
6307
6342
|
|
|
6308
|
-
|
|
6343
|
+
if (finalCols.length > 0) {
|
|
6344
|
+
const orderParts = finalCols.map((c, i) => \`"\${c}" \${finalDirs[i]!.toUpperCase()}\`);
|
|
6345
|
+
orderBySQL = \`ORDER BY \${orderParts.join(", ")}\`;
|
|
6346
|
+
}
|
|
6309
6347
|
}
|
|
6310
6348
|
|
|
6311
6349
|
// Add limit and offset params
|
|
@@ -6314,7 +6352,9 @@ export async function listRecords(
|
|
|
6314
6352
|
const allParams = [...queryParams, ...whereParams, limit, offset];
|
|
6315
6353
|
|
|
6316
6354
|
// Get total count for pagination
|
|
6317
|
-
const countText =
|
|
6355
|
+
const countText = distinctCols
|
|
6356
|
+
? \`SELECT COUNT(*) FROM (SELECT DISTINCT ON (\${_distinctOnColsSQL}) 1 FROM "\${ctx.table}" \${countWhereSQL} ORDER BY \${_distinctOnColsSQL}) __distinct_count\`
|
|
6357
|
+
: \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
6318
6358
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
6319
6359
|
const countResult = await ctx.pg.query(countText, countParams);
|
|
6320
6360
|
const total = parseInt(countResult.rows[0].count, 10);
|
package/dist/index.js
CHANGED
|
@@ -1779,8 +1779,8 @@ async function introspect(connectionString, schema) {
|
|
|
1779
1779
|
c.column_default,
|
|
1780
1780
|
a.atttypmod
|
|
1781
1781
|
FROM information_schema.columns c
|
|
1782
|
-
LEFT JOIN pg_catalog.
|
|
1783
|
-
LEFT JOIN pg_catalog.
|
|
1782
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = c.table_schema
|
|
1783
|
+
LEFT JOIN pg_catalog.pg_class cl ON cl.relname = c.table_name AND cl.relnamespace = n.oid
|
|
1784
1784
|
LEFT JOIN pg_catalog.pg_attribute a ON a.attrelid = cl.oid AND a.attname = c.column_name
|
|
1785
1785
|
WHERE c.table_schema = $1
|
|
1786
1786
|
ORDER BY c.table_name, c.ordinal_position
|
|
@@ -2167,8 +2167,9 @@ export const ${Type}ListParamsSchema = z.object({
|
|
|
2167
2167
|
offset: z.number().int().nonnegative().optional(),
|
|
2168
2168
|
where: z.any().optional(),
|
|
2169
2169
|
vector: VectorSearchParamsSchema.optional(),
|
|
2170
|
-
orderBy: z.enum([${columnNames}]).optional(),
|
|
2171
|
-
order: z.enum(["asc", "desc"]).optional()
|
|
2170
|
+
orderBy: z.union([z.enum([${columnNames}]), z.array(z.enum([${columnNames}]))]).optional(),
|
|
2171
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
2172
|
+
distinctOn: z.union([z.enum([${columnNames}]), z.array(z.enum([${columnNames}]))]).optional()
|
|
2172
2173
|
}).strict();
|
|
2173
2174
|
|
|
2174
2175
|
// Schema for ordering parameters
|
|
@@ -2302,7 +2303,8 @@ const listSchema = z.object({
|
|
|
2302
2303
|
limit: z.number().int().positive().max(1000).optional(),
|
|
2303
2304
|
offset: z.number().int().min(0).optional(),
|
|
2304
2305
|
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
2305
|
-
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional()
|
|
2306
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
2307
|
+
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional(),${hasVectorColumns ? `
|
|
2306
2308
|
vector: z.object({
|
|
2307
2309
|
field: z.string(),
|
|
2308
2310
|
query: z.array(z.number()),
|
|
@@ -2978,6 +2980,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2978
2980
|
};` : ""}
|
|
2979
2981
|
orderBy?: string | string[];
|
|
2980
2982
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2983
|
+
distinctOn?: string | string[];
|
|
2981
2984
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
2982
2985
|
/**
|
|
2983
2986
|
* List ${table.name} records with field exclusion
|
|
@@ -2998,6 +3001,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2998
3001
|
};` : ""}
|
|
2999
3002
|
orderBy?: string | string[];
|
|
3000
3003
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3004
|
+
distinctOn?: string | string[];
|
|
3001
3005
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3002
3006
|
/**
|
|
3003
3007
|
* List ${table.name} records with pagination and filtering
|
|
@@ -3029,6 +3033,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3029
3033
|
};` : ""}
|
|
3030
3034
|
orderBy?: string | string[];
|
|
3031
3035
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3036
|
+
distinctOn?: string | string[];
|
|
3032
3037
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3033
3038
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3034
3039
|
include?: ${Type}IncludeSpec;
|
|
@@ -3045,6 +3050,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3045
3050
|
};` : ""}
|
|
3046
3051
|
orderBy?: string | string[];
|
|
3047
3052
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3053
|
+
distinctOn?: string | string[];
|
|
3048
3054
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>>>> {
|
|
3049
3055
|
return this.post<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
3050
3056
|
}` : ` /**
|
|
@@ -3066,6 +3072,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3066
3072
|
};` : ""}
|
|
3067
3073
|
orderBy?: string | string[];
|
|
3068
3074
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3075
|
+
distinctOn?: string | string[];
|
|
3069
3076
|
}): Promise<PaginatedResponse<Partial<Select${Type}>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3070
3077
|
/**
|
|
3071
3078
|
* List ${table.name} records with field exclusion
|
|
@@ -3086,6 +3093,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3086
3093
|
};` : ""}
|
|
3087
3094
|
orderBy?: string | string[];
|
|
3088
3095
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3096
|
+
distinctOn?: string | string[];
|
|
3089
3097
|
}): Promise<PaginatedResponse<Partial<Select${Type}>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3090
3098
|
/**
|
|
3091
3099
|
* List ${table.name} records with pagination and filtering
|
|
@@ -3111,6 +3119,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3111
3119
|
};` : ""}
|
|
3112
3120
|
orderBy?: string | string[];
|
|
3113
3121
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3122
|
+
distinctOn?: string | string[];
|
|
3114
3123
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3115
3124
|
async list(params?: {
|
|
3116
3125
|
include?: ${Type}IncludeSpec;
|
|
@@ -3127,6 +3136,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3127
3136
|
};` : ""}
|
|
3128
3137
|
orderBy?: string | string[];
|
|
3129
3138
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3139
|
+
distinctOn?: string | string[];
|
|
3130
3140
|
}): Promise<PaginatedResponse<Select${Type} | Partial<Select${Type}>>> {
|
|
3131
3141
|
return this.post<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
3132
3142
|
}`}
|
|
@@ -5257,6 +5267,7 @@ export async function listRecords(
|
|
|
5257
5267
|
include?: any;
|
|
5258
5268
|
orderBy?: string | string[];
|
|
5259
5269
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
5270
|
+
distinctOn?: string | string[];
|
|
5260
5271
|
vector?: {
|
|
5261
5272
|
field: string;
|
|
5262
5273
|
query: number[];
|
|
@@ -5266,7 +5277,11 @@ export async function listRecords(
|
|
|
5266
5277
|
}
|
|
5267
5278
|
): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
5268
5279
|
try {
|
|
5269
|
-
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector } = params;
|
|
5280
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, distinctOn } = params;
|
|
5281
|
+
|
|
5282
|
+
// DISTINCT ON support
|
|
5283
|
+
const distinctCols: string[] | null = distinctOn ? (Array.isArray(distinctOn) ? distinctOn : [distinctOn]) : null;
|
|
5284
|
+
const _distinctOnColsSQL = distinctCols ? distinctCols.map(c => '"' + c + '"').join(', ') : '';
|
|
5270
5285
|
|
|
5271
5286
|
// Get distance operator if vector search
|
|
5272
5287
|
const distanceOp = vector ? getVectorDistanceOperator(vector.metric) : "";
|
|
@@ -5325,26 +5340,49 @@ export async function listRecords(
|
|
|
5325
5340
|
}
|
|
5326
5341
|
|
|
5327
5342
|
// Build SELECT clause
|
|
5343
|
+
const _distinctOnPrefix = distinctCols ? 'DISTINCT ON (' + _distinctOnColsSQL + ') ' : '';
|
|
5328
5344
|
const baseColumns = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
5329
5345
|
const selectClause = vector
|
|
5330
|
-
? \`\${baseColumns}, ("\${vector.field}" \${distanceOp} ($1)::vector) AS _distance\`
|
|
5331
|
-
: baseColumns
|
|
5346
|
+
? \`\${_distinctOnPrefix}\${baseColumns}, ("\${vector.field}" \${distanceOp} ($1)::vector) AS _distance\`
|
|
5347
|
+
: \`\${_distinctOnPrefix}\${baseColumns}\`;
|
|
5332
5348
|
|
|
5333
5349
|
// Build ORDER BY clause
|
|
5334
5350
|
let orderBySQL = "";
|
|
5335
5351
|
if (vector) {
|
|
5336
5352
|
// For vector search, always order by distance
|
|
5337
5353
|
orderBySQL = \`ORDER BY "\${vector.field}" \${distanceOp} ($1)::vector\`;
|
|
5338
|
-
} else
|
|
5339
|
-
const
|
|
5340
|
-
const
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5354
|
+
} else {
|
|
5355
|
+
const userCols = orderBy ? (Array.isArray(orderBy) ? orderBy : [orderBy]) : [];
|
|
5356
|
+
const userDirs: ("asc" | "desc")[] = orderBy
|
|
5357
|
+
? (Array.isArray(order) ? order : (order ? Array(userCols.length).fill(order) : Array(userCols.length).fill("asc")))
|
|
5358
|
+
: [];
|
|
5359
|
+
|
|
5360
|
+
const finalCols: string[] = [];
|
|
5361
|
+
const finalDirs: string[] = [];
|
|
5362
|
+
|
|
5363
|
+
if (distinctCols) {
|
|
5364
|
+
// DISTINCT ON requires its columns to be the leftmost ORDER BY prefix
|
|
5365
|
+
for (const col of distinctCols) {
|
|
5366
|
+
const userIdx = userCols.indexOf(col);
|
|
5367
|
+
finalCols.push(col);
|
|
5368
|
+
finalDirs.push(userIdx >= 0 ? (userDirs[userIdx] || "asc") : "asc");
|
|
5369
|
+
}
|
|
5370
|
+
// Append remaining user-specified cols not already covered by distinctOn
|
|
5371
|
+
for (let i = 0; i < userCols.length; i++) {
|
|
5372
|
+
if (!distinctCols.includes(userCols[i]!)) {
|
|
5373
|
+
finalCols.push(userCols[i]!);
|
|
5374
|
+
finalDirs.push(userDirs[i] || "asc");
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
} else {
|
|
5378
|
+
finalCols.push(...userCols);
|
|
5379
|
+
finalDirs.push(...userDirs.map(d => d || "asc"));
|
|
5380
|
+
}
|
|
5346
5381
|
|
|
5347
|
-
|
|
5382
|
+
if (finalCols.length > 0) {
|
|
5383
|
+
const orderParts = finalCols.map((c, i) => \`"\${c}" \${finalDirs[i]!.toUpperCase()}\`);
|
|
5384
|
+
orderBySQL = \`ORDER BY \${orderParts.join(", ")}\`;
|
|
5385
|
+
}
|
|
5348
5386
|
}
|
|
5349
5387
|
|
|
5350
5388
|
// Add limit and offset params
|
|
@@ -5353,7 +5391,9 @@ export async function listRecords(
|
|
|
5353
5391
|
const allParams = [...queryParams, ...whereParams, limit, offset];
|
|
5354
5392
|
|
|
5355
5393
|
// Get total count for pagination
|
|
5356
|
-
const countText =
|
|
5394
|
+
const countText = distinctCols
|
|
5395
|
+
? \`SELECT COUNT(*) FROM (SELECT DISTINCT ON (\${_distinctOnColsSQL}) 1 FROM "\${ctx.table}" \${countWhereSQL} ORDER BY \${_distinctOnColsSQL}) __distinct_count\`
|
|
5396
|
+
: \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
5357
5397
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
5358
5398
|
const countResult = await ctx.pg.query(countText, countParams);
|
|
5359
5399
|
const total = parseInt(countResult.rows[0].count, 10);
|