@mastra/couchbase 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-vector-query-sources-20250516172905

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.
@@ -5,9 +5,14 @@ import type {
5
5
  CreateIndexParams,
6
6
  UpsertVectorParams,
7
7
  QueryVectorParams,
8
+ DescribeIndexParams,
9
+ ParamsToArgs,
10
+ DeleteIndexParams,
11
+ DeleteVectorParams,
12
+ UpdateVectorParams,
8
13
  } from '@mastra/core/vector';
9
14
  import type { Bucket, Cluster, Collection, Scope } from 'couchbase';
10
- import { connect, SearchRequest, VectorQuery, VectorSearch } from 'couchbase';
15
+ import { MutateInSpec, connect, SearchRequest, VectorQuery, VectorSearch } from 'couchbase';
11
16
 
12
17
  type MastraMetric = 'cosine' | 'euclidean' | 'dotproduct';
13
18
  type CouchbaseMetric = 'cosine' | 'l2_norm' | 'dot_product';
@@ -17,6 +22,15 @@ export const DISTANCE_MAPPING: Record<MastraMetric, CouchbaseMetric> = {
17
22
  dotproduct: 'dot_product',
18
23
  };
19
24
 
25
+ export type CouchbaseVectorParams = {
26
+ connectionString: string;
27
+ username: string;
28
+ password: string;
29
+ bucketName: string;
30
+ scopeName: string;
31
+ collectionName: string;
32
+ };
33
+
20
34
  export class CouchbaseVector extends MastraVector {
21
35
  private clusterPromise: Promise<Cluster>;
22
36
  private cluster: Cluster;
@@ -28,19 +42,66 @@ export class CouchbaseVector extends MastraVector {
28
42
  private scope: Scope;
29
43
  private vector_dimension: number;
30
44
 
45
+ /**
46
+ * @deprecated Passing parameters as positional arguments is deprecated.
47
+ * Use the object parameter instead. This signature will be removed on May 20th, 2025.
48
+ */
31
49
  constructor(
32
- cnn_string: string,
50
+ connectionString: string,
33
51
  username: string,
34
52
  password: string,
35
53
  bucketName: string,
36
54
  scopeName: string,
37
55
  collectionName: string,
56
+ );
57
+ constructor(params: CouchbaseVectorParams);
58
+ constructor(
59
+ paramsOrConnectionString: CouchbaseVectorParams | string,
60
+ username?: string,
61
+ password?: string,
62
+ bucketName?: string,
63
+ scopeName?: string,
64
+ collectionName?: string,
38
65
  ) {
66
+ let connectionString_: string,
67
+ username_: string,
68
+ password_: string,
69
+ bucketName_: string,
70
+ scopeName_: string,
71
+ collectionName_: string;
72
+
73
+ if (
74
+ typeof paramsOrConnectionString === 'object' &&
75
+ paramsOrConnectionString !== null &&
76
+ 'connectionString' in paramsOrConnectionString
77
+ ) {
78
+ // Object params (preferred)
79
+ connectionString_ = paramsOrConnectionString.connectionString as string;
80
+ username_ = paramsOrConnectionString.username;
81
+ password_ = paramsOrConnectionString.password;
82
+ bucketName_ = paramsOrConnectionString.bucketName;
83
+ scopeName_ = paramsOrConnectionString.scopeName;
84
+ collectionName_ = paramsOrConnectionString.collectionName;
85
+ } else {
86
+ // Positional args (deprecated)
87
+ if (arguments.length > 1) {
88
+ console.warn(
89
+ 'Deprecation Warning: CouchbaseVector constructor positional arguments are deprecated. Please use a single object parameter instead. This signature will be removed on May 20th, 2025.',
90
+ );
91
+ }
92
+ connectionString_ = paramsOrConnectionString as string;
93
+ username_ = username!;
94
+ password_ = password!;
95
+ bucketName_ = bucketName!;
96
+ scopeName_ = scopeName!;
97
+ collectionName_ = collectionName!;
98
+ }
99
+
39
100
  super();
40
101
 
41
- const baseClusterPromise = connect(cnn_string, {
42
- username,
43
- password,
102
+ const baseClusterPromise = connect(connectionString_, {
103
+ username: username_,
104
+ password: password_,
44
105
  configProfile: 'wanDevelopment',
45
106
  });
46
107
 
@@ -53,9 +114,9 @@ export class CouchbaseVector extends MastraVector {
53
114
  },
54
115
  }) ?? baseClusterPromise;
55
116
  this.cluster = null as unknown as Cluster;
56
- this.bucketName = bucketName;
57
- this.collectionName = collectionName;
58
- this.scopeName = scopeName;
117
+ this.bucketName = bucketName_;
118
+ this.collectionName = collectionName_;
119
+ this.scopeName = scopeName_;
59
120
  this.collection = null as unknown as Collection;
60
121
  this.bucket = null as unknown as Bucket;
61
122
  this.scope = null as unknown as Scope;
@@ -84,81 +145,92 @@ export class CouchbaseVector extends MastraVector {
84
145
  throw new Error('Dimension must be a positive integer');
85
146
  }
86
147
 
87
- await this.scope.searchIndexes().upsertIndex({
88
- name: indexName,
89
- sourceName: this.bucketName,
90
- type: 'fulltext-index',
91
- params: {
92
- doc_config: {
93
- docid_prefix_delim: '',
94
- docid_regexp: '',
95
- mode: 'scope.collection.type_field',
96
- type_field: 'type',
97
- },
98
- mapping: {
99
- default_analyzer: 'standard',
100
- default_datetime_parser: 'dateTimeOptional',
101
- default_field: '_all',
102
- default_mapping: {
103
- dynamic: true,
104
- enabled: false,
148
+ try {
149
+ await this.scope.searchIndexes().upsertIndex({
150
+ name: indexName,
151
+ sourceName: this.bucketName,
152
+ type: 'fulltext-index',
153
+ params: {
154
+ doc_config: {
155
+ docid_prefix_delim: '',
156
+ docid_regexp: '',
157
+ mode: 'scope.collection.type_field',
158
+ type_field: 'type',
105
159
  },
106
- default_type: '_default',
107
- docvalues_dynamic: true, // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
108
- index_dynamic: true,
109
- store_dynamic: true, // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
110
- type_field: '_type',
111
- types: {
112
- [`${this.scopeName}.${this.collectionName}`]: {
160
+ mapping: {
161
+ default_analyzer: 'standard',
162
+ default_datetime_parser: 'dateTimeOptional',
163
+ default_field: '_all',
164
+ default_mapping: {
113
165
  dynamic: true,
114
- enabled: true,
115
- properties: {
116
- embedding: {
117
- enabled: true,
118
- fields: [
119
- {
120
- dims: dimension,
121
- index: true,
122
- name: 'embedding',
123
- similarity: DISTANCE_MAPPING[metric],
124
- type: 'vector',
125
- vector_index_optimized_for: 'recall',
126
- store: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
127
- docvalues: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
128
- include_term_vectors: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
129
- },
130
- ],
131
- },
132
- content: {
133
- enabled: true,
134
- fields: [
135
- {
136
- index: true,
137
- name: 'content',
138
- store: true,
139
- type: 'text',
140
- },
141
- ],
166
+ enabled: false,
167
+ },
168
+ default_type: '_default',
169
+ docvalues_dynamic: true, // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
170
+ index_dynamic: true,
171
+ store_dynamic: true, // [Doc](https://docs.couchbase.com/server/current/search/search-index-params.html#params) mentions this attribute is required for vector search to return the indexed field
172
+ type_field: '_type',
173
+ types: {
174
+ [`${this.scopeName}.${this.collectionName}`]: {
175
+ dynamic: true,
176
+ enabled: true,
177
+ properties: {
178
+ embedding: {
179
+ enabled: true,
180
+ fields: [
181
+ {
182
+ dims: dimension,
183
+ index: true,
184
+ name: 'embedding',
185
+ similarity: DISTANCE_MAPPING[metric],
186
+ type: 'vector',
187
+ vector_index_optimized_for: 'recall',
188
+ store: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
189
+ docvalues: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
190
+ include_term_vectors: true, // CHANGED due to https://docs.couchbase.com/server/current/search/search-index-params.html#fields
191
+ },
192
+ ],
193
+ },
194
+ content: {
195
+ enabled: true,
196
+ fields: [
197
+ {
198
+ index: true,
199
+ name: 'content',
200
+ store: true,
201
+ type: 'text',
202
+ },
203
+ ],
204
+ },
142
205
  },
143
206
  },
144
207
  },
145
208
  },
209
+ store: {
210
+ indexType: 'scorch',
211
+ segmentVersion: 16,
212
+ },
146
213
  },
147
- store: {
148
- indexType: 'scorch',
149
- segmentVersion: 16,
214
+ sourceUuid: '',
215
+ sourceParams: {},
216
+ sourceType: 'gocbcore',
217
+ planParams: {
218
+ maxPartitionsPerPIndex: 64,
219
+ indexPartitions: 16,
220
+ numReplicas: 0,
150
221
  },
151
- },
152
- sourceUuid: '',
153
- sourceParams: {},
154
- sourceType: 'gocbcore',
155
- planParams: {
156
- maxPartitionsPerPIndex: 64,
157
- indexPartitions: 16,
158
- numReplicas: 0,
159
- },
160
- });
161
- this.vector_dimension = dimension;
222
+ });
223
+ this.vector_dimension = dimension;
224
+ } catch (error: any) {
225
+ // Check for 'already exists' error (Couchbase may throw a 400 or 409, or have a message)
226
+ const message = error?.message || error?.toString();
227
+ if (message && message.toLowerCase().includes('index exists')) {
228
+ // Fetch index info and check dimension
229
+ await this.validateExistingIndex(indexName, dimension, metric);
230
+ return;
231
+ }
232
+ throw error;
233
+ }
162
234
  }
163
235
 
164
236
  async upsert(params: UpsertVectorParams): Promise<string[]> {
@@ -244,7 +316,16 @@ export class CouchbaseVector extends MastraVector {
244
316
  return indexes?.map(index => index.name) || [];
245
317
  }
246
318
 
247
- async describeIndex(indexName: string): Promise<IndexStats> {
319
+ /**
320
+ * Retrieves statistics about a vector index.
321
+ *
322
+ * @param params - The parameters for describing an index
323
+ * @param params.indexName - The name of the index to describe
324
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
325
+ */
326
+ async describeIndex(...args: ParamsToArgs<DescribeIndexParams>): Promise<IndexStats> {
327
+ const params = this.normalizeArgs<DescribeIndexParams>('describeIndex', args);
328
+ const { indexName } = params;
248
329
  await this.getCollection();
249
330
  if (!(await this.listIndexes()).includes(indexName)) {
250
331
  throw new Error(`Index ${indexName} does not exist`);
@@ -265,7 +346,9 @@ export class CouchbaseVector extends MastraVector {
265
346
  };
266
347
  }
267
348
 
268
- async deleteIndex(indexName: string): Promise<void> {
349
+ async deleteIndex(...args: ParamsToArgs<DeleteIndexParams>): Promise<void> {
350
+ const params = this.normalizeArgs<DeleteIndexParams>('deleteIndex', args);
351
+ const { indexName } = params;
269
352
  await this.getCollection();
270
353
  if (!(await this.listIndexes()).includes(indexName)) {
271
354
  throw new Error(`Index ${indexName} does not exist`);
@@ -273,4 +356,68 @@ export class CouchbaseVector extends MastraVector {
273
356
  await this.scope.searchIndexes().dropIndex(indexName);
274
357
  this.vector_dimension = null as unknown as number;
275
358
  }
359
+
360
+ /**
361
+ * Updates a vector by its ID with the provided vector and/or metadata.
362
+ * @param indexName - The name of the index containing the vector.
363
+ * @param id - The ID of the vector to update.
364
+ * @param update - An object containing the vector and/or metadata to update.
365
+ * @param update.vector - An optional array of numbers representing the new vector.
366
+ * @param update.metadata - An optional record containing the new metadata.
367
+ * @returns A promise that resolves when the update is complete.
368
+ * @throws Will throw an error if no updates are provided or if the update operation fails.
369
+ */
370
+ async updateVector(...args: ParamsToArgs<UpdateVectorParams>): Promise<void> {
371
+ const params = this.normalizeArgs<UpdateVectorParams>('updateVector', args);
372
+ const { id, update } = params;
373
+ if (!update.vector && !update.metadata) {
374
+ throw new Error('No updates provided');
375
+ }
376
+ if (update.vector && this.vector_dimension && update.vector.length !== this.vector_dimension) {
377
+ throw new Error('Vector dimension mismatch');
378
+ }
379
+ const collection = await this.getCollection();
380
+
381
+ // Check if document exists
382
+ try {
383
+ await collection.get(id);
384
+ } catch (err: any) {
385
+ if (err.code === 13 || err.message?.includes('document not found')) {
386
+ throw new Error(`Vector with id ${id} does not exist`);
387
+ }
388
+ throw err;
389
+ }
390
+
391
+ const specs: MutateInSpec[] = [];
392
+ if (update.vector) specs.push(MutateInSpec.replace('embedding', update.vector));
393
+ if (update.metadata) specs.push(MutateInSpec.replace('metadata', update.metadata));
394
+
395
+ await collection.mutateIn(id, specs);
396
+ }
397
+
398
+ /**
399
+ * Deletes a vector by its ID.
400
+ * @param indexName - The name of the index containing the vector.
401
+ * @param id - The ID of the vector to delete.
402
+ * @returns A promise that resolves when the deletion is complete.
403
+ * @throws Will throw an error if the deletion operation fails.
404
+ */
405
+ async deleteVector(...args: ParamsToArgs<DeleteVectorParams>): Promise<void> {
406
+ const params = this.normalizeArgs<DeleteVectorParams>('deleteVector', args);
407
+
408
+ const { id } = params;
409
+ const collection = await this.getCollection();
410
+
411
+ // Check if document exists
412
+ try {
413
+ await collection.get(id);
414
+ } catch (err: any) {
415
+ if (err.code === 13 || err.message?.includes('document not found')) {
416
+ throw new Error(`Vector with id ${id} does not exist`);
417
+ }
418
+ throw err;
419
+ }
420
+
421
+ await collection.remove(id);
422
+ }
276
423
  }
@@ -165,14 +165,14 @@ describe('Unit Testing CouchbaseVector', () => {
165
165
  });
166
166
 
167
167
  it('should connect to couchbase', async () => {
168
- couchbase_client = new CouchbaseVector(
169
- 'COUCHBASE_CONNECTION_STRING',
170
- 'COUCHBASE_USERNAME',
171
- 'COUCHBASE_PASSWORD',
172
- test_bucketName,
173
- test_scopeName,
174
- test_collectionName,
175
- );
168
+ couchbase_client = new CouchbaseVector({
169
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
170
+ username: 'COUCHBASE_USERNAME',
171
+ password: 'COUCHBASE_PASSWORD',
172
+ bucketName: test_bucketName,
173
+ scopeName: test_scopeName,
174
+ collectionName: test_collectionName,
175
+ });
176
176
  expect(mockCouchbaseConnectFn).toHaveBeenCalledTimes(1);
177
177
  expect(mockCouchbaseConnectFn).toHaveBeenCalledWith('COUCHBASE_CONNECTION_STRING', {
178
178
  username: 'COUCHBASE_USERNAME',
@@ -200,14 +200,14 @@ describe('Unit Testing CouchbaseVector', () => {
200
200
  describe('Index Operations', () => {
201
201
  beforeAll(async () => {
202
202
  clearAllMocks();
203
- couchbase_client = new CouchbaseVector(
204
- 'COUCHBASE_CONNECTION_STRING',
205
- 'COUCHBASE_USERNAME',
206
- 'COUCHBASE_PASSWORD',
207
- test_bucketName,
208
- test_scopeName,
209
- test_collectionName,
210
- );
203
+ couchbase_client = new CouchbaseVector({
204
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
205
+ username: 'COUCHBASE_USERNAME',
206
+ password: 'COUCHBASE_PASSWORD',
207
+ bucketName: test_bucketName,
208
+ scopeName: test_scopeName,
209
+ collectionName: test_collectionName,
210
+ });
211
211
  await couchbase_client.getCollection();
212
212
  });
213
213
 
@@ -311,7 +311,7 @@ describe('Unit Testing CouchbaseVector', () => {
311
311
  }, 50000);
312
312
 
313
313
  it('should describe index', async () => {
314
- const stats = await couchbase_client.describeIndex(test_indexName);
314
+ const stats = await couchbase_client.describeIndex({ indexName: test_indexName });
315
315
 
316
316
  expect(mockScopeSearchIndexesFn).toHaveBeenCalledTimes(2);
317
317
 
@@ -328,7 +328,7 @@ describe('Unit Testing CouchbaseVector', () => {
328
328
  }, 50000);
329
329
 
330
330
  it('should delete index', async () => {
331
- await couchbase_client.deleteIndex(test_indexName);
331
+ await couchbase_client.deleteIndex({ indexName: test_indexName });
332
332
 
333
333
  expect(mockScopeSearchIndexesFn).toHaveBeenCalledTimes(2);
334
334
 
@@ -343,14 +343,14 @@ describe('Unit Testing CouchbaseVector', () => {
343
343
 
344
344
  describe('Vector Operations', () => {
345
345
  beforeAll(async () => {
346
- couchbase_client = new CouchbaseVector(
347
- 'COUCHBASE_CONNECTION_STRING',
348
- 'COUCHBASE_USERNAME',
349
- 'COUCHBASE_PASSWORD',
350
- test_bucketName,
351
- test_scopeName,
352
- test_collectionName,
353
- );
346
+ couchbase_client = new CouchbaseVector({
347
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
348
+ username: 'COUCHBASE_USERNAME',
349
+ password: 'COUCHBASE_PASSWORD',
350
+ bucketName: test_bucketName,
351
+ scopeName: test_scopeName,
352
+ collectionName: test_collectionName,
353
+ });
354
354
 
355
355
  await couchbase_client.getCollection();
356
356
  });
@@ -510,14 +510,14 @@ describe('Unit Testing CouchbaseVector', () => {
510
510
  describe('Error Cases and Edge Cases', () => {
511
511
  beforeAll(async () => {
512
512
  clearAllMocks();
513
- couchbase_client = new CouchbaseVector(
514
- 'COUCHBASE_CONNECTION_STRING',
515
- 'COUCHBASE_USERNAME',
516
- 'COUCHBASE_PASSWORD',
517
- test_bucketName,
518
- test_scopeName,
519
- test_collectionName,
520
- );
513
+ couchbase_client = new CouchbaseVector({
514
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
515
+ username: 'COUCHBASE_USERNAME',
516
+ password: 'COUCHBASE_PASSWORD',
517
+ bucketName: test_bucketName,
518
+ scopeName: test_scopeName,
519
+ collectionName: test_collectionName,
520
+ });
521
521
  await couchbase_client.getCollection();
522
522
  });
523
523
 
@@ -542,13 +542,13 @@ describe('Unit Testing CouchbaseVector', () => {
542
542
  });
543
543
 
544
544
  it('should throw error when describing a non-existent index', async () => {
545
- await expect(couchbase_client.describeIndex('non_existent_index')).rejects.toThrow(
545
+ await expect(couchbase_client.describeIndex({ indexName: 'non_existent_index' })).rejects.toThrow(
546
546
  `Index non_existent_index does not exist`,
547
547
  );
548
548
  });
549
549
 
550
550
  it('should throw error when deleting a non-existent index', async () => {
551
- await expect(couchbase_client.deleteIndex('non_existent_index')).rejects.toThrow(
551
+ await expect(couchbase_client.deleteIndex({ indexName: 'non_existent_index' })).rejects.toThrow(
552
552
  `Index non_existent_index does not exist`,
553
553
  );
554
554
  });
@@ -577,14 +577,14 @@ describe('Unit Testing CouchbaseVector', () => {
577
577
  describe('Vector Dimension Tracking', () => {
578
578
  beforeEach(async () => {
579
579
  clearAllMocks();
580
- couchbase_client = new CouchbaseVector(
581
- 'COUCHBASE_CONNECTION_STRING',
582
- 'COUCHBASE_USERNAME',
583
- 'COUCHBASE_PASSWORD',
584
- test_bucketName,
585
- test_scopeName,
586
- test_collectionName,
587
- );
580
+ couchbase_client = new CouchbaseVector({
581
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
582
+ username: 'COUCHBASE_USERNAME',
583
+ password: 'COUCHBASE_PASSWORD',
584
+ bucketName: test_bucketName,
585
+ scopeName: test_scopeName,
586
+ collectionName: test_collectionName,
587
+ });
588
588
  await couchbase_client.getCollection();
589
589
  });
590
590
 
@@ -637,7 +637,7 @@ describe('Unit Testing CouchbaseVector', () => {
637
637
  clearAllMocks();
638
638
 
639
639
  // Delete the index
640
- await couchbase_client.deleteIndex(test_indexName);
640
+ await couchbase_client.deleteIndex({ indexName: test_indexName });
641
641
 
642
642
  // Verify dimension is reset
643
643
  expect((couchbase_client as any).vector_dimension).toBeNull();
@@ -647,14 +647,14 @@ describe('Unit Testing CouchbaseVector', () => {
647
647
  describe('Implementation Details', () => {
648
648
  beforeEach(async () => {
649
649
  clearAllMocks();
650
- couchbase_client = new CouchbaseVector(
651
- 'COUCHBASE_CONNECTION_STRING',
652
- 'COUCHBASE_USERNAME',
653
- 'COUCHBASE_PASSWORD',
654
- test_bucketName,
655
- test_scopeName,
656
- test_collectionName,
657
- );
650
+ couchbase_client = new CouchbaseVector({
651
+ connectionString: 'COUCHBASE_CONNECTION_STRING',
652
+ username: 'COUCHBASE_USERNAME',
653
+ password: 'COUCHBASE_PASSWORD',
654
+ bucketName: test_bucketName,
655
+ scopeName: test_scopeName,
656
+ collectionName: test_collectionName,
657
+ });
658
658
  await couchbase_client.getCollection();
659
659
  });
660
660
 
@@ -713,7 +713,7 @@ describe('Unit Testing CouchbaseVector', () => {
713
713
  },
714
714
  });
715
715
 
716
- const stats = await couchbase_client.describeIndex(test_indexName);
716
+ const stats = await couchbase_client.describeIndex({ indexName: test_indexName });
717
717
  expect(stats.metric).toBe(mastraMetric);
718
718
  }
719
719
  });