@h-ai/vecdb 0.1.0-alpha.56 → 0.1.0-alpha.58
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 +145 -0
- package/README.ja.md +145 -0
- package/README.md +5 -1
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/README.en.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @h-ai/vecdb
|
|
2
|
+
|
|
3
|
+
[简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
[Framework home](../../README.en.md) · Related modules: [@h-ai/core](../core/README.en.md)
|
|
6
|
+
|
|
7
|
+
A vector database module with a unified `vecdb` object for LanceDB, pgvector, Qdrant, and Chroma.
|
|
8
|
+
|
|
9
|
+
## Supported vector databases
|
|
10
|
+
|
|
11
|
+
- LanceDB (embedded, local file storage, zero configuration)
|
|
12
|
+
- pgvector (PostgreSQL + pgvector extension)
|
|
13
|
+
- Qdrant (high-performance vector search engine; server 1.10.0+ with the Query API)
|
|
14
|
+
- Chroma (automatically start a local service in embedded mode, or connect to an existing service)
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { HaiVecdbError, vecdb } from '@h-ai/vecdb'
|
|
20
|
+
|
|
21
|
+
// 初始化(LanceDB)
|
|
22
|
+
await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
|
|
23
|
+
|
|
24
|
+
// 创建集合
|
|
25
|
+
await vecdb.collection.create('docs', { dimension: 1536 })
|
|
26
|
+
|
|
27
|
+
// 插入向量
|
|
28
|
+
await vecdb.vector.insert('docs', [
|
|
29
|
+
{ id: 'doc-1', vector: Array.from({ length: 1536 }).fill(0.1), content: '文档内容', metadata: { source: 'wiki' } },
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
// 搜索
|
|
33
|
+
const searchResult = await vecdb.vector.search('docs', Array.from({ length: 1536 }).fill(0.2), { topK: 5, minScore: 0.7 })
|
|
34
|
+
if (searchResult.success) {
|
|
35
|
+
for (const item of searchResult.data) {
|
|
36
|
+
// item.id, item.score, item.content
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 关闭
|
|
41
|
+
const closeResult = await vecdb.close()
|
|
42
|
+
if (!closeResult.success) {
|
|
43
|
+
throw new Error(closeResult.error.message)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// LanceDB(默认,嵌入式本地存储)
|
|
51
|
+
await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
|
|
52
|
+
|
|
53
|
+
// pgvector(连接字符串)
|
|
54
|
+
await vecdb.init({ type: 'pgvector', url: 'postgres://user:pass@localhost:5432/mydb' })
|
|
55
|
+
|
|
56
|
+
// pgvector(分字段)
|
|
57
|
+
await vecdb.init({
|
|
58
|
+
type: 'pgvector',
|
|
59
|
+
host: 'localhost',
|
|
60
|
+
port: 5432,
|
|
61
|
+
database: 'mydb',
|
|
62
|
+
user: 'admin',
|
|
63
|
+
password: 'secret',
|
|
64
|
+
indexType: 'hnsw',
|
|
65
|
+
tablePrefix: 'vec_',
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Qdrant
|
|
69
|
+
await vecdb.init({ type: 'qdrant', url: 'http://localhost:6333', apiKey: 'optional-key' })
|
|
70
|
+
|
|
71
|
+
// Chroma(嵌入式:自动拉起本地 `chroma run` 服务并持久化到 path)
|
|
72
|
+
await vecdb.init({ type: 'chroma', path: './data/chroma' })
|
|
73
|
+
|
|
74
|
+
// Chroma(直连已有服务,不拉起进程)
|
|
75
|
+
await vecdb.init({ type: 'chroma', url: 'http://localhost:8000' })
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> On Node, Chroma provides only an HTTP client, with no in-process embedded database. In embedded mode, `vecdb.init`
|
|
79
|
+
> starts a local service with `serverCommand` (default: `chroma`, from the `chromadb` package), and `vecdb.close`
|
|
80
|
+
> stops the process. Install the optional `chromadb` dependency; if the command is unavailable, `init` returns `CONNECTION_FAILED`.
|
|
81
|
+
|
|
82
|
+
### Operation logging
|
|
83
|
+
|
|
84
|
+
Enable operation logging with `operationLog` in the vector database configuration. Logs are emitted before collection/vector operations reach the real provider, without a shared wrapper in `vecdb-main.ts`.
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
# config/_vecdb.yml
|
|
88
|
+
type: lancedb
|
|
89
|
+
path: ./data/vecdb
|
|
90
|
+
operationLog:
|
|
91
|
+
read: false # collection.exists/info/list, vector.search/count
|
|
92
|
+
write: false # collection.create/drop, vector.insert/upsert/delete
|
|
93
|
+
maxLength: 1000 # 参数序列化后的最大输出长度,超出会截断
|
|
94
|
+
level: debug # info | debug | trace,默认 debug
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
await vecdb.init({
|
|
99
|
+
type: 'lancedb',
|
|
100
|
+
path: './data/vecdb',
|
|
101
|
+
operationLog: {
|
|
102
|
+
read: true,
|
|
103
|
+
write: true,
|
|
104
|
+
maxLength: 500,
|
|
105
|
+
level: 'debug',
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> Note: `vecdb.config` returns a **redacted configuration snapshot**. Connection-string usernames/passwords, standalone `password`, and `apiKey` are replaced with `[REDACTED]`.
|
|
111
|
+
|
|
112
|
+
> Behavior: empty batch `insert/upsert/delete` calls are no-ops. Writes and searches validate vector dimensions, returning `HaiVecdbError.DIMENSION_MISMATCH` on a mismatch; missing optional drivers return `HaiVecdbError.DRIVER_NOT_FOUND`.
|
|
113
|
+
|
|
114
|
+
## Error handling
|
|
115
|
+
|
|
116
|
+
All operations return `HaiResult<T>`; check `result.success` for success or failure.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const result = await vecdb.collection.create('docs', { dimension: 1536 })
|
|
120
|
+
if (!result.success) {
|
|
121
|
+
switch (result.error.code) {
|
|
122
|
+
case HaiVecdbError.NOT_INITIALIZED.code:
|
|
123
|
+
// 请先调用 vecdb.init()
|
|
124
|
+
break
|
|
125
|
+
case HaiVecdbError.COLLECTION_ALREADY_EXISTS.code:
|
|
126
|
+
// 集合已存在
|
|
127
|
+
break
|
|
128
|
+
case HaiVecdbError.CONNECTION_FAILED.code:
|
|
129
|
+
// 连接失败
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Tests
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pnpm --filter @h-ai/vecdb test
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> pgvector / Qdrant tests require Docker.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
Apache-2.0
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @h-ai/vecdb
|
|
2
|
+
|
|
3
|
+
[简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
[フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/core](../core/README.ja.md)
|
|
6
|
+
|
|
7
|
+
共通の `vecdb` オブジェクトで LanceDB、pgvector、Qdrant、Chroma を操作するベクトルデータベースモジュールです。
|
|
8
|
+
|
|
9
|
+
## 対応ベクトルデータベース
|
|
10
|
+
|
|
11
|
+
- LanceDB(組み込み型、ローカルファイル保存、設定不要)
|
|
12
|
+
- pgvector(PostgreSQL + pgvector 拡張)
|
|
13
|
+
- Qdrant(高性能ベクトル検索エンジン。Query API を使用するため、サーバー 1.10.0 以降が必要)
|
|
14
|
+
- Chroma(組み込みモードでローカルサービスを自動起動、または既存サービスへ直接接続)
|
|
15
|
+
|
|
16
|
+
## クイックスタート
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { HaiVecdbError, vecdb } from '@h-ai/vecdb'
|
|
20
|
+
|
|
21
|
+
// 初始化(LanceDB)
|
|
22
|
+
await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
|
|
23
|
+
|
|
24
|
+
// 创建集合
|
|
25
|
+
await vecdb.collection.create('docs', { dimension: 1536 })
|
|
26
|
+
|
|
27
|
+
// 插入向量
|
|
28
|
+
await vecdb.vector.insert('docs', [
|
|
29
|
+
{ id: 'doc-1', vector: Array.from({ length: 1536 }).fill(0.1), content: '文档内容', metadata: { source: 'wiki' } },
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
// 搜索
|
|
33
|
+
const searchResult = await vecdb.vector.search('docs', Array.from({ length: 1536 }).fill(0.2), { topK: 5, minScore: 0.7 })
|
|
34
|
+
if (searchResult.success) {
|
|
35
|
+
for (const item of searchResult.data) {
|
|
36
|
+
// item.id, item.score, item.content
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 关闭
|
|
41
|
+
const closeResult = await vecdb.close()
|
|
42
|
+
if (!closeResult.success) {
|
|
43
|
+
throw new Error(closeResult.error.message)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 設定
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// LanceDB(默认,嵌入式本地存储)
|
|
51
|
+
await vecdb.init({ type: 'lancedb', path: './data/vecdb' })
|
|
52
|
+
|
|
53
|
+
// pgvector(连接字符串)
|
|
54
|
+
await vecdb.init({ type: 'pgvector', url: 'postgres://user:pass@localhost:5432/mydb' })
|
|
55
|
+
|
|
56
|
+
// pgvector(分字段)
|
|
57
|
+
await vecdb.init({
|
|
58
|
+
type: 'pgvector',
|
|
59
|
+
host: 'localhost',
|
|
60
|
+
port: 5432,
|
|
61
|
+
database: 'mydb',
|
|
62
|
+
user: 'admin',
|
|
63
|
+
password: 'secret',
|
|
64
|
+
indexType: 'hnsw',
|
|
65
|
+
tablePrefix: 'vec_',
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Qdrant
|
|
69
|
+
await vecdb.init({ type: 'qdrant', url: 'http://localhost:6333', apiKey: 'optional-key' })
|
|
70
|
+
|
|
71
|
+
// Chroma(嵌入式:自动拉起本地 `chroma run` 服务并持久化到 path)
|
|
72
|
+
await vecdb.init({ type: 'chroma', path: './data/chroma' })
|
|
73
|
+
|
|
74
|
+
// Chroma(直连已有服务,不拉起进程)
|
|
75
|
+
await vecdb.init({ type: 'chroma', url: 'http://localhost:8000' })
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> Node 版の Chroma は HTTP クライアントのみで、プロセス内の組み込み DB はありません。組み込みモードでは `vecdb.init` が
|
|
79
|
+
> `serverCommand`(既定は `chromadb` パッケージの `chroma`)でローカルサービスを起動し、`vecdb.close` が
|
|
80
|
+
> そのプロセスを停止します。任意依存の `chromadb` をインストールしてください。コマンドが利用できない場合、`init` は `CONNECTION_FAILED` を返します。
|
|
81
|
+
|
|
82
|
+
### 操作ログ
|
|
83
|
+
|
|
84
|
+
ベクトルデータベース設定の `operationLog` で操作ログを有効にできます。ログはコレクション・ベクトル操作が実際の Provider に渡る前に出力され、`vecdb-main.ts` で一括ラップはしません。
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
# config/_vecdb.yml
|
|
88
|
+
type: lancedb
|
|
89
|
+
path: ./data/vecdb
|
|
90
|
+
operationLog:
|
|
91
|
+
read: false # collection.exists/info/list, vector.search/count
|
|
92
|
+
write: false # collection.create/drop, vector.insert/upsert/delete
|
|
93
|
+
maxLength: 1000 # 参数序列化后的最大输出长度,超出会截断
|
|
94
|
+
level: debug # info | debug | trace,默认 debug
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
await vecdb.init({
|
|
99
|
+
type: 'lancedb',
|
|
100
|
+
path: './data/vecdb',
|
|
101
|
+
operationLog: {
|
|
102
|
+
read: true,
|
|
103
|
+
write: true,
|
|
104
|
+
maxLength: 500,
|
|
105
|
+
level: 'debug',
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> 注:`vecdb.config` は**機密情報をマスクした設定スナップショット**を返します。接続文字列のユーザー名・パスワード、個別の `password`、`apiKey` は `[REDACTED]` に置換されます。
|
|
111
|
+
|
|
112
|
+
> 動作:空の一括 `insert/upsert/delete` は何もしません。書き込み・検索時にはベクトル次元を検証し、不一致なら `HaiVecdbError.DIMENSION_MISMATCH`、任意ドライバーの不足なら `HaiVecdbError.DRIVER_NOT_FOUND` を返します。
|
|
113
|
+
|
|
114
|
+
## エラー処理
|
|
115
|
+
|
|
116
|
+
すべての操作は `HaiResult<T>` を返し、`result.success` で成功・失敗を判定します。
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const result = await vecdb.collection.create('docs', { dimension: 1536 })
|
|
120
|
+
if (!result.success) {
|
|
121
|
+
switch (result.error.code) {
|
|
122
|
+
case HaiVecdbError.NOT_INITIALIZED.code:
|
|
123
|
+
// 请先调用 vecdb.init()
|
|
124
|
+
break
|
|
125
|
+
case HaiVecdbError.COLLECTION_ALREADY_EXISTS.code:
|
|
126
|
+
// 集合已存在
|
|
127
|
+
break
|
|
128
|
+
case HaiVecdbError.CONNECTION_FAILED.code:
|
|
129
|
+
// 连接失败
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## テスト
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pnpm --filter @h-ai/vecdb test
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> pgvector / Qdrant のテストには Docker が必要です。
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
Apache-2.0
|
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# @h-ai/vecdb
|
|
2
2
|
|
|
3
|
+
[简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
[框架首页](../../README.md) · 相关模块:[@h-ai/core](../core/README.md)
|
|
6
|
+
|
|
3
7
|
向量数据库模块,通过统一的 `vecdb` 对象访问 LanceDB、pgvector、Qdrant、Chroma。
|
|
4
8
|
|
|
5
9
|
## 支持的向量数据库
|
|
6
10
|
|
|
7
11
|
- LanceDB(嵌入式,本地文件存储,零配置)
|
|
8
12
|
- pgvector(PostgreSQL + pgvector 扩展)
|
|
9
|
-
- Qdrant
|
|
13
|
+
- Qdrant(高性能向量搜索引擎;服务端需 1.10.0+,使用 Query API)
|
|
10
14
|
- Chroma(支持嵌入式自动拉起本地服务,或直连已有服务)
|
|
11
15
|
|
|
12
16
|
## 快速开始
|
package/dist/index.js
CHANGED
|
@@ -619,6 +619,9 @@ var logger2 = core.logger.child({ module: "vecdb", scope: "lancedb" });
|
|
|
619
619
|
function escapeLanceFilterValue(value) {
|
|
620
620
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
621
621
|
}
|
|
622
|
+
function escapeLanceSqlValue(value) {
|
|
623
|
+
return value.replace(/'/g, "''");
|
|
624
|
+
}
|
|
622
625
|
function createLancedbProvider() {
|
|
623
626
|
let connection = null;
|
|
624
627
|
let config = null;
|
|
@@ -692,7 +695,7 @@ function createLancedbProvider() {
|
|
|
692
695
|
metadata: "{}"
|
|
693
696
|
};
|
|
694
697
|
const table = await connection.createTable(name, [initRecord]);
|
|
695
|
-
await table.delete(
|
|
698
|
+
await table.delete("id = '__init__'");
|
|
696
699
|
collectionMetas.set(name, { dimension, metric });
|
|
697
700
|
logger2.info("Collection created", { name, dimension, metric });
|
|
698
701
|
return ok(void 0);
|
|
@@ -775,7 +778,7 @@ function createLancedbProvider() {
|
|
|
775
778
|
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(records);
|
|
776
779
|
} else {
|
|
777
780
|
logger2.warn("LanceDB mergeInsert is unavailable, falling back to delete/add upsert");
|
|
778
|
-
const ids = documents.map((d) => `
|
|
781
|
+
const ids = documents.map((d) => `'${escapeLanceSqlValue(d.id)}'`).join(", ");
|
|
779
782
|
await table.delete(`id IN (${ids})`);
|
|
780
783
|
await table.add(records);
|
|
781
784
|
}
|
|
@@ -788,7 +791,7 @@ function createLancedbProvider() {
|
|
|
788
791
|
if (!table) {
|
|
789
792
|
return err(HaiVecdbError.COLLECTION_NOT_FOUND, vecdbM("vecdb_collectionNotFound", { params: { name: collection } }));
|
|
790
793
|
}
|
|
791
|
-
const idList = ids.map((id) => `
|
|
794
|
+
const idList = ids.map((id) => `'${escapeLanceSqlValue(id)}'`).join(", ");
|
|
792
795
|
await table.delete(`id IN (${idList})`);
|
|
793
796
|
logger2.info("Vectors deleted", { collection, count: ids.length });
|
|
794
797
|
return ok(void 0);
|
|
@@ -1494,14 +1497,14 @@ function createQdrantProvider() {
|
|
|
1494
1497
|
}));
|
|
1495
1498
|
filter = { must: mustConditions };
|
|
1496
1499
|
}
|
|
1497
|
-
const searchResult = await client.
|
|
1498
|
-
vector,
|
|
1500
|
+
const searchResult = await client.query(collection, {
|
|
1501
|
+
query: vector,
|
|
1499
1502
|
limit: topK,
|
|
1500
1503
|
score_threshold: minScore > 0 ? minScore : void 0,
|
|
1501
1504
|
filter,
|
|
1502
1505
|
with_payload: true
|
|
1503
1506
|
});
|
|
1504
|
-
const results = searchResult.map((point) => {
|
|
1507
|
+
const results = searchResult.points.map((point) => {
|
|
1505
1508
|
const payload = point.payload ?? {};
|
|
1506
1509
|
const { _id, _content, ...metadata } = payload;
|
|
1507
1510
|
return {
|