postgresdk 0.18.20 → 0.18.21

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/README.md CHANGED
@@ -960,6 +960,27 @@ const results = await sdk.books.list({
960
960
  });
961
961
  ```
962
962
 
963
+ **Multi-field trigram search:**
964
+ ```typescript
965
+ // Greatest strategy (default): score = GREATEST(sim(name), sim(url))
966
+ const results = await sdk.websites.list({
967
+ trigram: { fields: ["name", "url"], query: "google", strategy: "greatest" }
968
+ });
969
+
970
+ // Concat strategy: concatenate fields before scoring ("name url")
971
+ const results = await sdk.websites.list({
972
+ trigram: { fields: ["name", "url"], query: "google", strategy: "concat" }
973
+ });
974
+
975
+ // Weighted strategy: weighted average of per-field scores
976
+ const results = await sdk.websites.list({
977
+ trigram: {
978
+ fields: [{ field: "name", weight: 2 }, { field: "url", weight: 1 }],
979
+ query: "google"
980
+ }
981
+ });
982
+ ```
983
+
963
984
  **Note:** `trigram` and `vector` are mutually exclusive on a single `list()` call.
964
985
 
965
986
  See the generated SDK documentation for all available operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$like`, `$ilike`, `$similarity`, `$wordSimilarity`, `$strictWordSimilarity`, `$is`, `$isNot`, `$or`, `$and`, `$jsonbContains`, `$jsonbContainedBy`, `$jsonbHasKey`, `$jsonbHasAnyKeys`, `$jsonbHasAllKeys`, `$jsonbPath`.
