@h-ai/vecdb 1.0.0-beta.7 → 1.0.0-beta.9

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.en.md CHANGED
@@ -51,6 +51,8 @@ if (!closeResult.success) {
51
51
 
52
52
  ### Configuration
53
53
 
54
+ pgvector persists each collection metric in its vector column comment; indexes, info and queries use that definition. Changing the connection default leaves existing collections unchanged. Legacy collections recover the metric from their vector index; missing both index and valid metadata returns CONFIG_ERROR until repaired.
55
+
54
56
  ```ts
55
57
  // LanceDB(默认,嵌入式本地存储)
56
58
  await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
package/README.ja.md CHANGED
@@ -51,6 +51,8 @@ if (!closeResult.success) {
51
51
 
52
52
  ### 設定
53
53
 
54
+ pgvector は距離尺度を vector 列コメントに保存し、索引・info・検索で共有します。接続の既定値変更は既存集合に影響しません。旧集合は実際の索引から復元し、索引と有効なメタデータが両方ない場合は CONFIG_ERROR を返します。
55
+
54
56
  ```ts
55
57
  // LanceDB(默认,嵌入式本地存储)
56
58
  await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
package/README.md CHANGED
@@ -51,6 +51,8 @@ if (!closeResult.success) {
51
51
 
52
52
  ### 配置
53
53
 
54
+ pgvector 创建集合时将距离算法保存在 vector 列注释,索引、info 与查询均读取该集合定义;修改连接默认 metric 不改变已有集合。旧集合从实际向量索引恢复算法;索引和有效注释均缺失时返回 CONFIG_ERROR,须先恢复集合定义。
55
+
54
56
  ```ts
55
57
  // LanceDB(默认,嵌入式本地存储)
56
58
  await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
package/dist/index.js CHANGED
@@ -38,7 +38,8 @@ var en_US_default = {
38
38
  vecdb_configDatabaseRequired: "Database name is required",
39
39
  vecdb_sharedBackendRequired: "Production mode requires a shared backend; local files and memory backends are for development or explicit end-to-end tests.",
40
40
  vecdb_chromaEndpointInvalid: "Chroma requires an HTTP(S) root endpoint without credentials, path prefix, query or fragment",
41
- vecdb_chromaTokenInvalid: "Chroma Bearer token must contain only printable ASCII characters without whitespace"
41
+ vecdb_chromaTokenInvalid: "Chroma Bearer token must contain only printable ASCII characters without whitespace",
42
+ vecdb_collectionMetricMissing: "Collection distance metric is missing; restore its vector index or column metadata"
42
43
  };
43
44
 
44
45
  // messages/ja-JP.json
@@ -71,7 +72,8 @@ var ja_JP_default = {
71
72
  vecdb_configDatabaseRequired: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u540D\u306F\u5FC5\u9808\u3067\u3059",
72
73
  vecdb_sharedBackendRequired: "\u672C\u756A\u74B0\u5883\u306B\u306F\u5171\u6709\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u304C\u5FC5\u8981\u3067\u3059\u3002\u30ED\u30FC\u30AB\u30EB\u30D5\u30A1\u30A4\u30EB\u3068\u30E1\u30E2\u30EA\u30FC\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306F\u958B\u767A\u74B0\u5883\u307E\u305F\u306F\u660E\u793A\u7684\u306A E2E \u30C6\u30B9\u30C8\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002",
73
74
  vecdb_chromaEndpointInvalid: "Chroma \u306B\u306F HTTP(S) \u306E\u30EB\u30FC\u30C8\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u8A8D\u8A3C\u60C5\u5831\u3001\u30D1\u30B9\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u3001\u30AF\u30A8\u30EA\u3001\u30D5\u30E9\u30B0\u30E1\u30F3\u30C8\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
74
- vecdb_chromaTokenInvalid: "Chroma Bearer \u30C8\u30FC\u30AF\u30F3\u306B\u306F\u7A7A\u767D\u3092\u542B\u307E\u306A\u3044\u8868\u793A\u53EF\u80FD\u306A ASCII \u6587\u5B57\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059"
75
+ vecdb_chromaTokenInvalid: "Chroma Bearer \u30C8\u30FC\u30AF\u30F3\u306B\u306F\u7A7A\u767D\u3092\u542B\u307E\u306A\u3044\u8868\u793A\u53EF\u80FD\u306A ASCII \u6587\u5B57\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059",
76
+ vecdb_collectionMetricMissing: "\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u306E\u8DDD\u96E2\u5C3A\u5EA6\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u30D9\u30AF\u30C8\u30EB\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u307E\u305F\u306F\u5217\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u5FA9\u5143\u3057\u3066\u304F\u3060\u3055\u3044"
75
77
  };
76
78
 
77
79
  // messages/zh-CN.json
@@ -104,7 +106,8 @@ var zh_CN_default = {
104
106
  vecdb_configDatabaseRequired: "\u6570\u636E\u5E93\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A",
105
107
  vecdb_sharedBackendRequired: "\u751F\u4EA7\u6A21\u5F0F\u9700\u8981\u8DE8\u8282\u70B9\u5171\u4EAB\u540E\u7AEF\uFF1B\u672C\u673A\u6587\u4EF6\u6216\u5185\u5B58\u540E\u7AEF\u4EC5\u7528\u4E8E\u5F00\u53D1\u548C\u663E\u5F0F\u7AEF\u5230\u7AEF\u6D4B\u8BD5\u3002",
106
108
  vecdb_chromaEndpointInvalid: "Chroma \u4EC5\u652F\u6301 HTTP(S) \u6839\u7AEF\u70B9\uFF0C\u4E0D\u5F97\u5305\u542B\u51ED\u636E\u3001\u8DEF\u5F84\u524D\u7F00\u3001query \u6216 fragment",
107
- vecdb_chromaTokenInvalid: "Chroma Bearer token \u4EC5\u652F\u6301\u65E0\u7A7A\u767D\u7684\u53EF\u6253\u5370 ASCII \u5B57\u7B26"
109
+ vecdb_chromaTokenInvalid: "Chroma Bearer token \u4EC5\u652F\u6301\u65E0\u7A7A\u767D\u7684\u53EF\u6253\u5370 ASCII \u5B57\u7B26",
110
+ vecdb_collectionMetricMissing: "\u96C6\u5408\u7F3A\u5C11\u6709\u6548\u8DDD\u79BB\u7B97\u6CD5\u5143\u6570\u636E\uFF0C\u8BF7\u6838\u5BF9\u5E76\u6062\u590D\u5411\u91CF\u7D22\u5F15\u6216\u5217\u6CE8\u91CA"
108
111
  };
109
112
 
110
113
  // src/vecdb-i18n.ts
@@ -1054,8 +1057,7 @@ function createPgvectorProvider() {
1054
1057
  function quoteIdent(name) {
1055
1058
  return `"${name.replace(/"/g, '""')}"`;
