@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 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 base class, type system, and query interfaces that concrete vector database implementations (such as Pinecone) must fulfill.
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
- ## Overview
47
-
48
- This package provides:
48
+ ## Quick Start
49
49
 
50
- - **VectorDBBase** -- an abstract class that defines a complete API for vector database operations (index management + record CRUD + similarity queries)
51
- - **Record types** -- `VectorRecord`, `RecordValues`, `RecordSparseValues`, `RecordMetadata` for representing vectors and their metadata
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
- Concrete implementations like `@memberjunction/ai-vectors-pinecone` extend `VectorDBBase` to connect to specific vector database services.
53
+ // Use any concrete provider that extends VectorDBBase
54
+ const db: VectorDBBase = new PineconeDatabase(apiKey);
57
55
 
58
- ## Core Components
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
- ### VectorDBBase (Abstract Class)
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
- The abstract methods support both synchronous and asynchronous return types via union types (`BaseResponse | Promise<BaseResponse>`), allowing implementations to choose the appropriate pattern.
124
+ All abstract methods use union return types (`BaseResponse | Promise<BaseResponse>`) so subclasses can implement them as either synchronous or asynchronous.
98
125
 
99
- ### Type System
126
+ ## Abstract Methods Reference
100
127
 
101
- #### Vector Records
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
- type RecordValues = Array<number>;
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
- type RecordSparseValues = {
115
- indices: Array<number>; // Non-zero positions
116
- values: Array<number>; // Corresponding values
117
- };
139
+ ### Record Operations
118
140
 
119
- type RecordMetadataValue = string | boolean | number | Array<string>;
120
- type RecordMetadata = Record<string, RecordMetadataValue>;
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
- #### Index Configuration
152
+ ### Virtual Methods (Non-Abstract)
124
153
 
125
- ```typescript
126
- type IndexDescription = {
127
- name: string; // Index name (max 45 chars)
128
- dimension: number; // Vector dimensionality
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
- #### Query Types
159
+ ## Hybrid Search
135
160
 
136
- ```mermaid
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
- QPB --> QBV
144
- QPB --> QBID
145
- QBV --> QO
146
- QBID --> QO
163
+ ### How It Works
147
164
 
148
- style QPB fill:#2d6a9f,stroke:#1a4971,color:#fff
149
- style QBV fill:#2d8659,stroke:#1a5c3a,color:#fff
150
- style QBID fill:#2d8659,stroke:#1a5c3a,color:#fff
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
- ```typescript
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
- // Query by providing a vector directly
164
- type QueryByVectorValues = QueryParamsBase & { vector: RecordValues };
171
+ `HybridQueryOptions` extends `QueryParamsBase` with the following additional fields:
165
172
 
166
- // Query using an existing record's vector
167
- type QueryByRecordId = QueryParamsBase & { id: string };
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
- // Union type for all query configurations
170
- type QueryOptions = QueryByRecordId | QueryByVectorValues;
171
- ```
180
+ Plus the inherited `QueryParamsBase` fields: `topK`, `includeValues`, `includeMetadata`, `filter`.
172
181
 
173
- #### Query Response
182
+ ### Implementing Hybrid Search in a Provider
174
183
 
175
184
  ```typescript
176
- type QueryResponse<T extends RecordMetadata = RecordMetadata> = {
177
- matches: Array<ScoredRecord<T>>; // Sorted by similarity
178
- namespace: string; // Execution namespace
179
- usage?: OperationUsage; // Read unit consumption
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
- interface ScoredRecord<T> extends VectorRecord<T> {
183
- score?: number; // Similarity score (interpretation depends on metric)
204
+ // ... remaining abstract method implementations
184
205
  }
185
206
  ```
186
207
 
187
- #### Standardized Response
188
-
189
- All operations return a `BaseResponse`:
208
+ ### Consumer Usage
190
209
 
191
210
  ```typescript
192
- type BaseResponse = {
193
- success: boolean;
194
- message: string;
195
- data: unknown;
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
- ## Usage
228
+ ## Implementing a Custom Provider
200
229
 
201
- ### Implementing a Provider
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
- VectorRecord,
237
+ BaseRequestParams,
207
238
  BaseResponse,
208
239
  CreateIndexParams,
240
+ EditIndexParams,
241
+ IndexList,
209
242
  QueryOptions,
210
- IndexList
243
+ UpdateOptions,
244
+ VectorRecord
211
245
  } from '@memberjunction/ai-vectordb';
212
246
 
213
- export class MyVectorDB extends VectorDBBase {
214
- constructor(apiKey: string) {
215
- super(apiKey); // Validates and stores the API key
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
- // Call your vector DB API using this.apiKey
220
- return { indexes: [] };
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
- return { success: true, message: 'Created', data: { id: params.id } };
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
- // Perform similarity search
229
- return { success: true, message: 'OK', data: { matches: [] } };
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
- // ... implement remaining abstract methods
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
- ### Consuming a Provider
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 { VectorDBBase, VectorRecord } from '@memberjunction/ai-vectordb';
341
+ import { RegisterClass } from '@memberjunction/global';
342
+
343
+ @RegisterClass(VectorDBBase, 'QdrantDB')
344
+ export class QdrantDB extends VectorDBBase {
345
+ // ... implementation
346
+ }
347
+ ```
240
348
 
241
- async function searchSimilar(vectorDB: VectorDBBase, embedding: number[]): Promise<void> {
242
- // Insert a record
243
- const record: VectorRecord = {
244
- id: 'doc-001',
245
- values: embedding,
246
- metadata: { entity: 'Products', recordId: '12345' }
247
- };
248
- await vectorDB.createRecord(record);
249
-
250
- // Query for similar vectors
251
- const result = await vectorDB.queryIndex({
252
- vector: embedding,
253
- topK: 10,
254
- includeMetadata: true
255
- });
256
-
257
- if (result.success) {
258
- for (const match of result.data.matches) {
259
- console.log(`Match: ${match.id} (score: ${match.score})`);
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 | Use Case |
270
- |---|---|---|
271
- | `cosine` | Measures angle between vectors (direction similarity) | Text embeddings, semantic search |
272
- | `euclidean` | Straight-line distance between points | Numeric features, specifications |
273
- | `dotproduct` | Measures both direction and magnitude alignment | Recommendation systems, weighted scoring |
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 | Vector Database |
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
- # Development mode
297
- npm run start
515
+ # Run tests
516
+ npm run test
517
+
518
+ # Watch mode
519
+ npm run test:watch
298
520
  ```
299
521
 
300
522
  ## License