package/dist/cli.js CHANGED
@@ -3228,6 +3228,7 @@ function emitHonoRoutes(table, _graph, opts) {
3228
3228
  const fileTableName = table.name;
3229
3229
  const Type = pascal(table.name);
3230
3230
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
3231
+ const trigramMetricZod = `z.enum(["similarity", "wordSimilarity", "strictWordSimilarity"]).optional()`;
3231
3232
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
3232
3233
  const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
3233
3234
  const rawPk = table.pk;
@@ -3294,12 +3295,27 @@ const listSchema = z.object({
3294
3295
  metric: z.enum(["cosine", "l2", "inner"]).optional(),
3295
3296
  maxDistance: z.number().optional()
3296
3297
  }).optional(),` : ""}
3297
- trigram: z.object({
3298
- field: z.string(),
3299
- query: z.string(),
3300
- metric: z.enum(["similarity", "wordSimilarity", "strictWordSimilarity"]).optional(),
3301
- threshold: z.number().min(0).max(1).optional()
3302
- }).optional()
3298
+ trigram: z.union([
3299
+ z.object({
3300
+ field: z.string(),
3301
+ query: z.string(),
3302
+ metric: ${trigramMetricZod},
3303
+ threshold: z.number().min(0).max(1).optional()
3304
+ }),
3305
+ z.object({
3306
+ fields: z.array(z.string()).min(1),
3307
+ strategy: z.enum(["greatest", "concat"]).optional(),
3308
+ query: z.string(),
3309
+ metric: ${trigramMetricZod},
3310
+ threshold: z.number().min(0).max(1).optional()
3311
+ }),
3312
+ z.object({
3313
+ fields: z.array(z.object({ field: z.string(), weight: z.number().positive() })).min(1),
3314
+ query: z.string(),
3315
+ metric: ${trigramMetricZod},
3316
+ threshold: z.number().min(0).max(1).optional()
3317
+ })
3318
+ ]).optional()
3303
3319
  }).strict().refine(
3304
3320
  (data) => !(data.select && data.exclude),
3305
3321
  { message: "Cannot specify both 'select' and 'exclude' parameters" }
@@ -3567,6 +3583,7 @@ function analyzeIncludeSpec(includeSpec) {
3567
3583
  function emitClient(table, graph, opts, model) {
3568
3584
  const Type = pascal(table.name);
3569
3585
  const ext = opts.useJsExtensions ? ".js" : "";
3586
+ const trigramParamType = `{ field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number } | { fields: string[]; strategy?: "greatest" | "concat"; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number } | { fields: Array<{ field: string; weight: number }>; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number }`;
3570
3587
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
3571
3588
  const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
3572
3589
  if (process.env.SDK_DEBUG) {
@@ -3970,7 +3987,7 @@ ${hasJsonbColumns ? ` /**
3970
3987
  metric?: "cosine" | "l2" | "inner";
3971
3988
  maxDistance?: number;
3972
3989
  };` : ""}
3973
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3990
+ trigram?: ${trigramParamType};
3974
3991
  orderBy?: string | string[];
3975
3992
  order?: "asc" | "desc" | ("asc" | "desc")[];
3976
3993
  distinctOn?: string | string[];
@@ -3992,7 +4009,7 @@ ${hasJsonbColumns ? ` /**
3992
4009
  metric?: "cosine" | "l2" | "inner";
3993
4010
  maxDistance?: number;
3994
4011
  };` : ""}
3995
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4012
+ trigram?: ${trigramParamType};
3996
4013
  orderBy?: string | string[];
3997
4014
  order?: "asc" | "desc" | ("asc" | "desc")[];
3998
4015
  distinctOn?: string | string[];
@@ -4025,7 +4042,7 @@ ${hasJsonbColumns ? ` /**
4025
4042
  metric?: "cosine" | "l2" | "inner";
4026
4043
  maxDistance?: number;
4027
4044
  };` : ""}
4028
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4045
+ trigram?: ${trigramParamType};
4029
4046
  orderBy?: string | string[];
4030
4047
  order?: "asc" | "desc" | ("asc" | "desc")[];
4031
4048
  distinctOn?: string | string[];
@@ -4043,7 +4060,7 @@ ${hasJsonbColumns ? ` /**
4043
4060
  metric?: "cosine" | "l2" | "inner";
4044
4061
  maxDistance?: number;
4045
4062
  };` : ""}
4046
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4063
+ trigram?: ${trigramParamType};
4047
4064
  orderBy?: string | string[];
4048
4065
  order?: "asc" | "desc" | ("asc" | "desc")[];
4049
4066
  distinctOn?: string | string[];
@@ -4066,7 +4083,7 @@ ${hasJsonbColumns ? ` /**
4066
4083
  metric?: "cosine" | "l2" | "inner";
4067
4084
  maxDistance?: number;
4068
4085
  };` : ""}
4069
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4086
+ trigram?: ${trigramParamType};
4070
4087
  orderBy?: string | string[];
4071
4088
  order?: "asc" | "desc" | ("asc" | "desc")[];
4072
4089
  distinctOn?: string | string[];
@@ -4088,7 +4105,7 @@ ${hasJsonbColumns ? ` /**
4088
4105
  metric?: "cosine" | "l2" | "inner";
4089
4106
  maxDistance?: number;
4090
4107
  };` : ""}
4091
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4108
+ trigram?: ${trigramParamType};
4092
4109
  orderBy?: string | string[];
4093
4110
  order?: "asc" | "desc" | ("asc" | "desc")[];
4094
4111
  distinctOn?: string | string[];
@@ -4115,7 +4132,7 @@ ${hasJsonbColumns ? ` /**
4115
4132
  metric?: "cosine" | "l2" | "inner";
4116
4133
  maxDistance?: number;
4117
4134
  };` : ""}
4118
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4135
+ trigram?: ${trigramParamType};
4119
4136
  orderBy?: string | string[];
4120
4137
  order?: "asc" | "desc" | ("asc" | "desc")[];
4121
4138
  distinctOn?: string | string[];
@@ -4133,7 +4150,7 @@ ${hasJsonbColumns ? ` /**
4133
4150
  metric?: "cosine" | "l2" | "inner";
4134
4151
  maxDistance?: number;
4135
4152
  };` : ""}
4136
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
4153
+ trigram?: ${trigramParamType};
4137
4154
  orderBy?: string | string[];
4138
4155
  order?: "asc" | "desc" | ("asc" | "desc")[];
4139
4156
  distinctOn?: string | string[];
@@ -5928,7 +5945,7 @@ export async function createRecord(
5928
5945
 
5929
5946
  const preparedVals = vals.map((v, i) =>
5930
5947
  v !== null && v !== undefined && typeof v === 'object' &&
5931
- (ctx.jsonbColumns?.includes(cols[i]) || ctx.vectorColumns?.includes(cols[i]))
5948
+ (ctx.jsonbColumns?.includes(cols[i]!) || ctx.vectorColumns?.includes(cols[i]!))
5932
5949
  ? JSON.stringify(v)
5933
5950
  : v
5934
5951
  );
@@ -6279,20 +6296,51 @@ function getVectorDistanceOperator(metric?: string): string {
6279
6296
  }
6280
6297
  }
6281
6298
 
6299
+ export type TrigramMetric = "similarity" | "wordSimilarity" | "strictWordSimilarity";
6300
+
6301
+ export type TrigramParam =
6302
+ | { field: string; query: string; metric?: TrigramMetric; threshold?: number }
6303
+ | { fields: string[]; strategy?: "greatest" | "concat"; query: string; metric?: TrigramMetric; threshold?: number }
6304
+ | { fields: Array<{ field: string; weight: number }>; query: string; metric?: TrigramMetric; threshold?: number };
6305
+
6282
6306
  /**
6283
- * Build the pg_trgm SQL function expression for a given field and metric.
6307
+ * Build a pg_trgm function expression for a raw SQL field expression.
6284
6308
  * $1 is always the query string parameter.
6285
6309
  */
6286
- function getTrigramFnExpr(field: string, metric?: string): string {
6310
+ function trigramFnRaw(fieldExpr: string, metric?: TrigramMetric): string {
6287
6311
  switch (metric) {
6288
- case "wordSimilarity":
6289
- return \`word_similarity($1, "\${field}")\`;
6290
- case "strictWordSimilarity":
6291
- return \`strict_word_similarity($1, "\${field}")\`;
6292
- case "similarity":
6293
- default:
6294
- return \`similarity("\${field}", $1)\`;
6312
+ case "wordSimilarity": return \`word_similarity($1, \${fieldExpr})\`;
6313
+ case "strictWordSimilarity": return \`strict_word_similarity($1, \${fieldExpr})\`;
6314
+ default: return \`similarity(\${fieldExpr}, $1)\`;
6315
+ }
6316
+ }
6317
+
6318
+ /** Build the full pg_trgm SQL expression for the given trigram param. */
6319
+ function getTrigramFnExpr(trigram: TrigramParam): string {
6320
+ if ("field" in trigram) {
6321
+ return trigramFnRaw(\`"\${trigram.field}"\`, trigram.metric);
6322
+ }
6323
+
6324
+ const { fields, metric } = trigram;
6325
+
6326
+ // Weighted: fields is Array<{ field, weight }>
6327
+ if (fields.length > 0 && typeof fields[0] === "object" && "weight" in fields[0]) {
6328
+ const wfields = fields as Array<{ field: string; weight: number }>;
6329
+ const totalWeight = wfields.reduce((sum, f) => sum + f.weight, 0);
6330
+ const terms = wfields.map(f => \`\${trigramFnRaw(\`"\${f.field}"\`, metric)} * \${f.weight}\`).join(" + ");
6331
+ return \`(\${terms}) / \${totalWeight}\`;
6332
+ }
6333
+
6334
+ const sfields = fields as string[];
6335
+ const strategy = (trigram as Extract<TrigramParam, { fields: string[] }>).strategy ?? "greatest";
6336
+
6337
+ if (strategy === "concat") {
6338
+ const concatExpr = sfields.map(f => \`"\${f}"\`).join(\` || ' ' || \`);
6339
+ return trigramFnRaw(concatExpr, metric);
6295
6340
  }
6341
+
6342
+ // greatest (default)
6343
+ return \`GREATEST(\${sfields.map(f => trigramFnRaw(\`"\${f}"\`, metric)).join(", ")})\`;
6296
6344
  }
6297
6345
 
6298
6346
  /** Builds a SQL ORDER BY clause from parallel cols/dirs arrays. Returns "" when cols is empty. */
@@ -6320,12 +6368,7 @@ export async function listRecords(
6320
6368
  metric?: "cosine" | "l2" | "inner";
6321
6369
  maxDistance?: number;
6322
6370
  };
6323
- trigram?: {
6324
- field: string;
6325
- query: string;
6326
- metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity";
6327
- threshold?: number;
6328
- };
6371
+ trigram?: TrigramParam;
6329
6372
  }
6330
6373
  ): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
6331
6374
  try {
@@ -6351,7 +6394,7 @@ export async function listRecords(
6351
6394
  const distanceOp = vector ? getVectorDistanceOperator(vector.metric) : "";
6352
6395
 
6353
6396
  // Get trigram similarity SQL expression (pg_trgm). $1 is always the query string.
6354
- const trigramFnExpr = trigram ? getTrigramFnExpr(trigram.field, trigram.metric) : "";
6397
+ const trigramFnExpr = trigram ? getTrigramFnExpr(trigram) : "";
6355
6398
 
6356
6399
  // Add vector or trigram query as $1 if present (mutually exclusive)
6357
6400
  const queryParams: any[] = vector
package/dist/index.js CHANGED
@@ -2267,6 +2267,7 @@ function emitHonoRoutes(table, _graph, opts) {
2267
2267
  const fileTableName = table.name;
2268
2268
  const Type = pascal(table.name);
2269
2269
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
2270
+ const trigramMetricZod = `z.enum(["similarity", "wordSimilarity", "strictWordSimilarity"]).optional()`;
2270
2271
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
2271
2272
  const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
2272
2273
  const rawPk = table.pk;
@@ -2333,12 +2334,27 @@ const listSchema = z.object({
2333
2334
  metric: z.enum(["cosine", "l2", "inner"]).optional(),
2334
2335
  maxDistance: z.number().optional()
2335
2336
  }).optional(),` : ""}
2336
- trigram: z.object({
2337
- field: z.string(),
2338
- query: z.string(),
2339
- metric: z.enum(["similarity", "wordSimilarity", "strictWordSimilarity"]).optional(),
2340
- threshold: z.number().min(0).max(1).optional()
2341
- }).optional()
2337
+ trigram: z.union([
2338
+ z.object({
2339
+ field: z.string(),
2340
+ query: z.string(),
2341
+ metric: ${trigramMetricZod},
2342
+ threshold: z.number().min(0).max(1).optional()
2343
+ }),
2344
+ z.object({
2345
+ fields: z.array(z.string()).min(1),
2346
+ strategy: z.enum(["greatest", "concat"]).optional(),
2347
+ query: z.string(),
2348
+ metric: ${trigramMetricZod},
2349
+ threshold: z.number().min(0).max(1).optional()
2350
+ }),
2351
+ z.object({
2352
+ fields: z.array(z.object({ field: z.string(), weight: z.number().positive() })).min(1),
2353
+ query: z.string(),
2354
+ metric: ${trigramMetricZod},
2355
+ threshold: z.number().min(0).max(1).optional()
2356
+ })
2357
+ ]).optional()
2342
2358
  }).strict().refine(
2343
2359
  (data) => !(data.select && data.exclude),
2344
2360
  { message: "Cannot specify both 'select' and 'exclude' parameters" }
@@ -2606,6 +2622,7 @@ function analyzeIncludeSpec(includeSpec) {
2606
2622
  function emitClient(table, graph, opts, model) {
2607
2623
  const Type = pascal(table.name);
2608
2624
  const ext = opts.useJsExtensions ? ".js" : "";
2625
+ const trigramParamType = `{ field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number } | { fields: string[]; strategy?: "greatest" | "concat"; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number } | { fields: Array<{ field: string; weight: number }>; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number }`;
2609
2626
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
2610
2627
  const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
2611
2628
  if (process.env.SDK_DEBUG) {
@@ -3009,7 +3026,7 @@ ${hasJsonbColumns ? ` /**
3009
3026
  metric?: "cosine" | "l2" | "inner";
3010
3027
  maxDistance?: number;
3011
3028
  };` : ""}
3012
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3029
+ trigram?: ${trigramParamType};
3013
3030
  orderBy?: string | string[];
3014
3031
  order?: "asc" | "desc" | ("asc" | "desc")[];
3015
3032
  distinctOn?: string | string[];
@@ -3031,7 +3048,7 @@ ${hasJsonbColumns ? ` /**
3031
3048
  metric?: "cosine" | "l2" | "inner";
3032
3049
  maxDistance?: number;
3033
3050
  };` : ""}
3034
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3051
+ trigram?: ${trigramParamType};
3035
3052
  orderBy?: string | string[];
3036
3053
  order?: "asc" | "desc" | ("asc" | "desc")[];
3037
3054
  distinctOn?: string | string[];
@@ -3064,7 +3081,7 @@ ${hasJsonbColumns ? ` /**
3064
3081
  metric?: "cosine" | "l2" | "inner";
3065
3082
  maxDistance?: number;
3066
3083
  };` : ""}
3067
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3084
+ trigram?: ${trigramParamType};
3068
3085
  orderBy?: string | string[];
3069
3086
  order?: "asc" | "desc" | ("asc" | "desc")[];
3070
3087
  distinctOn?: string | string[];
@@ -3082,7 +3099,7 @@ ${hasJsonbColumns ? ` /**
3082
3099
  metric?: "cosine" | "l2" | "inner";
3083
3100
  maxDistance?: number;
3084
3101
  };` : ""}
3085
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3102
+ trigram?: ${trigramParamType};
3086
3103
  orderBy?: string | string[];
3087
3104
  order?: "asc" | "desc" | ("asc" | "desc")[];
3088
3105
  distinctOn?: string | string[];
@@ -3105,7 +3122,7 @@ ${hasJsonbColumns ? ` /**
3105
3122
  metric?: "cosine" | "l2" | "inner";
3106
3123
  maxDistance?: number;
3107
3124
  };` : ""}
3108
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3125
+ trigram?: ${trigramParamType};
3109
3126
  orderBy?: string | string[];
3110
3127
  order?: "asc" | "desc" | ("asc" | "desc")[];
3111
3128
  distinctOn?: string | string[];
@@ -3127,7 +3144,7 @@ ${hasJsonbColumns ? ` /**
3127
3144
  metric?: "cosine" | "l2" | "inner";
3128
3145
  maxDistance?: number;
3129
3146
  };` : ""}
3130
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3147
+ trigram?: ${trigramParamType};
3131
3148
  orderBy?: string | string[];
3132
3149
  order?: "asc" | "desc" | ("asc" | "desc")[];
3133
3150
  distinctOn?: string | string[];
@@ -3154,7 +3171,7 @@ ${hasJsonbColumns ? ` /**
3154
3171
  metric?: "cosine" | "l2" | "inner";
3155
3172
  maxDistance?: number;
3156
3173
  };` : ""}
3157
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3174
+ trigram?: ${trigramParamType};
3158
3175
  orderBy?: string | string[];
3159
3176
  order?: "asc" | "desc" | ("asc" | "desc")[];
3160
3177
  distinctOn?: string | string[];
@@ -3172,7 +3189,7 @@ ${hasJsonbColumns ? ` /**
3172
3189
  metric?: "cosine" | "l2" | "inner";
3173
3190
  maxDistance?: number;
3174
3191
  };` : ""}
3175
- trigram?: { field: string; query: string; metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity"; threshold?: number };
3192
+ trigram?: ${trigramParamType};
3176
3193
  orderBy?: string | string[];
3177
3194
  order?: "asc" | "desc" | ("asc" | "desc")[];
3178
3195
  distinctOn?: string | string[];
@@ -4967,7 +4984,7 @@ export async function createRecord(
4967
4984
 
4968
4985
  const preparedVals = vals.map((v, i) =>
4969
4986
  v !== null && v !== undefined && typeof v === 'object' &&
4970
- (ctx.jsonbColumns?.includes(cols[i]) || ctx.vectorColumns?.includes(cols[i]))
4987
+ (ctx.jsonbColumns?.includes(cols[i]!) || ctx.vectorColumns?.includes(cols[i]!))
4971
4988
  ? JSON.stringify(v)
4972
4989
  : v
4973
4990
  );
@@ -5318,20 +5335,51 @@ function getVectorDistanceOperator(metric?: string): string {
5318
5335
  }
5319
5336
  }
5320
5337
 
5338
+ export type TrigramMetric = "similarity" | "wordSimilarity" | "strictWordSimilarity";
5339
+
5340
+ export type TrigramParam =
5341
+ | { field: string; query: string; metric?: TrigramMetric; threshold?: number }
5342
+ | { fields: string[]; strategy?: "greatest" | "concat"; query: string; metric?: TrigramMetric; threshold?: number }
5343
+ | { fields: Array<{ field: string; weight: number }>; query: string; metric?: TrigramMetric; threshold?: number };
5344
+
5321
5345
  /**
5322
- * Build the pg_trgm SQL function expression for a given field and metric.
5346
+ * Build a pg_trgm function expression for a raw SQL field expression.
5323
5347
  * $1 is always the query string parameter.
5324
5348
  */
5325
- function getTrigramFnExpr(field: string, metric?: string): string {
5349
+ function trigramFnRaw(fieldExpr: string, metric?: TrigramMetric): string {
5326
5350
  switch (metric) {
5327
- case "wordSimilarity":
5328
- return \`word_similarity($1, "\${field}")\`;
5329
- case "strictWordSimilarity":
5330
- return \`strict_word_similarity($1, "\${field}")\`;
5331
- case "similarity":
5332
- default:
5333
- return \`similarity("\${field}", $1)\`;
5351
+ case "wordSimilarity": return \`word_similarity($1, \${fieldExpr})\`;
5352
+ case "strictWordSimilarity": return \`strict_word_similarity($1, \${fieldExpr})\`;
5353
+ default: return \`similarity(\${fieldExpr}, $1)\`;
5354
+ }
5355
+ }
5356
+
5357
+ /** Build the full pg_trgm SQL expression for the given trigram param. */
5358
+ function getTrigramFnExpr(trigram: TrigramParam): string {
5359
+ if ("field" in trigram) {
5360
+ return trigramFnRaw(\`"\${trigram.field}"\`, trigram.metric);
5361
+ }
5362
+
5363
+ const { fields, metric } = trigram;
5364
+
5365
+ // Weighted: fields is Array<{ field, weight }>
5366
+ if (fields.length > 0 && typeof fields[0] === "object" && "weight" in fields[0]) {
5367
+ const wfields = fields as Array<{ field: string; weight: number }>;
5368
+ const totalWeight = wfields.reduce((sum, f) => sum + f.weight, 0);
5369
+ const terms = wfields.map(f => \`\${trigramFnRaw(\`"\${f.field}"\`, metric)} * \${f.weight}\`).join(" + ");
5370
+ return \`(\${terms}) / \${totalWeight}\`;
5371
+ }
5372
+
5373
+ const sfields = fields as string[];
5374
+ const strategy = (trigram as Extract<TrigramParam, { fields: string[] }>).strategy ?? "greatest";
5375
+
5376
+ if (strategy === "concat") {
5377
+ const concatExpr = sfields.map(f => \`"\${f}"\`).join(\` || ' ' || \`);
5378
+ return trigramFnRaw(concatExpr, metric);
5334
5379
  }
5380
+
5381
+ // greatest (default)
5382
+ return \`GREATEST(\${sfields.map(f => trigramFnRaw(\`"\${f}"\`, metric)).join(", ")})\`;
5335
5383
  }
5336
5384
 
5337
5385
  /** Builds a SQL ORDER BY clause from parallel cols/dirs arrays. Returns "" when cols is empty. */
@@ -5359,12 +5407,7 @@ export async function listRecords(
5359
5407
  metric?: "cosine" | "l2" | "inner";
5360
5408
  maxDistance?: number;
5361
5409
  };
5362
- trigram?: {
5363
- field: string;
5364
- query: string;
5365
- metric?: "similarity" | "wordSimilarity" | "strictWordSimilarity";
5366
- threshold?: number;
5367
- };
5410
+ trigram?: TrigramParam;
5368
5411
  }
5369
5412
  ): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
5370
5413
  try {
@@ -5390,7 +5433,7 @@ export async function listRecords(
5390
5433
  const distanceOp = vector ? getVectorDistanceOperator(vector.metric) : "";
5391
5434
 
5392
5435
  // Get trigram similarity SQL expression (pg_trgm). $1 is always the query string.
5393
- const trigramFnExpr = trigram ? getTrigramFnExpr(trigram.field, trigram.metric) : "";
5436
+ const trigramFnExpr = trigram ? getTrigramFnExpr(trigram) : "";
5394
5437
 
5395
5438
  // Add vector or trigram query as $1 if present (mutually exclusive)
5396
5439
  const queryParams: any[] = vector
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.18.20",
3
+ "version": "0.18.21",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {