@atscript/db-postgres 0.1.142 → 0.1.143

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/index.cjs CHANGED
@@ -330,17 +330,6 @@ function buildCreateTable(table, fields, foreignKeys, options) {
330
330
  }
331
331
  return `CREATE TABLE IF NOT EXISTS ${quoteTableName(table)} (${colDefs.join(", ")})`;
332
332
  }
333
- /**
334
- * Offsets numbered placeholders ($1, $2, ...) in a SQL fragment by a given amount.
335
- * Used when WHERE params follow SET params in UPDATE statements.
336
- */
337
- function offsetPlaceholders(fragment, offset) {
338
- if (offset === 0) return fragment;
339
- return {
340
- sql: fragment.sql.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + offset}`),
341
- params: fragment.params
342
- };
343
- }
344
333
  //#endregion
345
334
  //#region src/filter-builder.ts
346
335
  /**
@@ -1354,13 +1343,21 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1354
1343
  for (const index of this._table.indexes.values()) if (index.type === "fulltext") indexes.push({
1355
1344
  name: index.key,
1356
1345
  description: `GIN tsvector index on ${index.fields.map((f) => f.name).join(", ")}`,
1357
- type: "text"
1358
- });
1359
- for (const [field, vec] of this._vectorFields) indexes.push({
1360
- name: vec.indexName,
1361
- description: `vector(${vec.dimensions}) on ${field}, ${vec.similarity}`,
1362
- type: "vector"
1346
+ type: "text",
1347
+ fields: this._indexLogicalPaths(index),
1348
+ isDefault: indexes.length === 0
1363
1349
  });
1350
+ let firstVector = true;
1351
+ for (const [field, vec] of this._vectorFields) {
1352
+ indexes.push({
1353
+ name: vec.indexName,
1354
+ description: `vector(${vec.dimensions}) on ${field}, ${vec.similarity}`,
1355
+ type: "vector",
1356
+ fields: [field],
1357
+ isDefault: firstVector
1358
+ });
1359
+ firstVector = false;
1360
+ }
1364
1361
  return indexes;
1365
1362
  }
1366
1363
  async search(text, query, indexName) {
@@ -1465,7 +1462,7 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1465
1462
  found = true;
1466
1463
  break;
1467
1464
  }
1468
- if (!found) throw new Error(`Vector index "${indexName}" not found`);
1465
+ if (!found) throw new Error((0, _atscript_db.vectorIndexNotFoundMessage)(indexName));
1469
1466
  } else {
1470
1467
  const first = this._vectorFields.entries().next();
1471
1468
  if (first.done) throw new Error("No vector fields defined");
@@ -1487,41 +1484,31 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1487
1484
  vectorStr: vectorToString(vector)
1488
1485
  };
1489
1486
  }
1490
- _buildVectorSearchQuery(vector, query, indexName) {
1491
- const ctx = this._prepareVectorSearch(vector, query, indexName);
1492
- let inner = `SELECT *, (${qi(ctx.field)} ${ctx.distanceOp} $1::vector) AS _distance FROM ${quoteTableName(ctx.tableName)} WHERE ${offsetPlaceholders((0, _atscript_db_sql_tools.finalizeParams)(pgDialect, ctx.where), 1).sql}`;
1493
- const params = [ctx.vectorStr, ...ctx.where.params];
1494
- let sql = `SELECT * FROM (${inner}) _v`;
1495
- if (ctx.threshold !== void 0) {
1496
- sql += ` WHERE _distance <= $${params.length + 1}`;
1497
- params.push(thresholdToDistance(ctx.threshold, ctx.vec.similarity));
1498
- }
1499
- sql += ` ORDER BY _distance ASC`;
1500
- if (ctx.controls.$skip) {
1501
- sql += ` LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
1502
- params.push(ctx.controls.$limit || 1e3, ctx.controls.$skip);
1503
- } else {
1504
- sql += ` LIMIT $${params.length + 1}`;
1505
- params.push(ctx.controls.$limit || 20);
1506
- }
1487
+ /** The row source + distance cap (threshold on pgvector's distance scale) of a vector search. */
1488
+ _vectorSearchSource(ctx, withRows) {
1489
+ const distExpr = {
1490
+ sql: `(${qi(ctx.field)} ${ctx.distanceOp} ?::vector)`,
1491
+ params: [ctx.vectorStr]
1492
+ };
1507
1493
  return {
1508
- sql,
1509
- params
1494
+ source: (0, _atscript_db_sql_tools.vectorDistanceSource)(pgDialect, ctx.tableName, ctx.where, distExpr, withRows),
1495
+ maxDistance: ctx.threshold === void 0 ? void 0 : thresholdToDistance(ctx.threshold, ctx.vec.similarity)
1510
1496
  };
1511
1497
  }
