graphile-search 1.5.2 → 1.5.4

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.
@@ -9,13 +9,20 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createPgvectorAdapter = createPgvectorAdapter;
11
11
  /**
12
- * pgvector distance operators.
12
+ * Build a distance expression for the given metric.
13
+ * Uses explicit SQL template literals for each operator to avoid sql.raw.
13
14
  */
14
- const METRIC_OPERATORS = {
15
- COSINE: '<=>',
16
- L2: '<->',
17
- IP: '<#>',
18
- };
15
+ function buildDistanceExpr(sql, columnExpr, vectorExpr, metric) {
16
+ switch (metric) {
17
+ case 'L2':
18
+ return sql `(${columnExpr} <-> ${vectorExpr})`;
19
+ case 'IP':
20
+ return sql `(${columnExpr} <#> ${vectorExpr})`;
21
+ case 'COSINE':
22
+ default:
23
+ return sql `(${columnExpr} <=> ${vectorExpr})`;
24
+ }
25
+ }
19
26
  function isVectorCodec(codec) {
20
27
  return codec?.name === 'vector';
21
28
  }
@@ -171,7 +178,6 @@ function createPgvectorAdapter(options = {}) {
171
178
  if (!vector || !Array.isArray(vector) || vector.length === 0)
172
179
  return null;
173
180
  const resolvedMetric = metric || defaultMetric;
174
- const operator = METRIC_OPERATORS[resolvedMetric] || METRIC_OPERATORS.COSINE;
175
181
  const vectorString = `[${vector.join(',')}]`;
176
182
  const vectorExpr = sql `${sql.value(vectorString)}::vector`;
177
183
  // Check if this column has chunks info and chunk querying is requested
@@ -190,14 +196,15 @@ function createPgvectorAdapter(options = {}) {
190
196
  // Alias to avoid ambiguity when the chunks table name might collide
191
197
  const chunksAlias = sql.identifier('__chunks');
192
198
  // Subquery: SELECT MIN(distance) FROM chunks WHERE chunks.parent_fk = parent.pk
199
+ const chunkDistanceExpr = buildDistanceExpr(sql, sql `${chunksAlias}.${chunkEmbedding}`, vectorExpr, resolvedMetric);
193
200
  const chunkDistanceSubquery = sql `(
194
- SELECT MIN(${chunksAlias}.${chunkEmbedding} ${sql.raw(operator)} ${vectorExpr})
201
+ SELECT MIN(${chunkDistanceExpr})
195
202
  FROM ${chunksTableRef} AS ${chunksAlias}
196
203
  WHERE ${chunksAlias}.${parentFk} = ${parentId}
197
204
  )`;
198
205
  // Also compute direct parent distance if the parent has an embedding
199
206
  const parentColumnExpr = sql `${alias}.${sql.identifier(column.attributeName)}`;
200
- const parentDistanceExpr = sql `(${parentColumnExpr} ${sql.raw(operator)} ${vectorExpr})`;
207
+ const parentDistanceExpr = buildDistanceExpr(sql, parentColumnExpr, vectorExpr, resolvedMetric);
201
208
  // Use LEAST of parent distance and closest chunk distance
202
209
  // COALESCE handles cases where parent or chunks may not have embeddings
203
210
  const combinedDistanceExpr = sql `LEAST(
@@ -215,7 +222,7 @@ function createPgvectorAdapter(options = {}) {
215
222
  }
216
223
  // Standard (non-chunk) query
217
224
  const columnExpr = sql `${alias}.${sql.identifier(column.attributeName)}`;
218
- const distanceExpr = sql `(${columnExpr} ${sql.raw(operator)} ${vectorExpr})`;
225
+ const distanceExpr = buildDistanceExpr(sql, columnExpr, vectorExpr, resolvedMetric);
219
226
  let whereClause = null;
220
227
  if (distance !== undefined && distance !== null) {
221
228
  whereClause = sql `${distanceExpr} <= ${sql.value(distance)}`;
@@ -6,13 +6,20 @@
6
6
  * Wraps the same SQL logic as graphile-pgvector but as a SearchAdapter.
7
7
  */
8
8
  /**
9
- * pgvector distance operators.
9
+ * Build a distance expression for the given metric.
10
+ * Uses explicit SQL template literals for each operator to avoid sql.raw.
10
11
  */
11
- const METRIC_OPERATORS = {
12
- COSINE: '<=>',
13
- L2: '<->',
14
- IP: '<#>',
15
- };
12
+ function buildDistanceExpr(sql, columnExpr, vectorExpr, metric) {
13
+ switch (metric) {
14
+ case 'L2':
15
+ return sql `(${columnExpr} <-> ${vectorExpr})`;
16
+ case 'IP':
17
+ return sql `(${columnExpr} <#> ${vectorExpr})`;
18
+ case 'COSINE':
19
+ default:
20
+ return sql `(${columnExpr} <=> ${vectorExpr})`;
21
+ }
22
+ }
16
23
  function isVectorCodec(codec) {
17
24
  return codec?.name === 'vector';
18
25
  }
@@ -168,7 +175,6 @@ export function createPgvectorAdapter(options = {}) {
168
175
  if (!vector || !Array.isArray(vector) || vector.length === 0)
169
176
  return null;
170
177
  const resolvedMetric = metric || defaultMetric;
171
- const operator = METRIC_OPERATORS[resolvedMetric] || METRIC_OPERATORS.COSINE;
172
178
  const vectorString = `[${vector.join(',')}]`;
173
179
  const vectorExpr = sql `${sql.value(vectorString)}::vector`;
174
180
  // Check if this column has chunks info and chunk querying is requested
@@ -187,14 +193,15 @@ export function createPgvectorAdapter(options = {}) {
187
193
  // Alias to avoid ambiguity when the chunks table name might collide
188
194
  const chunksAlias = sql.identifier('__chunks');
189
195
  // Subquery: SELECT MIN(distance) FROM chunks WHERE chunks.parent_fk = parent.pk
196
+ const chunkDistanceExpr = buildDistanceExpr(sql, sql `${chunksAlias}.${chunkEmbedding}`, vectorExpr, resolvedMetric);
190
197
  const chunkDistanceSubquery = sql `(
191
- SELECT MIN(${chunksAlias}.${chunkEmbedding} ${sql.raw(operator)} ${vectorExpr})
198
+ SELECT MIN(${chunkDistanceExpr})
192
199
  FROM ${chunksTableRef} AS ${chunksAlias}
193
200
  WHERE ${chunksAlias}.${parentFk} = ${parentId}
194
201
  )`;
195
202
  // Also compute direct parent distance if the parent has an embedding
196
203
  const parentColumnExpr = sql `${alias}.${sql.identifier(column.attributeName)}`;
197
- const parentDistanceExpr = sql `(${parentColumnExpr} ${sql.raw(operator)} ${vectorExpr})`;
204
+ const parentDistanceExpr = buildDistanceExpr(sql, parentColumnExpr, vectorExpr, resolvedMetric);
198
205
  // Use LEAST of parent distance and closest chunk distance
199
206
  // COALESCE handles cases where parent or chunks may not have embeddings
200
207
  const combinedDistanceExpr = sql `LEAST(
@@ -212,7 +219,7 @@ export function createPgvectorAdapter(options = {}) {
212
219
  }
213
220
  // Standard (non-chunk) query
214
221
  const columnExpr = sql `${alias}.${sql.identifier(column.attributeName)}`;
215
- const distanceExpr = sql `(${columnExpr} ${sql.raw(operator)} ${vectorExpr})`;
222
+ const distanceExpr = buildDistanceExpr(sql, columnExpr, vectorExpr, resolvedMetric);
216
223
  let whereClause = null;
217
224
  if (distance !== undefined && distance !== null) {
218
225
  whereClause = sql `${distanceExpr} <= ${sql.value(distance)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-search",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Unified PostGraphile v5 search plugin — abstracts tsvector, BM25, pg_trgm, and pgvector behind a single adapter-based architecture with composite searchScore",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "homepage": "https://github.com/constructive-io/constructive",
@@ -31,20 +31,20 @@
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.19.11",
33
33
  "@types/pg": "^8.18.0",
34
- "graphile-connection-filter": "^1.3.2",
35
- "graphile-test": "^4.7.2",
36
- "makage": "^0.1.10",
34
+ "graphile-connection-filter": "^1.3.4",
35
+ "graphile-test": "^4.7.4",
36
+ "makage": "^0.3.0",
37
37
  "pg": "^8.20.0",
38
- "pgsql-test": "^4.7.2"
38
+ "pgsql-test": "^4.7.4"
39
39
  },
40
40
  "peerDependencies": {
41
- "@dataplan/pg": "1.0.0-rc.8",
42
- "graphile-build": "5.0.0-rc.6",
43
- "graphile-build-pg": "5.0.0-rc.8",
44
- "graphile-config": "1.0.0-rc.6",
41
+ "@dataplan/pg": "1.0.0",
42
+ "graphile-build": "5.0.0",
43
+ "graphile-build-pg": "5.0.0",
44
+ "graphile-config": "1.0.0",
45
45
  "graphql": "16.13.0",
46
- "pg-sql2": "5.0.0-rc.5",
47
- "postgraphile": "5.0.0-rc.10"
46
+ "pg-sql2": "5.0.0",
47
+ "postgraphile": "5.0.0"
48
48
  },
49
49
  "keywords": [
50
50
  "postgraphile",
@@ -62,5 +62,5 @@
62
62
  "hybrid-search",
63
63
  "searchScore"
64
64
  ],
65
- "gitHead": "93deca687ce109bf90d2a0a73ea6b05b22724160"
65
+ "gitHead": "2f6e083d11764a04b2a6bc14df5b2ca2725defc6"
66
66
  }