1056
1059
  }
1057
- function distanceOp() {
1058
- const metric = config?.metric ?? "cosine";
1060
+ function distanceOp(metric) {
1059
1061
  switch (metric) {
1060
1062
  case "cosine":
1061
1063
  return "<=>";
@@ -1067,8 +1069,7 @@ function createPgvectorProvider() {
1067
1069
  return "<=>";
1068
1070
  }
1069
1071
  }
1070
- function distanceToScore2(distance) {
1071
- const metric = config?.metric ?? "cosine";
1072
+ function distanceToScore2(distance, metric) {
1072
1073
  switch (metric) {
1073
1074
  case "cosine":
1074
1075
  return 1 - distance / 2;
@@ -1116,6 +1117,20 @@ function createPgvectorProvider() {
1116
1117
  `, [quoteIdent(table)]);
1117
1118
  return ok(dimResult.rows.length > 0 ? Number(dimResult.rows[0].atttypmod) : 0);
1118
1119
  }
1120
+ async function getCollectionMetric(name) {
1121
+ const result = await pool.query(`SELECT col_description(a.attrelid, a.attnum) AS definition,
1122
+ (SELECT opc.opcname FROM pg_index i JOIN pg_opclass opc ON opc.oid = ANY(i.indclass)
1123
+ WHERE i.indrelid = a.attrelid AND opc.opcname IN ('vector_cosine_ops', 'vector_l2_ops', 'vector_ip_ops') LIMIT 1) AS operator
1124
+ FROM pg_attribute a WHERE a.attrelid = $1::regclass AND a.attname = 'vector'`, [quoteIdent(tableName(name))]);
1125
+ const row = result.rows[0];
1126
+ const stored = typeof row?.definition === "string" && row.definition.startsWith("hai.vecdb.metric=") ? row.definition.slice("hai.vecdb.metric=".length) : void 0;
1127
+ const inherited = row?.operator === "vector_cosine_ops" ? "cosine" : row?.operator === "vector_l2_ops" ? "euclidean" : row?.operator === "vector_ip_ops" ? "dot" : void 0;
1128
+ const metric = stored ?? inherited;
1129
+ const parsed = DistanceMetricSchema.safeParse(metric);
1130
+ if (metric === void 0 || !parsed.success)
1131
+ return err(HaiVecdbError.CONFIG_ERROR, vecdbM("vecdb_collectionMetricMissing"));
1132
+ return ok(parsed.data);
1133
+ }
1119
1134
  function validateVectorDimension(expected, actual) {
1120
1135
  if (expected === actual) {
1121
1136
  return null;
@@ -1141,7 +1156,7 @@ function createPgvectorProvider() {
1141
1156
  async create(name, options) {
1142
1157
  const table = tableName(name);
1143
1158
  const dimension = options.dimension;
1144
- const metric = options.metric ?? config?.metric ?? "cosine";
1159
+ const metric = DistanceMetricSchema.parse(options.metric ?? config?.metric ?? "cosine");
1145
1160
  const indexType = config?.indexType ?? "hnsw";
1146
1161
  logger3.debug("Creating collection", { name, dimension, metric, indexType });
1147
1162
  const checkResult = await pool.query(
@@ -1160,7 +1175,7 @@ function createPgvectorProvider() {
1160
1175
  metadata JSONB DEFAULT '{}'::jsonb
1161
1176
  )
1162
1177
  `);
1163
- const distOp = distanceOp();
1178
+ const distOp = distanceOp(metric);
1164
1179
  const opsClass = distOp === "<=>" ? "vector_cosine_ops" : distOp === "<->" ? "vector_l2_ops" : "vector_ip_ops";
1165
1180
  if (indexType === "hnsw") {
1166
1181
  await pool.query(`
@@ -1174,6 +1189,7 @@ function createPgvectorProvider() {
1174
1189
  WITH (lists = 100)
1175
1190
  `);
1176
1191
  }
1192
+ await pool.query(`COMMENT ON COLUMN ${qi}.vector IS 'hai.vecdb.metric=${metric}'`);
1177
1193
  logger3.info("Collection created", { name, dimension, metric, indexType });
1178
1194
  return ok(void 0);
1179
1195
  },
@@ -1206,12 +1222,15 @@ function createPgvectorProvider() {
1206
1222
  if (!dimensionResult.success) {
1207
1223
  return dimensionResult;
1208
1224
  }
1225
+ const metric = await getCollectionMetric(name);
1226
+ if (!metric.success)
1227
+ return metric;
1209
1228
  const countResult = await pool.query(`SELECT COUNT(*) as count FROM ${quoteIdent(table)}`);
1210
1229
  const count = Number.parseInt(String(countResult.rows[0].count), 10);
1211
1230
  return ok({
1212
1231
  name,
1213
1232
  dimension: dimensionResult.data,
1214
- metric: config?.metric ?? "cosine",
1233
+ metric: metric.data,
1215
1234
  count
1216
1235
  });
1217
1236
  },
@@ -1299,7 +1318,6 @@ function createPgvectorProvider() {
1299
1318
  const table = tableName(collection);
1300
1319
  const topK = options?.topK ?? 10;
1301
1320
  const minScore = options?.minScore ?? 0;
1302
- const op = distanceOp();
1303
1321
  logger3.debug("Searching vectors", { collection, topK, hasFilter: !!options?.filter });
1304
1322
  const dimensionResult = await getCollectionDimension(collection);
1305
1323
  if (!dimensionResult.success) {
@@ -1309,6 +1327,10 @@ function createPgvectorProvider() {
1309
1327
  if (dimensionValidation) {
1310
1328
  return dimensionValidation;
1311
1329
  }
1330
+ const metric = await getCollectionMetric(collection);
1331
+ if (!metric.success)
1332
+ return metric;
1333
+ const op = distanceOp(metric.data);
1312
1334
  const vectorStr = `[${vector.join(",")}]`;
1313
1335
  let filterSQL = "";
1314
1336
  const filterParams = [vectorStr, topK];
@@ -1333,7 +1355,7 @@ function createPgvectorProvider() {
1333
1355
  filterParams
1334
1356
  );
1335
1357
  const searchResults = result.rows.map((row) => {
1336
- const score = distanceToScore2(row.distance);
1358
+ const score = distanceToScore2(row.distance, metric.data);
1337
1359
  return {
1338
1360
  id: row.id,
1339
1361
  score,