1512
- _buildVectorSearchCountQuery(vector, query, indexName) {
1498
+ _buildVectorSearchQuery(vector, query, indexName) {
1513
1499
  const ctx = this._prepareVectorSearch(vector, query, indexName);
1514
- let inner = `SELECT (${qi(ctx.field)} ${ctx.distanceOp} $1::vector) AS _distance FROM ${quoteTableName(ctx.tableName)} WHERE ${offsetPlaceholders((0, _atscript_db_sql_tools.finalizeParams)(pgDialect, ctx.where), 1).sql}`;
1515
- const params = [ctx.vectorStr, ...ctx.where.params];
1516
- let sql = `SELECT COUNT(*) AS cnt FROM (${inner}) _v`;
1517
- if (ctx.threshold !== void 0) {
1518
- sql += ` WHERE _distance <= $${params.length + 1}`;
1519
- params.push(thresholdToDistance(ctx.threshold, ctx.vec.similarity));
1520
- }
1521
- return {
1522
- sql,
1523
- params
1524
- };
1500
+ const { source, maxDistance } = this._vectorSearchSource(ctx, true);
1501
+ const skip = Number(ctx.controls.$skip) || 0;
1502
+ return (0, _atscript_db_sql_tools.buildVectorSearchSelect)(pgDialect, source, {
1503
+ select: ctx.controls.$select,
1504
+ limit: Number(ctx.controls.$limit) || (skip ? 1e3 : 20),
1505
+ skip,
1506
+ maxDistance
1507
+ });
1508
+ }
1509
+ _buildVectorSearchCountQuery(vector, query, indexName) {
1510
+ const { source, maxDistance } = this._vectorSearchSource(this._prepareVectorSearch(vector, query, indexName), false);
1511
+ return (0, _atscript_db_sql_tools.buildVectorSearchCount)(pgDialect, source, { maxDistance });
1525
1512
  }
1526
1513
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
1527
1514
  _resolveVectorThreshold(controls, indexName) {
@@ -1583,10 +1570,7 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1583
1570
  };
1584
1571
  }
1585
1572
  _buildGeoSearchSelect(ctx) {
1586
- return (0, _atscript_db_sql_tools.buildGeoSearchSelect)(pgDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, {
1587
- $limit: ctx.controls.$limit,
1588
- $skip: ctx.controls.$skip
1589
- });
1573
+ return (0, _atscript_db_sql_tools.buildGeoSearchSelect)(pgDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, ctx.controls);
1590
1574
  }
1591
1575
  };
1592
1576
  /** A text / character type: the value is converted by the assignment into the column. */
package/dist/index.d.cts CHANGED
@@ -265,6 +265,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
265
265
  }>;
266
266
  /** Resolves vector field and computes shared context for vector search SQL builders. */
267
267
  private _prepareVectorSearch;
268
+ /** The row source + distance cap (threshold on pgvector's distance scale) of a vector search. */
269
+ private _vectorSearchSource;
268
270
  private _buildVectorSearchQuery;
269
271
  private _buildVectorSearchCountQuery;
270
272
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
package/dist/index.d.mts CHANGED
@@ -265,6 +265,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
265
265
  }>;
266
266
  /** Resolves vector field and computes shared context for vector search SQL builders. */
267
267
  private _prepareVectorSearch;
268
+ /** The row source + distance cap (threshold on pgvector's distance scale) of a vector search. */
269
+ private _vectorSearchSource;
268
270
  private _buildVectorSearchQuery;
269
271
  private _buildVectorSearchCountQuery;
270
272
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable, isColumnTypeChanged } from "@atscript/db";
1
+ import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable, isColumnTypeChanged, vectorIndexNotFoundMessage } from "@atscript/db";
2
2
  import { resolveAggregateSearch } from "@atscript/db/agg";
