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 +21 -0
- package/dist/cli.js +74 -31
- package/dist/index.js +74 -31
- package/package.json +1 -1
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.
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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
|
|
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
|
|
6310
|
+
function trigramFnRaw(fieldExpr: string, metric?: TrigramMetric): string {
|
|
6287
6311
|
switch (metric) {
|
|
6288
|
-
case "wordSimilarity":
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
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
|
|
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.
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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?: {
|
|
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
|
|
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
|
|
5349
|
+
function trigramFnRaw(fieldExpr: string, metric?: TrigramMetric): string {
|
|
5326
5350
|
switch (metric) {
|
|
5327
|
-
case "wordSimilarity":
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
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
|
|
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
|