{"version":3,"sources":["../src/query/dialect/vector/pagination.ts","../src/query/dialect/vector/pgvector-strategy.ts"],"names":["sql","assertFiniteEmbedding","vectorPhysicalName","quoteIdentifier","VECTOR_CONTRIBUTION_PREFIX","vectorScoreExpression","vectorMinScoreCondition"],"mappings":"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","file":"chunk-FZKXBPTS.cjs","sourcesContent":["/**\n * Shared vector-search pagination clause.\n *\n * Every vector strategy pages its ordered relevance scan the same way: emit a\n * bare `LIMIT` for the first page (`offset` unset or `0`), and `LIMIT … OFFSET`\n * only when a non-zero offset must discard the leading rows. The three engines'\n * `buildSearch` methods each repeated this ternary verbatim; it lives here once\n * so the emitted clause can never drift between dialects.\n *\n * `limit` and `offset` ride as bound parameters (never interpolated), so the\n * clause — string and bound values alike — is identical across engines.\n */\nimport { sql, type SqlFragment } from \"../../sql-fragment\";\n\nexport function vectorPageClause(limit: number, offset?: number): SqlFragment {\n  return offset === undefined || offset === 0 ?\n      sql`LIMIT ${limit}`\n    : sql`LIMIT ${limit} OFFSET ${offset}`;\n}\n","/**\n * pgvector strategy.\n *\n * Postgres' pgvector extension is the one engine where the *same* SQL serves\n * brute-force and ANN: `ORDER BY embedding <=> q LIMIT k` scans sequentially\n * with no index and is rewritten to an HNSW/IVFFlat scan the moment a matching\n * index exists — the planner picks it up automatically. So `buildSearch` never\n * branches on `slot.indexType`; it emits one relevance scan and lets Postgres\n * decide. (Contrast libSQL/sqlite-vec, whose ANN needs distinct syntax.)\n *\n * Storage is per-`(nodeKind, fieldPath)`: a `vector(N)` column carrying the\n * field's fixed dimension `N`, with the operator-class-typed ANN index folded\n * into the same table contribution so materialization builds table + index\n * together. The typed column means the index does not need the legacy\n * `::vector(N)` cast-in-index escape hatch the old shared table relied on.\n *\n * Metric support is the full pgvector set — `cosine`, `l2`, `inner_product` —\n * advertised as data on `capabilities`, mirroring `vector_cosine_ops` /\n * `vector_l2_ops` / `vector_ip_ops` and the `<=>` / `<->` / `<#>` operators.\n *\n * Mirrors the `libsqlVectorStrategy` / `sqliteVecStrategy` structure while\n * letting Postgres' planner pick the ANN index when one exists.\n */\nimport { type StrategyTableContribution } from \"../../../backend/table-contribution\";\nimport {\n  type DeleteEmbeddingParams,\n  type UpsertEmbeddingBatchParams,\n  type UpsertEmbeddingParams,\n  type VectorCapabilities,\n  type VectorMetric,\n  type VectorSearchParams,\n} from \"../../../backend/types\";\nimport { sql, type SqlFragment } from \"../../sql-fragment\";\nimport {\n  assertFiniteEmbedding,\n  quoteIdentifier,\n  VECTOR_CONTRIBUTION_PREFIX,\n  vectorMinScoreCondition,\n  vectorPhysicalName,\n  vectorScoreExpression,\n  type VectorSlot,\n  type VectorStrategy,\n} from \"../vector-strategy\";\nimport { vectorPageClause } from \"./pagination\";\n\n/** Physical-name prefixes for the per-field table and its ANN index. */\nconst TABLE_PREFIX = \"tg_vec\";\nconst INDEX_PREFIX = \"tg_vecidx\";\n\n/**\n * pgvector defines `hnsw.ef_search` as an integer GUC with a valid range\n * of 1..1000; `SET hnsw.ef_search = 1001` errors at the server. The\n * backend validates a per-search `efSearch` against this ceiling before\n * inlining it into `SET LOCAL`, surfacing a clear message instead of a raw\n * pgvector error.\n */\nexport const MAX_HNSW_EF_SEARCH = 1000;\n\n/**\n * Validates a per-search `efSearch` override against pgvector's\n * `hnsw.ef_search` valid range. `undefined` (no override) is accepted.\n */\nexport function assertPgvectorEfSearch(efSearch?: number): void {\n  if (efSearch === undefined) return;\n  if (!Number.isInteger(efSearch) || efSearch <= 0) {\n    throw new Error(`efSearch must be a positive integer, got: ${efSearch}`);\n  }\n  if (efSearch > MAX_HNSW_EF_SEARCH) {\n    throw new RangeError(\n      `efSearch must be ≤ ${MAX_HNSW_EF_SEARCH} (pgvector's hnsw.ef_search ` +\n        `valid range is 1..${MAX_HNSW_EF_SEARCH}), got: ${efSearch}`,\n    );\n  }\n}\n\n/**\n * pgvector's `vector` type caps at 16,000 dimensions for a stored column\n * (2,000 for an indexed column, but the column-type ceiling is what governs\n * storage; recall degradation beyond 2,000 is a tuning concern, not a hard\n * limit advertised here).\n */\nconst PGVECTOR_MAX_DIMENSIONS = 16_000;\n\n/** Default HNSW `m` (max connections per layer) — pgvector's own default. */\nconst DEFAULT_HNSW_M = 16;\n/** Default HNSW `ef_construction` (build candidate list) — pgvector's own default. */\nconst DEFAULT_HNSW_EF_CONSTRUCTION = 64;\n/** Default IVFFlat `lists` (inverted lists) — pgvector's own default. */\nconst DEFAULT_IVFFLAT_LISTS = 100;\n\nconst PGVECTOR_CAPABILITIES: VectorCapabilities = {\n  supported: true,\n  metrics: [\"cosine\", \"l2\", \"inner_product\"],\n  indexTypes: [\"hnsw\", \"ivfflat\", \"none\"],\n  maxDimensions: PGVECTOR_MAX_DIMENSIONS,\n  // The backend probes `pg_extension.extversion` once and turns on\n  // `hnsw.iterative_scan` / `ivfflat.iterative_scan` when pgvector >= 0.8, so a\n  // filtered scan re-enters the index for more candidates.\n  //\n  // NOT a full-page guarantee, on either axis. The iterative scan stops at\n  // `hnsw.max_scan_tuples` / `ivfflat.max_probes`; and on pgvector < 0.8 there\n  // is no iterative scan at all, so the search stays `ef_search`-bounded and\n  // behaves like a post-filter. A statically-declared mode cannot express\n  // either, which is why the guarantee is its own field.\n  filteredApproximateSearch: {\n    mode: \"iterative-scan\",\n    guaranteesFullPage: false,\n  },\n  // The one bundled engine with a real per-search frontier: `hnsw.ef_search`\n  // sizes the HNSW candidate list. IVFFlat has no equivalent (`ivfflat.probes`\n  // is a different structure's knob and is not what `efSearch` means), and the\n  // GUC is applied with `SET LOCAL` inside the search's own transaction so\n  // concurrent searches cannot inherit each other's override.\n  searchFrontierTuning: {\n    tunable: true,\n    parameter: \"hnsw.ef_search\",\n    indexType: \"hnsw\",\n    requiresTransactionScope: true,\n  },\n};\n\n/** Whether a slot's declared index type materializes a real pgvector ANN index. */\nfunction usesAnnIndex(slot: VectorSlot): boolean {\n  return slot.indexType === \"hnsw\" || slot.indexType === \"ivfflat\";\n}\n\n/**\n * pgvector operator class for a metric's ANN index — `vector_cosine_ops` /\n * `vector_l2_ops` / `vector_ip_ops`, mirroring the `<=>` / `<->` / `<#>`\n * distance operators this strategy emits.\n */\nfunction operatorClass(metric: VectorMetric): string {\n  switch (metric) {\n    case \"cosine\": {\n      return \"vector_cosine_ops\";\n    }\n    case \"l2\": {\n      return \"vector_l2_ops\";\n    }\n    case \"inner_product\": {\n      return \"vector_ip_ops\";\n    }\n    default: {\n      const _exhaustive: never = metric;\n      throw new Error(`Unsupported vector metric: ${String(_exhaustive)}`);\n    }\n  }\n}\n\n/**\n * Formats an embedding as a `::vector` literal. The value is bound as a\n * parameter (not interpolated) — Postgres parses the `[a,b,c]` text and the\n * cast types it. Finiteness is validated first so a NaN names its index.\n */\nfunction vectorLiteral(\n  embedding: readonly number[],\n  name: string,\n): SqlFragment {\n  assertFiniteEmbedding(embedding, name);\n  return sql`${`[${embedding.join(\",\")}]`}::vector`;\n}\n\nfunction distanceExpression(\n  embeddingColumn: SqlFragment,\n  queryEmbedding: readonly number[],\n  metric: VectorMetric,\n): SqlFragment {\n  const query = vectorLiteral(queryEmbedding, \"queryEmbedding\");\n  switch (metric) {\n    case \"cosine\": {\n      return sql`(${embeddingColumn} <=> ${query})`;\n    }\n    case \"l2\": {\n      return sql`(${embeddingColumn} <-> ${query})`;\n    }\n    case \"inner_product\": {\n      return sql`(${embeddingColumn} <#> ${query})`;\n    }\n    default: {\n      const _exhaustive: never = metric;\n      throw new Error(`Unsupported vector metric: ${String(_exhaustive)}`);\n    }\n  }\n}\n\nfunction tableName(\n  graphId: string,\n  nodeKind: string,\n  fieldPath: string,\n): string {\n  return vectorPhysicalName(TABLE_PREFIX, graphId, nodeKind, fieldPath);\n}\n\nexport const pgvectorStrategy: VectorStrategy = {\n  name: \"pgvector\",\n  capabilities: PGVECTOR_CAPABILITIES,\n\n  tableName,\n\n  ownedTables(slot): readonly StrategyTableContribution[] {\n    const table = tableName(slot.graphId, slot.nodeKind, slot.fieldPath);\n    const name = quoteIdentifier(table);\n\n    // No standalone graph_id index: the PRIMARY KEY (graph_id, node_id) already\n    // covers `WHERE graph_id = ?` via its leading column, so a separate index\n    // would be pure write amplification.\n    const createDdl = [\n      `CREATE TABLE IF NOT EXISTS ${name} (\n  \"graph_id\" TEXT NOT NULL,\n  \"node_id\" TEXT NOT NULL,\n  \"embedding\" vector(${slot.dimensions}) NOT NULL,\n  \"created_at\" TIMESTAMPTZ NOT NULL,\n  \"updated_at\" TIMESTAMPTZ NOT NULL,\n  PRIMARY KEY (\"graph_id\", \"node_id\")\n);`,\n    ];\n\n    // The HNSW/IVFFlat index is intentionally NOT created here. pgvector\n    // similarity SQL is planner-driven (`ORDER BY embedding <=> q LIMIT k`\n    // uses the index when present, sequential-scans when not), so the index\n    // is a pure materialization concern: `buildCreateIndex` builds it through\n    // `materializeIndexes()` with the field's declared `m`/`ef_construction`/\n    // `lists`. Building it eagerly here would bake in default tuning (the\n    // write-ensure slot has no `indexParams`) and `CREATE INDEX IF NOT EXISTS`\n    // would then mask the tuned index materialization would emit.\n\n    return [\n      {\n        scope: \"graph\",\n        logicalName: `${VECTOR_CONTRIBUTION_PREFIX}:${slot.nodeKind}.${slot.fieldPath}`,\n        owner: \"pgvector\",\n        tableName: table,\n        createDdl,\n        runtimeEnsure: true,\n      },\n    ];\n  },\n\n  buildUpsert(\n    slot,\n    params: UpsertEmbeddingParams,\n    timestamp,\n  ): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const value = vectorLiteral(params.embedding, \"embedding\");\n    return [\n      sql`\n        INSERT INTO ${table} (\"graph_id\", \"node_id\", \"embedding\", \"created_at\", \"updated_at\")\n        VALUES (${params.graphId}, ${params.nodeId}, ${value}, ${timestamp}, ${timestamp})\n        ON CONFLICT (\"graph_id\", \"node_id\")\n        DO UPDATE SET \"embedding\" = EXCLUDED.\"embedding\", \"updated_at\" = EXCLUDED.\"updated_at\"\n      `,\n    ];\n  },\n\n  buildUpsertFromInsertedNode(slot, sourceAlias, embedding, timestamp) {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const source = sql.identifier(sourceAlias);\n    const sourceColumn = (name: string): SqlFragment =>\n      sql`${source}.${sql.identifier(name)}`;\n    const value = vectorLiteral(embedding, \"embedding\");\n\n    return sql`\n      INSERT INTO ${table}\n        (\"graph_id\", \"node_id\", \"embedding\", \"created_at\", \"updated_at\")\n      SELECT\n        ${sourceColumn(\"graph_id\")}, ${sourceColumn(\"id\")},\n        ${value}, ${timestamp}, ${timestamp}\n      FROM ${source}\n      ON CONFLICT (\"graph_id\", \"node_id\")\n      DO UPDATE SET\n        \"embedding\" = EXCLUDED.\"embedding\",\n        \"updated_at\" = EXCLUDED.\"updated_at\"\n      RETURNING 1\n    `;\n  },\n\n  buildUpsertBatch(\n    slot,\n    params: UpsertEmbeddingBatchParams,\n    timestamp,\n  ): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const valueRows = sql.join(\n      params.rows.map(\n        (row) =>\n          sql`(${params.graphId}, ${row.nodeId}, ${vectorLiteral(row.embedding, \"embedding\")}, ${timestamp}, ${timestamp})`,\n      ),\n      sql`, `,\n    );\n    return [\n      sql`\n        INSERT INTO ${table} (\"graph_id\", \"node_id\", \"embedding\", \"created_at\", \"updated_at\")\n        VALUES ${valueRows}\n        ON CONFLICT (\"graph_id\", \"node_id\")\n        DO UPDATE SET \"embedding\" = EXCLUDED.\"embedding\", \"updated_at\" = EXCLUDED.\"updated_at\"\n      `,\n    ];\n  },\n\n  buildDelete(slot, params: DeleteEmbeddingParams): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    return [\n      sql`\n        DELETE FROM ${table}\n        WHERE \"graph_id\" = ${params.graphId} AND \"node_id\" = ${params.nodeId}\n      `,\n    ];\n  },\n\n  buildDeleteBatch(\n    slot,\n    params: Omit<DeleteEmbeddingParams, \"nodeId\"> &\n      Readonly<{ nodeIds: readonly string[] }>,\n  ): readonly SqlFragment[] {\n    if (params.nodeIds.length === 0) return [];\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    return [\n      sql`DELETE FROM ${table} WHERE \"graph_id\" = ${params.graphId} AND \"node_id\" IN (${sql.join(\n        params.nodeIds.map((nodeId) => sql`${nodeId}`),\n        sql`, `,\n      )})`,\n    ];\n  },\n\n  buildSearch(\n    slot,\n    params: VectorSearchParams,\n    candidates?: SqlFragment,\n  ): SqlFragment {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const embeddingColumn = sql`${table}.\"embedding\"`;\n    const distance = distanceExpression(\n      embeddingColumn,\n      params.queryEmbedding,\n      params.metric,\n    );\n    const score = vectorScoreExpression(distance, params.metric);\n\n    // Same SQL for brute-force and ANN: with a matching HNSW/IVFFlat index the\n    // Postgres planner rewrites this `ORDER BY distance LIMIT k` into an index\n    // scan automatically, so the strategy never branches on `slot.indexType`.\n    const conditions: SqlFragment[] = [\n      sql`${table}.\"graph_id\" = ${params.graphId}`,\n    ];\n    if (params.minScore !== undefined) {\n      conditions.push(\n        vectorMinScoreCondition(distance, params.metric, params.minScore),\n      );\n    }\n    // Candidate pushdown keeps the HNSW scan (verified plan: HNSW Index Scan\n    // -> Nested Loop probe of the nodes pkey -> Limit). Exact under\n    // `hnsw.iterative_scan` (pgvector >= 0.8, applied by the backend);\n    // bounded by `ef_search` on older pgvector — still strictly better than\n    // ranking tombstones into top-k and dropping them post-hoc.\n    //\n    // Plain `IN (subquery)`: with fresh statistics the planner hashes it\n    // (brute-force scans) or drives per-row probes of the nodes pkey\n    // under the ordered HNSW scan — the plan-verified shape the liveness\n    // pushdown was built on. Stale statistics degrade any membership\n    // form; the answer is `store.refreshStatistics()` after bulk loads,\n    // not a cleverer SQL shape.\n    if (candidates !== undefined) {\n      conditions.push(sql`${table}.\"node_id\" IN (${candidates})`);\n    }\n    // Pagination is rank-relative: the scan fetches `limit + offset`\n    // ordered candidates and OFFSET discards the leading page.\n    const pageOffset = params.offset ?? 0;\n    const pageClause = vectorPageClause(params.limit, params.offset);\n    // IVFFlat's iterative scan only offers `relaxed_order` (no\n    // strict_order mode), so under the backend-applied\n    // `ivfflat.iterative_scan` the index may emit the candidate set\n    // slightly out of distance order. Re-sort the bounded set inside a\n    // MATERIALIZED wrapper — the fence stops the planner from collapsing\n    // the sort back into the index scan's claimed ordering — and page in\n    // the wrapper: an OFFSET inside the relaxed scan could discard the\n    // wrong rows. Score is monotone in distance, so ordering by score per\n    // the metric's direction restores exact ranking.\n    if (slot.indexType === \"ivfflat\") {\n      const direction =\n        params.metric === \"cosine\" ? sql.raw(\"DESC\") : sql.raw(\"ASC\");\n      const relaxedBody = sql`\n        SELECT ${table}.\"node_id\" AS node_id, ${score} AS score\n        FROM ${table}\n        WHERE ${sql.join(conditions, sql` AND `)}\n        ORDER BY ${distance} ASC\n        LIMIT ${params.limit + pageOffset}\n      `;\n      return sql`\n        WITH tg_vec_relaxed AS MATERIALIZED (${relaxedBody})\n        SELECT node_id, score FROM tg_vec_relaxed\n        ORDER BY score ${direction}, node_id ASC\n        ${pageClause}\n      `;\n    }\n    return sql`\n      SELECT ${table}.\"node_id\" AS node_id, ${score} AS score\n      FROM ${table}\n      WHERE ${sql.join(conditions, sql` AND `)}\n      ORDER BY ${distance} ASC\n      ${pageClause}\n    `;\n  },\n\n  distanceExpression(embeddingColumn, queryEmbedding, metric) {\n    return distanceExpression(embeddingColumn, queryEmbedding, metric);\n  },\n\n  buildCreateIndex(slot, options): SqlFragment | undefined {\n    if (!usesAnnIndex(slot)) return undefined;\n    return sql.raw(\n      pgvectorIndexDdl(\n        tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n        slot,\n        options?.concurrent === true,\n      ),\n    );\n  },\n\n  buildDropIndex(slot): SqlFragment | undefined {\n    if (!usesAnnIndex(slot)) return undefined;\n    const indexName = pgvectorIndexName(slot);\n    return sql.raw(`DROP INDEX IF EXISTS ${quoteIdentifier(indexName)}`);\n  },\n\n  buildDropStorage(slot): readonly string[] {\n    const table = quoteIdentifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    // CASCADE drops the ANN index along with the table.\n    return [`DROP TABLE IF EXISTS ${table} CASCADE`];\n  },\n};\n\nfunction pgvectorIndexName(slot: VectorSlot): string {\n  return vectorPhysicalName(\n    INDEX_PREFIX,\n    slot.graphId,\n    slot.nodeKind,\n    slot.fieldPath,\n  );\n}\n\nfunction pgvectorIndexDdl(\n  table: string,\n  slot: VectorSlot,\n  concurrent: boolean,\n): string {\n  const indexName = quoteIdentifier(pgvectorIndexName(slot));\n  const quotedTable = quoteIdentifier(table);\n  const opClass = operatorClass(slot.metric);\n  // Honor the field's declared tuning; fall back to pgvector defaults.\n  const m = slot.indexParams?.m ?? DEFAULT_HNSW_M;\n  const efConstruction =\n    slot.indexParams?.efConstruction ?? DEFAULT_HNSW_EF_CONSTRUCTION;\n  const lists = slot.indexParams?.lists ?? DEFAULT_IVFFLAT_LISTS;\n  // CONCURRENTLY builds without taking a write-blocking lock on the live table\n  // (materializeIndexes passes concurrent on Postgres). Run outside a tx.\n  const create = `CREATE INDEX${concurrent ? \" CONCURRENTLY\" : \"\"} IF NOT EXISTS`;\n\n  switch (slot.indexType) {\n    case \"hnsw\": {\n      return `${create} ${indexName} ON ${quotedTable} USING hnsw (\"embedding\" ${opClass}) WITH (m = ${m}, ef_construction = ${efConstruction});`;\n    }\n    case \"ivfflat\": {\n      return `${create} ${indexName} ON ${quotedTable} USING ivfflat (\"embedding\" ${opClass}) WITH (lists = ${lists});`;\n    }\n    case \"none\": {\n      throw new Error(\n        \"pgvectorIndexDdl called for a brute-force-only slot (indexType 'none').\",\n      );\n    }\n    default: {\n      const _exhaustive: never = slot.indexType;\n      throw new Error(`Unsupported vector index type: ${String(_exhaustive)}`);\n    }\n  }\n}\n"]}