3
- import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildSelect, buildUpdate, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, finalizeParams, geoWindowFromControls, insertManyColumns, normalizeGeoPointValue, parseRegexString, quotedJsonPathSegments, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlStringLiteral, sqlTimeZoneLiteral } from "@atscript/db-sql-tools";
3
+ import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildSelect, buildUpdate, buildVectorSearchCount, buildVectorSearchSelect, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, finalizeParams, geoWindowFromControls, insertManyColumns, normalizeGeoPointValue, parseRegexString, quotedJsonPathSegments, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlStringLiteral, sqlTimeZoneLiteral, vectorDistanceSource } from "@atscript/db-sql-tools";
4
4
  import { BUCKET_MAX_INSTANT, BUCKET_MIN_INSTANT } from "@uniqu/core";
5
5
  //#region src/sql-builder.ts
6
6
  /**
@@ -329,17 +329,6 @@ function buildCreateTable(table, fields, foreignKeys, options) {
329
329
  }
330
330
  return `CREATE TABLE IF NOT EXISTS ${quoteTableName(table)} (${colDefs.join(", ")})`;
331
331
  }
332
- /**
333
- * Offsets numbered placeholders ($1, $2, ...) in a SQL fragment by a given amount.
334
- * Used when WHERE params follow SET params in UPDATE statements.
335
- */
336
- function offsetPlaceholders(fragment, offset) {
337
- if (offset === 0) return fragment;
338
- return {
339
- sql: fragment.sql.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + offset}`),
340
- params: fragment.params
341
- };
342
- }
343
332
  //#endregion
344
333
  //#region src/filter-builder.ts
345
334
  /**
@@ -1353,13 +1342,21 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1353
1342
  for (const index of this._table.indexes.values()) if (index.type === "fulltext") indexes.push({
1354
1343
  name: index.key,
1355
1344
  description: `GIN tsvector index on ${index.fields.map((f) => f.name).join(", ")}`,
1356
- type: "text"
1357
- });
1358
- for (const [field, vec] of this._vectorFields) indexes.push({
1359
- name: vec.indexName,
1360
- description: `vector(${vec.dimensions}) on ${field}, ${vec.similarity}`,
1361
- type: "vector"
1345
+ type: "text",
1346
+ fields: this._indexLogicalPaths(index),
1347
+ isDefault: indexes.length === 0
1362
1348
  });
1349
+ let firstVector = true;
1350
+ for (const [field, vec] of this._vectorFields) {
1351
+ indexes.push({
1352
+ name: vec.indexName,
1353
+ description: `vector(${vec.dimensions}) on ${field}, ${vec.similarity}`,
1354
+ type: "vector",
1355
+ fields: [field],
1356
+ isDefault: firstVector
1357
+ });
1358
+ firstVector = false;
1359
+ }
1363
1360
  return indexes;
1364
1361
  }
1365
1362
  async search(text, query, indexName) {
@@ -1464,7 +1461,7 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1464
1461
  found = true;
1465
1462
  break;
1466
1463
  }
1467
- if (!found) throw new Error(`Vector index "${indexName}" not found`);
1464
+ if (!found) throw new Error(vectorIndexNotFoundMessage(indexName));
1468
1465
  } else {
1469
1466
  const first = this._vectorFields.entries().next();
1470
1467
  if (first.done) throw new Error("No vector fields defined");
@@ -1486,41 +1483,31 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1486
1483
  vectorStr: vectorToString(vector)
1487
1484
  };
1488
1485
  }
1489
- _buildVectorSearchQuery(vector, query, indexName) {
1490
- const ctx = this._prepareVectorSearch(vector, query, indexName);
1491
- let inner = `SELECT *, (${qi(ctx.field)} ${ctx.distanceOp} $1::vector) AS _distance FROM ${quoteTableName(ctx.tableName)} WHERE ${offsetPlaceholders(finalizeParams(pgDialect, ctx.where), 1).sql}`;
1492
- const params = [ctx.vectorStr, ...ctx.where.params];
1493
- let sql = `SELECT * FROM (${inner}) _v`;
1494
- if (ctx.threshold !== void 0) {
1495
- sql += ` WHERE _distance <= $${params.length + 1}`;
1496
- params.push(thresholdToDistance(ctx.threshold, ctx.vec.similarity));
1497
- }
1498
- sql += ` ORDER BY _distance ASC`;
1499
- if (ctx.controls.$skip) {
1500
- sql += ` LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
1501
- params.push(ctx.controls.$limit || 1e3, ctx.controls.$skip);
1502
- } else {
1503
- sql += ` LIMIT $${params.length + 1}`;
1504
- params.push(ctx.controls.$limit || 20);
1505
- }
1486
+ /** The row source + distance cap (threshold on pgvector's distance scale) of a vector search. */
1487
+ _vectorSearchSource(ctx, withRows) {
1488
+ const distExpr = {
1489
+ sql: `(${qi(ctx.field)} ${ctx.distanceOp} ?::vector)`,
1490
+ params: [ctx.vectorStr]
1491
+ };
1506
1492
  return {
1507
- sql,
1508
- params
1493
+ source: vectorDistanceSource(pgDialect, ctx.tableName, ctx.where, distExpr, withRows),
1494
+ maxDistance: ctx.threshold === void 0 ? void 0 : thresholdToDistance(ctx.threshold, ctx.vec.similarity)
1509
1495
  };
1510
1496
  }
