@memberjunction/ai-vectordb 5.21.0 → 5.23.0
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.md +359 -137
- package/dist/generic/MetadataFilter.d.ts +78 -0
- package/dist/generic/MetadataFilter.d.ts.map +1 -0
- package/dist/generic/MetadataFilter.js +107 -0
- package/dist/generic/MetadataFilter.js.map +1 -0
- package/dist/generic/VectorDBBase.d.ts +67 -17
- package/dist/generic/VectorDBBase.d.ts.map +1 -1
- package/dist/generic/VectorDBBase.js +50 -1
- package/dist/generic/VectorDBBase.js.map +1 -1
- package/dist/generic/configuration.types.d.ts +175 -0
- package/dist/generic/configuration.types.d.ts.map +1 -0
- package/dist/generic/configuration.types.js +9 -0
- package/dist/generic/configuration.types.js.map +1 -0
- package/dist/generic/query.types.d.ts +14 -0
- package/dist/generic/query.types.d.ts.map +1 -1
- package/dist/generic/record.d.ts +26 -0
- package/dist/generic/record.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @memberjunction/ai-vectordb
|
|
2
2
|
|
|
3
|
-
A provider-agnostic abstraction layer for vector databases in MemberJunction. This package defines the abstract
|
|
3
|
+
A provider-agnostic abstraction layer for vector databases in MemberJunction. This package defines the `VectorDBBase` abstract class and all associated types that concrete vector database implementations must fulfill. It contains no provider-specific logic -- only the contracts, data structures, and query interfaces shared across all implementations.
|
|
4
4
|
|
|
5
5
|
## Architecture
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ graph TD
|
|
|
9
9
|
subgraph Abstraction["@memberjunction/ai-vectordb"]
|
|
10
10
|
VDBB["VectorDBBase (abstract)"]
|
|
11
11
|
VR["VectorRecord"]
|
|
12
|
-
QO["QueryOptions"]
|
|
12
|
+
QO["QueryOptions / HybridQueryOptions"]
|
|
13
13
|
QR["QueryResponse / ScoredRecord"]
|
|
14
14
|
IDX["IndexDescription / IndexList"]
|
|
15
15
|
BR["BaseResponse"]
|
|
@@ -37,29 +37,47 @@ graph TD
|
|
|
37
37
|
style Consumers fill:#7c5295,stroke:#563a6b,color:#fff
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
All vector database providers extend `VectorDBBase` and implement its abstract methods. Consumer code programs against the base class, making it straightforward to swap providers without changing application logic.
|
|
41
|
+
|
|
40
42
|
## Installation
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
45
|
npm install @memberjunction/ai-vectordb
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
##
|
|
47
|
-
|
|
48
|
-
This package provides:
|
|
48
|
+
## Quick Start
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- **Query types** -- `QueryOptions`, `QueryResponse`, `ScoredRecord` for similarity search
|
|
53
|
-
- **Index types** -- `IndexDescription`, `IndexList`, `IndexModelMetricEnum` for index configuration
|
|
54
|
-
- **Response types** -- `BaseResponse` for standardized success/failure responses
|
|
50
|
+
```typescript
|
|
51
|
+
import { VectorDBBase, VectorRecord } from '@memberjunction/ai-vectordb';
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
// Use any concrete provider that extends VectorDBBase
|
|
54
|
+
const db: VectorDBBase = new PineconeDatabase(apiKey);
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
// Insert a vector record
|
|
57
|
+
const record: VectorRecord = {
|
|
58
|
+
id: 'doc-001',
|
|
59
|
+
values: [0.1, 0.2, 0.3 /* ... */],
|
|
60
|
+
metadata: { entity: 'Products', recordId: '12345' }
|
|
61
|
+
};
|
|
62
|
+
await db.createRecord(record);
|
|
63
|
+
|
|
64
|
+
// Query for similar vectors
|
|
65
|
+
const result = await db.queryIndex({
|
|
66
|
+
vector: [0.1, 0.2, 0.3 /* ... */],
|
|
67
|
+
topK: 10,
|
|
68
|
+
includeMetadata: true
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (result.success) {
|
|
72
|
+
for (const match of result.data.matches) {
|
|
73
|
+
console.log(`Match: ${match.id} (score: ${match.score})`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
59
77
|
|
|
60
|
-
|
|
78
|
+
## VectorDBBase Abstract Class
|
|
61
79
|
|
|
62
|
-
All vector database providers must extend this class. The constructor requires an API key, which is validated and stored for subclass access via a protected getter.
|
|
80
|
+
All vector database providers must extend this class. The constructor requires an API key, which is validated (must be non-empty) and stored for subclass access via a protected getter.
|
|
63
81
|
|
|
64
82
|
```mermaid
|
|
65
83
|
classDiagram
|
|
@@ -67,12 +85,14 @@ classDiagram
|
|
|
67
85
|
<<abstract>>
|
|
68
86
|
#apiKey : string
|
|
69
87
|
+constructor(apiKey: string)
|
|
88
|
+
+SupportsHybridSearch : boolean
|
|
70
89
|
+listIndexes()* IndexList
|
|
71
90
|
+getIndex(params)* BaseResponse
|
|
72
91
|
+createIndex(params)* BaseResponse
|
|
73
92
|
+deleteIndex(params)* BaseResponse
|
|
74
93
|
+editIndex(params)* BaseResponse
|
|
75
94
|
+queryIndex(params)* BaseResponse
|
|
95
|
+
+HybridQuery(params) BaseResponse
|
|
76
96
|
+createRecord(record)* BaseResponse
|
|
77
97
|
+createRecords(records)* BaseResponse
|
|
78
98
|
+getRecord(params)* BaseResponse
|
|
@@ -88,213 +108,415 @@ classDiagram
|
|
|
88
108
|
+queryIndex(params) BaseResponse
|
|
89
109
|
}
|
|
90
110
|
|
|
111
|
+
class CustomProvider {
|
|
112
|
+
+SupportsHybridSearch : boolean
|
|
113
|
+
+HybridQuery(params) BaseResponse
|
|
114
|
+
}
|
|
115
|
+
|
|
91
116
|
VectorDBBase <|-- PineconeDatabase
|
|
117
|
+
VectorDBBase <|-- CustomProvider
|
|
92
118
|
|
|
93
119
|
style VectorDBBase fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
94
120
|
style PineconeDatabase fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
121
|
+
style CustomProvider fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
95
122
|
```
|
|
96
123
|
|
|
97
|
-
|
|
124
|
+
All abstract methods use union return types (`BaseResponse | Promise<BaseResponse>`) so subclasses can implement them as either synchronous or asynchronous.
|
|
98
125
|
|
|
99
|
-
|
|
126
|
+
## Abstract Methods Reference
|
|
100
127
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
// Core vector record with generic metadata support
|
|
105
|
-
type VectorRecord<T extends RecordMetadata = RecordMetadata> = {
|
|
106
|
-
id: string; // Unique record identifier
|
|
107
|
-
values: RecordValues; // Dense vector (array of numbers)
|
|
108
|
-
sparseValues?: RecordSparseValues; // Optional sparse representation for hybrid search
|
|
109
|
-
metadata?: T; // Arbitrary filterable metadata
|
|
110
|
-
};
|
|
128
|
+
### Index Operations
|
|
111
129
|
|
|
112
|
-
|
|
130
|
+
| Method | Parameters | Returns | Description |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `listIndexes()` | none | `IndexList` | List all indexes in the project. |
|
|
133
|
+
| `getIndex(params)` | `BaseRequestParams` | `BaseResponse` | Retrieve configuration details for a single index by ID. |
|
|
134
|
+
| `createIndex(params)` | `CreateIndexParams` | `BaseResponse` | Create a new vector index with specified dimension, metric, and optional provider-specific params. |
|
|
135
|
+
| `deleteIndex(params)` | `BaseRequestParams` | `BaseResponse` | Permanently delete an index and all its records. |
|
|
136
|
+
| `editIndex(params)` | `EditIndexParams` | `BaseResponse` | Modify an existing index's configuration. |
|
|
137
|
+
| `queryIndex(params)` | `QueryOptions` | `BaseResponse` | Execute a similarity search against an index by vector values or by record ID. |
|
|
113
138
|
|
|
114
|
-
|
|
115
|
-
indices: Array<number>; // Non-zero positions
|
|
116
|
-
values: Array<number>; // Corresponding values
|
|
117
|
-
};
|
|
139
|
+
### Record Operations
|
|
118
140
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
141
|
+
| Method | Parameters | Returns | Description |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| `createRecord(record)` | `VectorRecord` | `BaseResponse` | Insert a single vector record into an index. |
|
|
144
|
+
| `createRecords(records)` | `VectorRecord[]` | `BaseResponse` | Insert multiple vector records in batch. |
|
|
145
|
+
| `getRecord(params)` | `BaseRequestParams` | `BaseResponse` | Fetch a single record by ID. |
|
|
146
|
+
| `getRecords(params)` | `BaseRequestParams` | `BaseResponse` | Fetch multiple records (IDs passed via `data`). |
|
|
147
|
+
| `updateRecord(record)` | `UpdateOptions` | `BaseResponse` | Update a single record's vector values and/or metadata. |
|
|
148
|
+
| `updateRecords(records)` | `UpdateOptions` | `BaseResponse` | Update multiple records in batch. |
|
|
149
|
+
| `deleteRecord(record)` | `VectorRecord` | `BaseResponse` | Delete a single record from an index. |
|
|
150
|
+
| `deleteRecords(records)` | `VectorRecord[]` | `BaseResponse` | Delete multiple records in batch. |
|
|
122
151
|
|
|
123
|
-
|
|
152
|
+
### Virtual Methods (Non-Abstract)
|
|
124
153
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
metric: IndexModelMetricEnum; // 'cosine' | 'euclidean' | 'dotproduct'
|
|
130
|
-
host: string; // Hosting URL
|
|
131
|
-
};
|
|
132
|
-
```
|
|
154
|
+
| Method | Parameters | Returns | Description |
|
|
155
|
+
|---|---|---|---|
|
|
156
|
+
| `SupportsHybridSearch` (getter) | none | `boolean` | Returns `false` by default. Override to return `true` in providers that implement `HybridQuery()`. |
|
|
157
|
+
| `HybridQuery(params)` | `HybridQueryOptions` | `BaseResponse` | Perform a hybrid search combining vector similarity with keyword matching. Default implementation falls back to `queryIndex()`. |
|
|
133
158
|
|
|
134
|
-
|
|
159
|
+
## Hybrid Search
|
|
135
160
|
|
|
136
|
-
|
|
137
|
-
graph LR
|
|
138
|
-
QPB["QueryParamsBase<br/>topK, includeValues,<br/>includeMetadata, filter"]
|
|
139
|
-
QBV["QueryByVectorValues<br/>+ vector"]
|
|
140
|
-
QBID["QueryByRecordId<br/>+ id"]
|
|
141
|
-
QO["QueryOptions"]
|
|
161
|
+
Hybrid search combines dense vector similarity (semantic search) with sparse keyword matching (BM25) to produce more relevant results than either technique alone. This is particularly useful when queries contain specific terms (product names, codes, identifiers) that benefit from exact text matching alongside semantic understanding.
|
|
142
162
|
|
|
143
|
-
|
|
144
|
-
QPB --> QBID
|
|
145
|
-
QBV --> QO
|
|
146
|
-
QBID --> QO
|
|
163
|
+
### How It Works
|
|
147
164
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
style QO fill:#b8762f,stroke:#8a5722,color:#fff
|
|
152
|
-
```
|
|
165
|
+
1. **Check support**: Read the `SupportsHybridSearch` getter before calling `HybridQuery()`.
|
|
166
|
+
2. **Call HybridQuery**: Pass both a vector embedding and an optional keyword query string.
|
|
167
|
+
3. **Fallback behavior**: If the provider does not override `HybridQuery()`, the base class automatically falls back to a pure vector `queryIndex()` call, ignoring keyword-specific parameters (`KeywordQuery`, `Alpha`, `FusionMethod`). This means consumer code does not need to branch on provider capability -- it is always safe to call `HybridQuery()`.
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
// Base query parameters shared by all query types
|
|
156
|
-
type QueryParamsBase = {
|
|
157
|
-
topK: number; // Number of results to return
|
|
158
|
-
includeValues?: boolean; // Include vector values in results
|
|
159
|
-
includeMetadata?: boolean; // Include metadata in results
|
|
160
|
-
filter?: object; // Metadata filter
|
|
161
|
-
};
|
|
169
|
+
### HybridQueryOptions
|
|
162
170
|
|
|
163
|
-
|
|
164
|
-
type QueryByVectorValues = QueryParamsBase & { vector: RecordValues };
|
|
171
|
+
`HybridQueryOptions` extends `QueryParamsBase` with the following additional fields:
|
|
165
172
|
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
| Property | Type | Required | Description |
|
|
174
|
+
|---|---|---|---|
|
|
175
|
+
| `vector` | `RecordValues` | Yes | Embedding vector for the semantic similarity component. |
|
|
176
|
+
| `KeywordQuery` | `string` | No | Keyword query string for BM25/text search. |
|
|
177
|
+
| `Alpha` | `number` | No | Balance between vector and keyword results. `0.0` = pure keyword, `1.0` = pure vector. Default: `0.7`. |
|
|
178
|
+
| `FusionMethod` | `'rrf' \| 'weighted'` | No | Strategy for fusing vector and keyword result lists. Default: `'rrf'` (Reciprocal Rank Fusion). |
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
type QueryOptions = QueryByRecordId | QueryByVectorValues;
|
|
171
|
-
```
|
|
180
|
+
Plus the inherited `QueryParamsBase` fields: `topK`, `includeValues`, `includeMetadata`, `filter`.
|
|
172
181
|
|
|
173
|
-
|
|
182
|
+
### Implementing Hybrid Search in a Provider
|
|
174
183
|
|
|
175
184
|
```typescript
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
import { VectorDBBase, HybridQueryOptions, BaseResponse } from '@memberjunction/ai-vectordb';
|
|
186
|
+
|
|
187
|
+
export class WeaviateDB extends VectorDBBase {
|
|
188
|
+
public override get SupportsHybridSearch(): boolean {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public override async HybridQuery(params: HybridQueryOptions): Promise<BaseResponse> {
|
|
193
|
+
const results = await this.client.hybridSearch({
|
|
194
|
+
vector: params.vector,
|
|
195
|
+
query: params.KeywordQuery,
|
|
196
|
+
alpha: params.Alpha ?? 0.7,
|
|
197
|
+
fusionType: params.FusionMethod ?? 'rrf',
|
|
198
|
+
limit: params.topK,
|
|
199
|
+
includeMetadata: params.includeMetadata
|
|
200
|
+
});
|
|
201
|
+
return { success: true, message: 'Hybrid query complete', data: results };
|
|
202
|
+
}
|
|
181
203
|
|
|
182
|
-
|
|
183
|
-
score?: number; // Similarity score (interpretation depends on metric)
|
|
204
|
+
// ... remaining abstract method implementations
|
|
184
205
|
}
|
|
185
206
|
```
|
|
186
207
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
All operations return a `BaseResponse`:
|
|
208
|
+
### Consumer Usage
|
|
190
209
|
|
|
191
210
|
```typescript
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
211
|
+
const db: VectorDBBase = getVectorProvider();
|
|
212
|
+
|
|
213
|
+
// Safe to call regardless of provider -- falls back to pure vector search
|
|
214
|
+
const results = await db.HybridQuery({
|
|
215
|
+
vector: embedding,
|
|
216
|
+
KeywordQuery: 'quarterly revenue report',
|
|
217
|
+
Alpha: 0.6,
|
|
218
|
+
topK: 20,
|
|
219
|
+
includeMetadata: true
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Or check support for conditional UI behavior
|
|
223
|
+
if (db.SupportsHybridSearch) {
|
|
224
|
+
showKeywordSearchInput();
|
|
225
|
+
}
|
|
197
226
|
```
|
|
198
227
|
|
|
199
|
-
##
|
|
228
|
+
## Implementing a Custom Provider
|
|
200
229
|
|
|
201
|
-
|
|
230
|
+
Follow these steps to add support for a new vector database (e.g., Weaviate, Qdrant, Milvus).
|
|
231
|
+
|
|
232
|
+
### Step 1: Create the Provider Class
|
|
202
233
|
|
|
203
234
|
```typescript
|
|
204
235
|
import {
|
|
205
236
|
VectorDBBase,
|
|
206
|
-
|
|
237
|
+
BaseRequestParams,
|
|
207
238
|
BaseResponse,
|
|
208
239
|
CreateIndexParams,
|
|
240
|
+
EditIndexParams,
|
|
241
|
+
IndexList,
|
|
209
242
|
QueryOptions,
|
|
210
|
-
|
|
243
|
+
UpdateOptions,
|
|
244
|
+
VectorRecord
|
|
211
245
|
} from '@memberjunction/ai-vectordb';
|
|
212
246
|
|
|
213
|
-
export class
|
|
214
|
-
|
|
215
|
-
|
|
247
|
+
export class QdrantDB extends VectorDBBase {
|
|
248
|
+
private client: QdrantClient;
|
|
249
|
+
|
|
250
|
+
constructor(apiKey: string, host: string) {
|
|
251
|
+
super(apiKey); // Validates the API key
|
|
252
|
+
this.client = new QdrantClient({ apiKey: this.apiKey, host });
|
|
216
253
|
}
|
|
217
254
|
|
|
218
255
|
async listIndexes(): Promise<IndexList> {
|
|
219
|
-
|
|
220
|
-
return {
|
|
256
|
+
const collections = await this.client.getCollections();
|
|
257
|
+
return {
|
|
258
|
+
indexes: collections.map(c => ({
|
|
259
|
+
name: c.name,
|
|
260
|
+
dimension: c.vectors.size,
|
|
261
|
+
metric: c.vectors.distance,
|
|
262
|
+
host: this.host
|
|
263
|
+
}))
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getIndex(params: BaseRequestParams): Promise<BaseResponse> {
|
|
268
|
+
const info = await this.client.getCollection(params.id);
|
|
269
|
+
return { success: true, message: 'Index retrieved', data: info };
|
|
221
270
|
}
|
|
222
271
|
|
|
223
272
|
async createIndex(params: CreateIndexParams): Promise<BaseResponse> {
|
|
224
|
-
|
|
273
|
+
await this.client.createCollection(params.id, {
|
|
274
|
+
vectors: { size: params.dimension, distance: params.metric }
|
|
275
|
+
});
|
|
276
|
+
return { success: true, message: 'Index created', data: null };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async deleteIndex(params: BaseRequestParams): Promise<BaseResponse> {
|
|
280
|
+
await this.client.deleteCollection(params.id);
|
|
281
|
+
return { success: true, message: 'Index deleted', data: null };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async editIndex(params: EditIndexParams): Promise<BaseResponse> {
|
|
285
|
+
await this.client.updateCollection(params.id, params.data);
|
|
286
|
+
return { success: true, message: 'Index updated', data: null };
|
|
225
287
|
}
|
|
226
288
|
|
|
227
289
|
async queryIndex(params: QueryOptions): Promise<BaseResponse> {
|
|
228
|
-
|
|
229
|
-
return { success: true, message: '
|
|
290
|
+
const results = await this.client.search(params);
|
|
291
|
+
return { success: true, message: 'Query complete', data: results };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async createRecord(record: VectorRecord): Promise<BaseResponse> {
|
|
295
|
+
await this.client.upsert([record]);
|
|
296
|
+
return { success: true, message: 'Record created', data: null };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async createRecords(records: VectorRecord[]): Promise<BaseResponse> {
|
|
300
|
+
await this.client.upsert(records);
|
|
301
|
+
return { success: true, message: `${records.length} records created`, data: null };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async getRecord(params: BaseRequestParams): Promise<BaseResponse> {
|
|
305
|
+
const record = await this.client.retrieve(params.id);
|
|
306
|
+
return { success: true, message: 'Record retrieved', data: record };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async getRecords(params: BaseRequestParams): Promise<BaseResponse> {
|
|
310
|
+
const records = await this.client.retrieveMany(params.data);
|
|
311
|
+
return { success: true, message: 'Records retrieved', data: records };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async updateRecord(record: UpdateOptions): Promise<BaseResponse> {
|
|
315
|
+
await this.client.update(record);
|
|
316
|
+
return { success: true, message: 'Record updated', data: null };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async updateRecords(records: UpdateOptions): Promise<BaseResponse> {
|
|
320
|
+
await this.client.updateMany(records);
|
|
321
|
+
return { success: true, message: 'Records updated', data: null };
|
|
230
322
|
}
|
|
231
323
|
|
|
232
|
-
|
|
324
|
+
async deleteRecord(record: VectorRecord): Promise<BaseResponse> {
|
|
325
|
+
await this.client.delete(record.id);
|
|
326
|
+
return { success: true, message: 'Record deleted', data: null };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async deleteRecords(records: VectorRecord[]): Promise<BaseResponse> {
|
|
330
|
+
await this.client.deleteMany(records.map(r => r.id));
|
|
331
|
+
return { success: true, message: 'Records deleted', data: null };
|
|
332
|
+
}
|
|
233
333
|
}
|
|
234
334
|
```
|
|
235
335
|
|
|
236
|
-
###
|
|
336
|
+
### Step 2: Register with MemberJunction Class Factory (Optional)
|
|
337
|
+
|
|
338
|
+
If you want MemberJunction's class factory to discover your provider automatically:
|
|
237
339
|
|
|
238
340
|
```typescript
|
|
239
|
-
import {
|
|
341
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
342
|
+
|
|
343
|
+
@RegisterClass(VectorDBBase, 'QdrantDB')
|
|
344
|
+
export class QdrantDB extends VectorDBBase {
|
|
345
|
+
// ... implementation
|
|
346
|
+
}
|
|
347
|
+
```
|
|
240
348
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
349
|
+
### Step 3: Add Hybrid Search Support (Optional)
|
|
350
|
+
|
|
351
|
+
If your vector database supports hybrid search natively, override the getter and method:
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
export class QdrantDB extends VectorDBBase {
|
|
355
|
+
public override get SupportsHybridSearch(): boolean {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
public override async HybridQuery(params: HybridQueryOptions): Promise<BaseResponse> {
|
|
360
|
+
// Use your provider's native hybrid search API
|
|
361
|
+
const results = await this.client.hybridSearch({
|
|
362
|
+
vector: params.vector,
|
|
363
|
+
keyword: params.KeywordQuery,
|
|
364
|
+
alpha: params.Alpha ?? 0.7,
|
|
365
|
+
limit: params.topK
|
|
366
|
+
});
|
|
367
|
+
return { success: true, message: 'Hybrid query complete', data: results };
|
|
261
368
|
}
|
|
262
369
|
}
|
|
263
370
|
```
|
|
264
371
|
|
|
372
|
+
## QueryOptions Reference
|
|
373
|
+
|
|
374
|
+
`QueryOptions` is a union type supporting two query strategies:
|
|
375
|
+
|
|
376
|
+
```mermaid
|
|
377
|
+
graph LR
|
|
378
|
+
QPB["QueryParamsBase<br/>topK, includeValues,<br/>includeMetadata, filter"]
|
|
379
|
+
QBV["QueryByVectorValues<br/>+ vector"]
|
|
380
|
+
QBID["QueryByRecordId<br/>+ id"]
|
|
381
|
+
QO["QueryOptions"]
|
|
382
|
+
|
|
383
|
+
QPB --> QBV
|
|
384
|
+
QPB --> QBID
|
|
385
|
+
QBV --> QO
|
|
386
|
+
QBID --> QO
|
|
387
|
+
|
|
388
|
+
style QPB fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
389
|
+
style QBV fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
390
|
+
style QBID fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
391
|
+
style QO fill:#b8762f,stroke:#8a5722,color:#fff
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### QueryParamsBase
|
|
395
|
+
|
|
396
|
+
Shared query parameters inherited by both query strategies.
|
|
397
|
+
|
|
398
|
+
| Property | Type | Required | Description |
|
|
399
|
+
|---|---|---|---|
|
|
400
|
+
| `topK` | `number` | Yes | Number of results to return. |
|
|
401
|
+
| `includeValues` | `boolean` | No | Whether to include embedding vectors in results. Default: `false`. |
|
|
402
|
+
| `includeMetadata` | `boolean` | No | Whether to include metadata in results. Default: `false`. |
|
|
403
|
+
| `filter` | `object` | No | Metadata filter to narrow the search scope. |
|
|
404
|
+
|
|
405
|
+
### QueryByVectorValues
|
|
406
|
+
|
|
407
|
+
Query by providing a vector directly. Extends `QueryParamsBase`.
|
|
408
|
+
|
|
409
|
+
| Property | Type | Required | Description |
|
|
410
|
+
|---|---|---|---|
|
|
411
|
+
| `vector` | `RecordValues` | Yes | Vector values output from an embedding model. |
|
|
412
|
+
|
|
413
|
+
### QueryByRecordId
|
|
414
|
+
|
|
415
|
+
Query using an existing record's stored vector. Extends `QueryParamsBase`.
|
|
416
|
+
|
|
417
|
+
| Property | Type | Required | Description |
|
|
418
|
+
|---|---|---|---|
|
|
419
|
+
| `id` | `string` | Yes | ID of an existing record whose vector to use for the query. |
|
|
420
|
+
|
|
421
|
+
### Usage Examples
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// Query by vector values
|
|
425
|
+
const resultByVector = await db.queryIndex({
|
|
426
|
+
vector: [0.1, 0.2, 0.3],
|
|
427
|
+
topK: 5,
|
|
428
|
+
includeMetadata: true,
|
|
429
|
+
filter: { category: 'finance' }
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Query by record ID (find similar to an existing record)
|
|
433
|
+
const resultById = await db.queryIndex({
|
|
434
|
+
id: 'doc-001',
|
|
435
|
+
topK: 10,
|
|
436
|
+
includeMetadata: true
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Type Reference
|
|
441
|
+
|
|
442
|
+
### Core Types
|
|
443
|
+
|
|
444
|
+
| Type | Description |
|
|
445
|
+
|---|---|
|
|
446
|
+
| `VectorRecord<T>` | A vector record with `id`, `values` (dense embedding), optional `sparseValues`, and optional `metadata`. Generic over metadata type. |
|
|
447
|
+
| `RecordValues` | `Array<number>` -- a dense embedding vector. |
|
|
448
|
+
| `RecordSparseValues` | Sparse vector representation with parallel `indices` and `values` arrays for non-zero positions. |
|
|
449
|
+
| `RecordMetadata` | `Record<string, RecordMetadataValue>` -- arbitrary key-value metadata attached to records. |
|
|
450
|
+
| `RecordMetadataValue` | `string \| boolean \| number \| Array<string>` -- valid metadata value types. |
|
|
451
|
+
| `BaseResponse` | Standard response envelope: `success` (boolean), `message` (string), `data` (result payload). |
|
|
452
|
+
|
|
453
|
+
### Index Types
|
|
454
|
+
|
|
455
|
+
| Type | Description |
|
|
456
|
+
|---|---|
|
|
457
|
+
| `IndexDescription` | Describes an index: `name` (max 45 chars), `dimension`, `metric`, and `host` URL. |
|
|
458
|
+
| `IndexList` | Container with an optional `indexes` array of `IndexDescription`. |
|
|
459
|
+
| `IndexModelMetricEnum` | `'cosine' \| 'euclidean' \| 'dotproduct'` distance metrics for similarity comparison. |
|
|
460
|
+
|
|
461
|
+
### Request/Options Types
|
|
462
|
+
|
|
463
|
+
| Type | Description |
|
|
464
|
+
|---|---|
|
|
465
|
+
| `BaseRequestParams` | Base request with `id` (string) and optional `data`. |
|
|
466
|
+
| `CreateIndexParams` | Extends `BaseRequestParams` with `dimension` (number), `metric` (IndexModelMetricEnum), and optional `additionalParams`. |
|
|
467
|
+
| `EditIndexParams` | Extends `BaseRequestParams` for index configuration changes. |
|
|
468
|
+
| `UpdateOptions<T>` | Record update with `id`, optional `values`, optional `sparseValues`, and optional `metadata`. Values are optional to support metadata-only updates. |
|
|
469
|
+
|
|
470
|
+
### Query Types
|
|
471
|
+
|
|
472
|
+
| Type | Description |
|
|
473
|
+
|---|---|
|
|
474
|
+
| `QueryParamsBase` | Shared query parameters: `topK`, `includeValues`, `includeMetadata`, `filter`. |
|
|
475
|
+
| `QueryByRecordId` | Extends `QueryParamsBase` with `id` -- query using an existing record's vector. |
|
|
476
|
+
| `QueryByVectorValues` | Extends `QueryParamsBase` with `vector` -- query using a raw embedding array. |
|
|
477
|
+
| `QueryOptions` | Union of `QueryByRecordId \| QueryByVectorValues`. |
|
|
478
|
+
| `HybridQueryOptions` | Extends `QueryParamsBase` with `vector`, `KeywordQuery`, `Alpha`, and `FusionMethod` for combined vector + keyword search. |
|
|
479
|
+
| `QueryResponse<T>` | Query result containing `matches` (sorted `ScoredRecord` array), `namespace`, and optional `usage`. |
|
|
480
|
+
| `ScoredRecord<T>` | Extends `VectorRecord` with a `score` field. Score interpretation depends on the index metric. |
|
|
481
|
+
| `OperationUsage` | Usage metrics with optional `readUnits` count. |
|
|
482
|
+
|
|
265
483
|
## Distance Metrics
|
|
266
484
|
|
|
267
485
|
The `IndexModelMetricEnum` supports three metrics for similarity comparison:
|
|
268
486
|
|
|
269
|
-
| Metric | Description |
|
|
270
|
-
|
|
271
|
-
| `cosine` | Measures angle between vectors (direction similarity) |
|
|
272
|
-
| `euclidean` | Straight-line distance between
|
|
273
|
-
| `dotproduct` | Measures both direction and magnitude alignment |
|
|
487
|
+
| Metric | Value | Description | Score Interpretation |
|
|
488
|
+
|---|---|---|---|
|
|
489
|
+
| Cosine | `'cosine'` | Measures angle between vectors (direction similarity) | Higher = more similar |
|
|
490
|
+
| Euclidean | `'euclidean'` | Straight-line distance between vector endpoints | Lower = more similar |
|
|
491
|
+
| Dot Product | `'dotproduct'` | Measures both direction and magnitude alignment | Higher = more similar |
|
|
274
492
|
|
|
275
493
|
## Available Implementations
|
|
276
494
|
|
|
277
|
-
| Package |
|
|
278
|
-
|
|
279
|
-
| `@memberjunction/ai-vectors-pinecone` | Pinecone |
|
|
495
|
+
| Package | Provider | Hybrid Search |
|
|
496
|
+
|---|---|---|
|
|
497
|
+
| `@memberjunction/ai-vectors-pinecone` | Pinecone | No |
|
|
280
498
|
|
|
281
|
-
Create additional implementations by extending `VectorDBBase` and registering with MemberJunction's class factory.
|
|
499
|
+
Create additional implementations by extending `VectorDBBase` and optionally registering with MemberJunction's class factory.
|
|
282
500
|
|
|
283
501
|
## Dependencies
|
|
284
502
|
|
|
285
503
|
| Package | Purpose |
|
|
286
504
|
|---|---|
|
|
287
505
|
| `@memberjunction/core` | Core MemberJunction functionality |
|
|
288
|
-
| `@memberjunction/global` | Global utilities |
|
|
506
|
+
| `@memberjunction/global` | Global utilities and class registration |
|
|
289
507
|
|
|
290
508
|
## Development
|
|
291
509
|
|
|
292
510
|
```bash
|
|
293
511
|
# Build
|
|
512
|
+
cd packages/AI/Vectors/Database
|
|
294
513
|
npm run build
|
|
295
514
|
|
|
296
|
-
#
|
|
297
|
-
npm run
|
|
515
|
+
# Run tests
|
|
516
|
+
npm run test
|
|
517
|
+
|
|
518
|
+
# Watch mode
|
|
519
|
+
npm run test:watch
|
|
298
520
|
```
|
|
299
521
|
|
|
300
522
|
## License
|