@mastra/pg 0.16.1-alpha.0 → 0.16.1

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.js CHANGED
@@ -363,7 +363,9 @@ var PgVector = class extends MastraVector {
363
363
  setupSchemaPromise = null;
364
364
  installVectorExtensionPromise = null;
365
365
  vectorExtensionInstalled = void 0;
366
+ vectorExtensionSchema = null;
366
367
  schemaSetupComplete = void 0;
368
+ cacheWarmupPromise = null;
367
369
  constructor({
368
370
  connectionString,
369
371
  schemaName,
@@ -394,18 +396,24 @@ var PgVector = class extends MastraVector {
394
396
  "vector.type": "postgres"
395
397
  }
396
398
  }) ?? basePool;
397
- void (async () => {
398
- const existingIndexes = await this.listIndexes();
399
- void existingIndexes.map(async (indexName) => {
400
- const info = await this.getIndexInfo({ indexName });
401
- const key = await this.getIndexCacheKey({
402
- indexName,
403
- metric: info.metric,
404
- dimension: info.dimension,
405
- type: info.type
406
- });
407
- this.createdIndexes.set(indexName, key);
408
- });
399
+ this.cacheWarmupPromise = (async () => {
400
+ try {
401
+ const existingIndexes = await this.listIndexes();
402
+ await Promise.all(
403
+ existingIndexes.map(async (indexName) => {
404
+ const info = await this.getIndexInfo({ indexName });
405
+ const key = await this.getIndexCacheKey({
406
+ indexName,
407
+ metric: info.metric,
408
+ dimension: info.dimension,
409
+ type: info.type
410
+ });
411
+ this.createdIndexes.set(indexName, key);
412
+ })
413
+ );
414
+ } catch (error) {
415
+ this.logger?.debug("Cache warming skipped or failed", { error });
416
+ }
409
417
  })();
410
418
  } catch (error) {
411
419
  throw new MastraError(
@@ -425,6 +433,45 @@ var PgVector = class extends MastraVector {
425
433
  if (!this.mutexesByName.has(indexName)) this.mutexesByName.set(indexName, new Mutex());
426
434
  return this.mutexesByName.get(indexName);
427
435
  }
436
+ /**
437
+ * Detects which schema contains the vector extension
438
+ */
439
+ async detectVectorExtensionSchema(client) {
440
+ try {
441
+ const result = await client.query(`
442
+ SELECT n.nspname as schema_name
443
+ FROM pg_extension e
444
+ JOIN pg_namespace n ON e.extnamespace = n.oid
445
+ WHERE e.extname = 'vector'
446
+ LIMIT 1;
447
+ `);
448
+ if (result.rows.length > 0) {
449
+ this.vectorExtensionSchema = result.rows[0].schema_name;
450
+ this.logger.debug("Vector extension found in schema", { schema: this.vectorExtensionSchema });
451
+ return this.vectorExtensionSchema;
452
+ }
453
+ return null;
454
+ } catch (error) {
455
+ this.logger.debug("Could not detect vector extension schema", { error });
456
+ return null;
457
+ }
458
+ }
459
+ /**
460
+ * Gets the properly qualified vector type name
461
+ */
462
+ getVectorTypeName() {
463
+ if (this.vectorExtensionSchema) {
464
+ if (this.vectorExtensionSchema === "pg_catalog") {
465
+ return "vector";
466
+ }
467
+ if (this.vectorExtensionSchema === (this.schema || "public")) {
468
+ return "vector";
469
+ }
470
+ const validatedSchema = parseSqlIdentifier(this.vectorExtensionSchema, "vector extension schema");
471
+ return `${validatedSchema}.vector`;
472
+ }
473
+ return "vector";
474
+ }
428
475
  getTableName(indexName) {
429
476
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
430
477
  const quotedIndexName = `"${parsedIndexName}"`;
@@ -496,11 +543,12 @@ var PgVector = class extends MastraVector {
496
543
  await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
497
544
  }
498
545
  const { tableName } = this.getTableName(indexName);
546
+ const vectorType = this.getVectorTypeName();
499
547
  const query = `
500
548
  WITH vector_scores AS (
501
549
  SELECT
502
550
  vector_id as id,
503
- 1 - (embedding <=> '${vectorStr}'::vector) as score,
551
+ 1 - (embedding <=> '${vectorStr}'::${vectorType}) as score,
504
552
  metadata
505
553
  ${includeVector ? ", embedding" : ""}
506
554
  FROM ${tableName}
@@ -544,13 +592,14 @@ var PgVector = class extends MastraVector {
544
592
  try {
545
593
  await client.query("BEGIN");
546
594
  const vectorIds = ids || vectors.map(() => crypto.randomUUID());
595
+ const vectorType = this.getVectorTypeName();
547
596
  for (let i = 0; i < vectors.length; i++) {
548
597
  const query = `
549
598
  INSERT INTO ${tableName} (vector_id, embedding, metadata)
550
- VALUES ($1, $2::vector, $3::jsonb)
599
+ VALUES ($1, $2::${vectorType}, $3::jsonb)
551
600
  ON CONFLICT (vector_id)
552
601
  DO UPDATE SET
553
- embedding = $2::vector,
602
+ embedding = $2::${vectorType},
554
603
  metadata = $3::jsonb
555
604
  RETURNING embedding::text
556
605
  `;
@@ -697,11 +746,15 @@ var PgVector = class extends MastraVector {
697
746
  try {
698
747
  await this.setupSchema(client);
699
748
  await this.installVectorExtension(client);
749
+ if (this.schema && this.vectorExtensionSchema && this.schema !== this.vectorExtensionSchema && this.vectorExtensionSchema !== "pg_catalog") {
750
+ await client.query(`SET search_path TO ${this.getSchemaName()}, "${this.vectorExtensionSchema}"`);
751
+ }
752
+ const vectorType = this.getVectorTypeName();
700
753
  await client.query(`
701
754
  CREATE TABLE IF NOT EXISTS ${tableName} (
702
755
  id SERIAL PRIMARY KEY,
703
756
  vector_id TEXT UNIQUE NOT NULL,
704
- embedding vector(${dimension}),
757
+ embedding ${vectorType}(${dimension}),
705
758
  metadata JSONB DEFAULT '{}'::jsonb
706
759
  );
707
760
  `);
@@ -756,17 +809,63 @@ var PgVector = class extends MastraVector {
756
809
  async setupIndex({ indexName, metric, indexConfig }, client) {
757
810
  const mutex = this.getMutexByName(`build-${indexName}`);
758
811
  await mutex.runExclusive(async () => {
812
+ const isConfigEmpty = !indexConfig || Object.keys(indexConfig).length === 0 || !indexConfig.type && !indexConfig.ivf && !indexConfig.hnsw;
813
+ const indexType = isConfigEmpty ? "ivfflat" : indexConfig.type || "ivfflat";
759
814
  const { tableName, vectorIndexName } = this.getTableName(indexName);
760
- if (this.createdIndexes.has(indexName)) {
815
+ let existingIndexInfo = null;
816
+ let dimension = 0;
817
+ try {
818
+ existingIndexInfo = await this.getIndexInfo({ indexName });
819
+ dimension = existingIndexInfo.dimension;
820
+ if (isConfigEmpty && existingIndexInfo.metric === metric) {
821
+ if (existingIndexInfo.type === "flat") {
822
+ this.logger?.debug(`No index exists for ${vectorIndexName}, will create default ivfflat index`);
823
+ } else {
824
+ this.logger?.debug(
825
+ `Index ${vectorIndexName} already exists (type: ${existingIndexInfo.type}, metric: ${existingIndexInfo.metric}), preserving existing configuration`
826
+ );
827
+ const cacheKey = await this.getIndexCacheKey({
828
+ indexName,
829
+ dimension,
830
+ type: existingIndexInfo.type,
831
+ metric: existingIndexInfo.metric
832
+ });
833
+ this.createdIndexes.set(indexName, cacheKey);
834
+ return;
835
+ }
836
+ }
837
+ let configMatches = existingIndexInfo.metric === metric && existingIndexInfo.type === indexType;
838
+ if (indexType === "hnsw") {
839
+ configMatches = configMatches && existingIndexInfo.config.m === (indexConfig.hnsw?.m ?? 8) && existingIndexInfo.config.efConstruction === (indexConfig.hnsw?.efConstruction ?? 32);
840
+ } else if (indexType === "flat") {
841
+ configMatches = configMatches && existingIndexInfo.type === "flat";
842
+ } else if (indexType === "ivfflat" && indexConfig.ivf?.lists) {
843
+ configMatches = configMatches && existingIndexInfo.config.lists === indexConfig.ivf?.lists;
844
+ }
845
+ if (configMatches) {
846
+ this.logger?.debug(`Index ${vectorIndexName} already exists with same configuration, skipping recreation`);
847
+ const cacheKey = await this.getIndexCacheKey({
848
+ indexName,
849
+ dimension,
850
+ type: existingIndexInfo.type,
851
+ metric: existingIndexInfo.metric
852
+ });
853
+ this.createdIndexes.set(indexName, cacheKey);
854
+ return;
855
+ }
856
+ this.logger?.info(`Index ${vectorIndexName} configuration changed, rebuilding index`);
761
857
  await client.query(`DROP INDEX IF EXISTS ${vectorIndexName}`);
858
+ this.describeIndexCache.delete(indexName);
859
+ } catch {
860
+ this.logger?.debug(`Index ${indexName} doesn't exist yet, will create it`);
762
861
  }
763
- if (indexConfig.type === "flat") {
862
+ if (indexType === "flat") {
764
863
  this.describeIndexCache.delete(indexName);
765
864
  return;
766
865
  }
767
866
  const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
768
867
  let indexSQL;
769
- if (indexConfig.type === "hnsw") {
868
+ if (indexType === "hnsw") {
770
869
  const m = indexConfig.hnsw?.m ?? 8;
771
870
  const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
772
871
  indexSQL = `
@@ -803,27 +902,48 @@ var PgVector = class extends MastraVector {
803
902
  if (!this.installVectorExtensionPromise) {
804
903
  this.installVectorExtensionPromise = (async () => {
805
904
  try {
806
- const extensionCheck = await client.query(`
807
- SELECT EXISTS (
808
- SELECT 1 FROM pg_extension WHERE extname = 'vector'
905
+ const existingSchema = await this.detectVectorExtensionSchema(client);
906
+ if (existingSchema) {
907
+ this.vectorExtensionInstalled = true;
908
+ this.vectorExtensionSchema = existingSchema;
909
+ this.logger.info(`Vector extension already installed in schema: ${existingSchema}`);
910
+ return;
911
+ }
912
+ try {
913
+ if (this.schema && this.schema !== "public") {
914
+ try {
915
+ await client.query(`CREATE EXTENSION IF NOT EXISTS vector SCHEMA ${this.getSchemaName()}`);
916
+ this.vectorExtensionInstalled = true;
917
+ this.vectorExtensionSchema = this.schema;
918
+ this.logger.info(`Vector extension installed in schema: ${this.schema}`);
919
+ return;
920
+ } catch (schemaError) {
921
+ this.logger.debug(`Could not install vector extension in schema ${this.schema}, trying public schema`, {
922
+ error: schemaError
923
+ });
924
+ }
925
+ }
926
+ await client.query("CREATE EXTENSION IF NOT EXISTS vector");
927
+ const installedSchema = await this.detectVectorExtensionSchema(client);
928
+ if (installedSchema) {
929
+ this.vectorExtensionInstalled = true;
930
+ this.vectorExtensionSchema = installedSchema;
931
+ this.logger.info(`Vector extension installed in schema: ${installedSchema}`);
932
+ }
933
+ } catch (error) {
934
+ this.logger.warn(
935
+ "Could not install vector extension. This requires superuser privileges. If the extension is already installed, you can ignore this warning.",
936
+ { error }
809
937
  );
810
- `);
811
- this.vectorExtensionInstalled = extensionCheck.rows[0].exists;
812
- if (!this.vectorExtensionInstalled) {
813
- try {
814
- await client.query("CREATE EXTENSION IF NOT EXISTS vector");
938
+ const existingSchema2 = await this.detectVectorExtensionSchema(client);
939
+ if (existingSchema2) {
815
940
  this.vectorExtensionInstalled = true;
816
- this.logger.info("Vector extension installed successfully");
817
- } catch {
818
- this.logger.warn(
819
- "Could not install vector extension. This requires superuser privileges. If the extension is already installed globally, you can ignore this warning."
820
- );
941
+ this.vectorExtensionSchema = existingSchema2;
942
+ this.logger.info(`Vector extension found in schema: ${existingSchema2}`);
821
943
  }
822
- } else {
823
- this.logger.debug("Vector extension already installed, skipping installation");
824
944
  }
825
945
  } catch (error) {
826
- this.logger.error("Error checking vector extension status", { error });
946
+ this.logger.error("Error setting up vector extension", { error });
827
947
  this.vectorExtensionInstalled = void 0;
828
948
  this.installVectorExtensionPromise = null;
829
949
  throw error;
@@ -1025,6 +1145,12 @@ var PgVector = class extends MastraVector {
1025
1145
  }
1026
1146
  }
1027
1147
  async disconnect() {
1148
+ if (this.cacheWarmupPromise) {
1149
+ try {
1150
+ await this.cacheWarmupPromise;
1151
+ } catch {
1152
+ }
1153
+ }
1028
1154
  await this.pool.end();
1029
1155
  }
1030
1156
  /**
@@ -1047,8 +1173,9 @@ var PgVector = class extends MastraVector {
1047
1173
  let updateParts = [];
1048
1174
  let values = [id];
1049
1175
  let valueIndex = 2;
1176
+ const vectorType = this.getVectorTypeName();
1050
1177
  if (update.vector) {
1051
- updateParts.push(`embedding = $${valueIndex}::vector`);
1178
+ updateParts.push(`embedding = $${valueIndex}::${vectorType}`);
1052
1179
  values.push(`[${update.vector.join(",")}]`);
1053
1180
  valueIndex++;
1054
1181
  }