1511
- _buildVectorSearchCountQuery(vector, query, indexName) {
1497
+ _buildVectorSearchQuery(vector, query, indexName) {
1512
1498
  const ctx = this._prepareVectorSearch(vector, query, indexName);
1513
- let inner = `SELECT (${qi(ctx.field)} ${ctx.distanceOp} $1::vector) AS _distance FROM ${quoteTableName(ctx.tableName)} WHERE ${offsetPlaceholders(finalizeParams(pgDialect, ctx.where), 1).sql}`;
1514
- const params = [ctx.vectorStr, ...ctx.where.params];
1515
- let sql = `SELECT COUNT(*) AS cnt FROM (${inner}) _v`;
1516
- if (ctx.threshold !== void 0) {
1517
- sql += ` WHERE _distance <= $${params.length + 1}`;
1518
- params.push(thresholdToDistance(ctx.threshold, ctx.vec.similarity));
1519
- }
1520
- return {
1521
- sql,
1522
- params
1523
- };
1499
+ const { source, maxDistance } = this._vectorSearchSource(ctx, true);
1500
+ const skip = Number(ctx.controls.$skip) || 0;
1501
+ return buildVectorSearchSelect(pgDialect, source, {
1502
+ select: ctx.controls.$select,
1503
+ limit: Number(ctx.controls.$limit) || (skip ? 1e3 : 20),
1504
+ skip,
1505
+ maxDistance
1506
+ });
1507
+ }
1508
+ _buildVectorSearchCountQuery(vector, query, indexName) {
1509
+ const { source, maxDistance } = this._vectorSearchSource(this._prepareVectorSearch(vector, query, indexName), false);
1510
+ return buildVectorSearchCount(pgDialect, source, { maxDistance });
1524
1511
  }
1525
1512
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
1526
1513
  _resolveVectorThreshold(controls, indexName) {
@@ -1582,10 +1569,7 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1582
1569
  };
1583
1570
  }
1584
1571
  _buildGeoSearchSelect(ctx) {
1585
- return buildGeoSearchSelect(pgDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, {
1586
- $limit: ctx.controls.$limit,
1587
- $skip: ctx.controls.$skip
1588
- });
1572
+ return buildGeoSearchSelect(pgDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, ctx.controls);
1589
1573
  }
1590
1574
  };
1591
1575
  /** A text / character type: the value is converted by the assignment into the column. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-postgres",
3
- "version": "0.1.142",
3
+ "version": "0.1.143",
4
4
  "description": "PostgreSQL adapter for @atscript/db with pg (node-postgres) driver support.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -55,8 +55,8 @@
55
55
  "@atscript/typescript": "^0.1.95",
56
56
  "@uniqu/core": "^0.1.11",
57
57
  "pg": ">=8.0.0",
58
- "@atscript/db": "^0.1.142",
59
- "@atscript/db-sql-tools": "^0.1.142"
58
+ "@atscript/db": "^0.1.143",
59
+ "@atscript/db-sql-tools": "^0.1.143"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "pg": {