@ekodb/ekodb-client 0.7.0 → 0.8.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/dist/client.d.ts +180 -13
- package/dist/client.js +257 -32
- package/dist/client.test.d.ts +7 -0
- package/dist/client.test.js +702 -0
- package/dist/query-builder.test.d.ts +4 -0
- package/dist/query-builder.test.js +318 -0
- package/dist/search.d.ts +12 -0
- package/dist/search.js +14 -0
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +3 -3
- package/dist/utils.test.d.ts +4 -0
- package/dist/utils.test.js +411 -0
- package/package.json +7 -4
- package/src/client.test.ts +961 -0
- package/src/client.ts +367 -42
- package/src/query-builder.test.ts +404 -0
- package/src/search.ts +22 -0
- package/src/utils.test.ts +506 -0
- package/src/utils.ts +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ekoDB TypeScript Client
|
|
3
3
|
*/
|
|
4
4
|
import { QueryBuilder } from "./query-builder";
|
|
5
|
-
import { SearchQuery,
|
|
5
|
+
import { SearchQuery, SearchResponse } from "./search";
|
|
6
6
|
import { Schema, SchemaBuilder, CollectionMetadata } from "./schema";
|
|
7
7
|
import { Script, FunctionResult } from "./functions";
|
|
8
8
|
export interface Record {
|
|
@@ -64,6 +64,52 @@ export interface BatchOperationResult {
|
|
|
64
64
|
error: string;
|
|
65
65
|
}>;
|
|
66
66
|
}
|
|
67
|
+
export interface InsertOptions {
|
|
68
|
+
ttl?: string;
|
|
69
|
+
bypassRipple?: boolean;
|
|
70
|
+
transactionId?: string;
|
|
71
|
+
bypassCache?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface UpdateOptions {
|
|
74
|
+
bypassRipple?: boolean;
|
|
75
|
+
transactionId?: string;
|
|
76
|
+
bypassCache?: boolean;
|
|
77
|
+
selectFields?: string[];
|
|
78
|
+
excludeFields?: string[];
|
|
79
|
+
}
|
|
80
|
+
export interface DeleteOptions {
|
|
81
|
+
bypassRipple?: boolean;
|
|
82
|
+
transactionId?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface UpsertOptions {
|
|
85
|
+
ttl?: string;
|
|
86
|
+
bypassRipple?: boolean;
|
|
87
|
+
transactionId?: string;
|
|
88
|
+
bypassCache?: boolean;
|
|
89
|
+
}
|
|
90
|
+
export interface FindOptions {
|
|
91
|
+
filter?: any;
|
|
92
|
+
sort?: any;
|
|
93
|
+
limit?: number;
|
|
94
|
+
skip?: number;
|
|
95
|
+
join?: any;
|
|
96
|
+
bypassRipple?: boolean;
|
|
97
|
+
bypassCache?: boolean;
|
|
98
|
+
selectFields?: string[];
|
|
99
|
+
excludeFields?: string[];
|
|
100
|
+
}
|
|
101
|
+
export interface BatchInsertOptions {
|
|
102
|
+
bypassRipple?: boolean;
|
|
103
|
+
transactionId?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface BatchUpdateOptions {
|
|
106
|
+
bypassRipple?: boolean;
|
|
107
|
+
transactionId?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface BatchDeleteOptions {
|
|
110
|
+
bypassRipple?: boolean;
|
|
111
|
+
transactionId?: string;
|
|
112
|
+
}
|
|
67
113
|
export interface CollectionConfig {
|
|
68
114
|
collection_name: string;
|
|
69
115
|
fields?: string[];
|
|
@@ -208,9 +254,9 @@ export declare class EkoDBClient {
|
|
|
208
254
|
* Insert a document into a collection
|
|
209
255
|
* @param collection - Collection name
|
|
210
256
|
* @param record - Document to insert
|
|
211
|
-
* @param
|
|
257
|
+
* @param options - Optional parameters (ttl, bypassRipple, transactionId, bypassCache)
|
|
212
258
|
*/
|
|
213
|
-
insert(collection: string, record: Record,
|
|
259
|
+
insert(collection: string, record: Record, options?: InsertOptions): Promise<Record>;
|
|
214
260
|
/**
|
|
215
261
|
* Find documents in a collection
|
|
216
262
|
*
|
|
@@ -237,19 +283,29 @@ export declare class EkoDBClient {
|
|
|
237
283
|
/**
|
|
238
284
|
* Find a document by ID
|
|
239
285
|
*/
|
|
240
|
-
|
|
286
|
+
findById(collection: string, id: string): Promise<Record>;
|
|
241
287
|
/**
|
|
242
288
|
* Update a document
|
|
289
|
+
* @param collection - Collection name
|
|
290
|
+
* @param id - Document ID
|
|
291
|
+
* @param record - Update data
|
|
292
|
+
* @param options - Optional parameters (bypassRipple, transactionId, bypassCache, selectFields, excludeFields)
|
|
243
293
|
*/
|
|
244
|
-
update(collection: string, id: string, record: Record): Promise<Record>;
|
|
294
|
+
update(collection: string, id: string, record: Record, options?: UpdateOptions): Promise<Record>;
|
|
245
295
|
/**
|
|
246
296
|
* Delete a document
|
|
297
|
+
* @param collection - Collection name
|
|
298
|
+
* @param id - Document ID
|
|
299
|
+
* @param options - Optional parameters (bypassRipple, transactionId)
|
|
247
300
|
*/
|
|
248
|
-
delete(collection: string, id: string): Promise<void>;
|
|
301
|
+
delete(collection: string, id: string, options?: DeleteOptions): Promise<void>;
|
|
249
302
|
/**
|
|
250
303
|
* Batch insert multiple documents
|
|
304
|
+
* @param collection - Collection name
|
|
305
|
+
* @param records - Array of documents to insert
|
|
306
|
+
* @param options - Optional parameters (bypassRipple, transactionId)
|
|
251
307
|
*/
|
|
252
|
-
batchInsert(collection: string, records: Record[],
|
|
308
|
+
batchInsert(collection: string, records: Record[], options?: BatchInsertOptions): Promise<BatchOperationResult>;
|
|
253
309
|
/**
|
|
254
310
|
* Batch update multiple documents
|
|
255
311
|
*/
|
|
@@ -324,6 +380,90 @@ export declare class EkoDBClient {
|
|
|
324
380
|
* @param transactionId - The transaction ID to rollback
|
|
325
381
|
*/
|
|
326
382
|
rollbackTransaction(transactionId: string): Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Insert or update a record (upsert operation)
|
|
385
|
+
*
|
|
386
|
+
* Attempts to update the record first. If the record doesn't exist (404 error),
|
|
387
|
+
* it will be inserted instead. This provides atomic insert-or-update semantics.
|
|
388
|
+
*
|
|
389
|
+
* @param collection - Collection name
|
|
390
|
+
* @param id - Record ID
|
|
391
|
+
* @param record - Record data to insert or update
|
|
392
|
+
* @param bypassRipple - Optional flag to bypass ripple effects
|
|
393
|
+
* @returns The inserted or updated record
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```typescript
|
|
397
|
+
* const record = { name: "John Doe", email: "john@example.com" };
|
|
398
|
+
* // Will update if exists, insert if not
|
|
399
|
+
* const result = await client.upsert("users", "user123", record);
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
/**
|
|
403
|
+
* Upsert a document (insert or update)
|
|
404
|
+
* @param collection - Collection name
|
|
405
|
+
* @param id - Document ID
|
|
406
|
+
* @param record - Document data
|
|
407
|
+
* @param options - Optional parameters (ttl, bypassRipple, transactionId, bypassCache)
|
|
408
|
+
*/
|
|
409
|
+
upsert(collection: string, id: string, record: Record, options?: UpsertOptions): Promise<Record>;
|
|
410
|
+
/**
|
|
411
|
+
* Find a single record by field value
|
|
412
|
+
*
|
|
413
|
+
* Convenience method for finding one record matching a specific field value.
|
|
414
|
+
* Returns null if no record matches, or the first matching record.
|
|
415
|
+
*
|
|
416
|
+
* @param collection - Collection name
|
|
417
|
+
* @param field - Field name to search
|
|
418
|
+
* @param value - Value to match
|
|
419
|
+
* @returns The matching record or null if not found
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* ```typescript
|
|
423
|
+
* // Find user by email
|
|
424
|
+
* const user = await client.findOne("users", "email", "john@example.com");
|
|
425
|
+
* if (user) {
|
|
426
|
+
* console.log("Found user:", user);
|
|
427
|
+
* }
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
findOne(collection: string, field: string, value: any): Promise<Record | null>;
|
|
431
|
+
/**
|
|
432
|
+
* Check if a record exists by ID
|
|
433
|
+
*
|
|
434
|
+
* This is more efficient than fetching the record when you only need to check existence.
|
|
435
|
+
*
|
|
436
|
+
* @param collection - Collection name
|
|
437
|
+
* @param id - Record ID to check
|
|
438
|
+
* @returns true if the record exists, false if it doesn't
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```typescript
|
|
442
|
+
* if (await client.exists("users", "user123")) {
|
|
443
|
+
* console.log("User exists");
|
|
444
|
+
* } else {
|
|
445
|
+
* console.log("User not found");
|
|
446
|
+
* }
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
exists(collection: string, id: string): Promise<boolean>;
|
|
450
|
+
/**
|
|
451
|
+
* Paginate through records
|
|
452
|
+
*
|
|
453
|
+
* Convenience method for pagination with page numbers (1-indexed).
|
|
454
|
+
*
|
|
455
|
+
* @param collection - Collection name
|
|
456
|
+
* @param page - Page number (1-indexed, i.e., first page is 1)
|
|
457
|
+
* @param pageSize - Number of records per page
|
|
458
|
+
* @returns Array of records for the requested page
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* // Get page 2 with 10 records per page
|
|
463
|
+
* const records = await client.paginate("users", 2, 10);
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
paginate(collection: string, page: number, pageSize: number): Promise<Record[]>;
|
|
327
467
|
/**
|
|
328
468
|
* List all collections
|
|
329
469
|
*/
|
|
@@ -332,6 +472,25 @@ export declare class EkoDBClient {
|
|
|
332
472
|
* Delete a collection
|
|
333
473
|
*/
|
|
334
474
|
deleteCollection(collection: string): Promise<void>;
|
|
475
|
+
/**
|
|
476
|
+
* Restore a deleted record from trash
|
|
477
|
+
* Records remain in trash for 30 days before permanent deletion
|
|
478
|
+
*
|
|
479
|
+
* @param collection - Collection name
|
|
480
|
+
* @param id - Record ID to restore
|
|
481
|
+
* @returns true if restored successfully
|
|
482
|
+
*/
|
|
483
|
+
restoreRecord(collection: string, id: string): Promise<boolean>;
|
|
484
|
+
/**
|
|
485
|
+
* Restore all deleted records in a collection from trash
|
|
486
|
+
* Records remain in trash for 30 days before permanent deletion
|
|
487
|
+
*
|
|
488
|
+
* @param collection - Collection name
|
|
489
|
+
* @returns Number of records restored
|
|
490
|
+
*/
|
|
491
|
+
restoreCollection(collection: string): Promise<{
|
|
492
|
+
recordsRestored: number;
|
|
493
|
+
}>;
|
|
335
494
|
/**
|
|
336
495
|
* Create a collection with schema
|
|
337
496
|
*
|
|
@@ -396,7 +555,11 @@ export declare class EkoDBClient {
|
|
|
396
555
|
* );
|
|
397
556
|
* ```
|
|
398
557
|
*/
|
|
399
|
-
search(collection: string,
|
|
558
|
+
search(collection: string, query: SearchQuery): Promise<SearchResponse>;
|
|
559
|
+
/**
|
|
560
|
+
* Health check - verify the ekoDB server is responding
|
|
561
|
+
*/
|
|
562
|
+
health(): Promise<boolean>;
|
|
400
563
|
/**
|
|
401
564
|
* Create a new chat session
|
|
402
565
|
*/
|
|
@@ -508,20 +671,24 @@ export declare class EkoDBClient {
|
|
|
508
671
|
* Simplified text search with full-text matching, fuzzy search, and stemming.
|
|
509
672
|
*
|
|
510
673
|
* @param collection - Collection name to search
|
|
511
|
-
* @param
|
|
512
|
-
* @param
|
|
513
|
-
* @returns
|
|
674
|
+
* @param query - Search query text
|
|
675
|
+
* @param options - Additional search options
|
|
676
|
+
* @returns Search response with results and metadata
|
|
514
677
|
*
|
|
515
678
|
* @example
|
|
516
679
|
* ```typescript
|
|
517
680
|
* const results = await client.textSearch(
|
|
518
681
|
* "documents",
|
|
519
682
|
* "ownership system",
|
|
520
|
-
*
|
|
683
|
+
* {
|
|
684
|
+
* limit: 10,
|
|
685
|
+
* select_fields: ["title", "content"],
|
|
686
|
+
* exclude_fields: ["author"]
|
|
687
|
+
* }
|
|
521
688
|
* );
|
|
522
689
|
* ```
|
|
523
690
|
*/
|
|
524
|
-
textSearch(collection: string,
|
|
691
|
+
textSearch(collection: string, query: string, options?: Partial<SearchQuery>): Promise<SearchResponse>;
|
|
525
692
|
/**
|
|
526
693
|
* Perform hybrid search combining text and vector search
|
|
527
694
|
*
|
package/dist/client.js
CHANGED
|
@@ -39,7 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.WebSocketClient = exports.EkoDBClient = exports.MergeStrategy = exports.RateLimitError = exports.SerializationFormat = void 0;
|
|
40
40
|
const msgpack_1 = require("@msgpack/msgpack");
|
|
41
41
|
const query_builder_1 = require("./query-builder");
|
|
42
|
-
const search_1 = require("./search");
|
|
43
42
|
const schema_1 = require("./schema");
|
|
44
43
|
/**
|
|
45
44
|
* Serialization format for client-server communication
|
|
@@ -121,7 +120,17 @@ class EkoDBClient {
|
|
|
121
120
|
body: JSON.stringify({ api_key: this.apiKey }),
|
|
122
121
|
});
|
|
123
122
|
if (!response.ok) {
|
|
124
|
-
|
|
123
|
+
let errorMsg = `Auth failed with status: ${response.status}`;
|
|
124
|
+
try {
|
|
125
|
+
const errorBody = (await response.json());
|
|
126
|
+
if (errorBody.error) {
|
|
127
|
+
errorMsg = errorBody.error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Ignore JSON parse errors, use default message
|
|
132
|
+
}
|
|
133
|
+
throw new Error(errorMsg);
|
|
125
134
|
}
|
|
126
135
|
const result = (await response.json());
|
|
127
136
|
this.token = result.token;
|
|
@@ -265,14 +274,24 @@ class EkoDBClient {
|
|
|
265
274
|
* Insert a document into a collection
|
|
266
275
|
* @param collection - Collection name
|
|
267
276
|
* @param record - Document to insert
|
|
268
|
-
* @param
|
|
277
|
+
* @param options - Optional parameters (ttl, bypassRipple, transactionId, bypassCache)
|
|
269
278
|
*/
|
|
270
|
-
async insert(collection, record,
|
|
279
|
+
async insert(collection, record, options) {
|
|
271
280
|
const data = { ...record };
|
|
272
|
-
if (ttl) {
|
|
273
|
-
data.ttl = ttl;
|
|
281
|
+
if (options?.ttl) {
|
|
282
|
+
data.ttl = options.ttl;
|
|
274
283
|
}
|
|
275
|
-
|
|
284
|
+
const params = new URLSearchParams();
|
|
285
|
+
if (options?.bypassRipple !== undefined) {
|
|
286
|
+
params.append("bypass_ripple", String(options.bypassRipple));
|
|
287
|
+
}
|
|
288
|
+
if (options?.transactionId) {
|
|
289
|
+
params.append("transaction_id", options.transactionId);
|
|
290
|
+
}
|
|
291
|
+
const url = params.toString()
|
|
292
|
+
? `/api/insert/${collection}?${params.toString()}`
|
|
293
|
+
: `/api/insert/${collection}`;
|
|
294
|
+
return this.makeRequest("POST", url, data);
|
|
276
295
|
}
|
|
277
296
|
/**
|
|
278
297
|
* Find documents in a collection
|
|
@@ -303,30 +322,67 @@ class EkoDBClient {
|
|
|
303
322
|
/**
|
|
304
323
|
* Find a document by ID
|
|
305
324
|
*/
|
|
306
|
-
async
|
|
325
|
+
async findById(collection, id) {
|
|
307
326
|
return this.makeRequest("GET", `/api/find/${collection}/${id}`);
|
|
308
327
|
}
|
|
309
328
|
/**
|
|
310
329
|
* Update a document
|
|
330
|
+
* @param collection - Collection name
|
|
331
|
+
* @param id - Document ID
|
|
332
|
+
* @param record - Update data
|
|
333
|
+
* @param options - Optional parameters (bypassRipple, transactionId, bypassCache, selectFields, excludeFields)
|
|
311
334
|
*/
|
|
312
|
-
async update(collection, id, record) {
|
|
313
|
-
|
|
335
|
+
async update(collection, id, record, options) {
|
|
336
|
+
const params = new URLSearchParams();
|
|
337
|
+
if (options?.bypassRipple !== undefined) {
|
|
338
|
+
params.append("bypass_ripple", String(options.bypassRipple));
|
|
339
|
+
}
|
|
340
|
+
if (options?.transactionId) {
|
|
341
|
+
params.append("transaction_id", options.transactionId);
|
|
342
|
+
}
|
|
343
|
+
const url = params.toString()
|
|
344
|
+
? `/api/update/${collection}/${id}?${params.toString()}`
|
|
345
|
+
: `/api/update/${collection}/${id}`;
|
|
346
|
+
return this.makeRequest("PUT", url, record);
|
|
314
347
|
}
|
|
315
348
|
/**
|
|
316
349
|
* Delete a document
|
|
350
|
+
* @param collection - Collection name
|
|
351
|
+
* @param id - Document ID
|
|
352
|
+
* @param options - Optional parameters (bypassRipple, transactionId)
|
|
317
353
|
*/
|
|
318
|
-
async delete(collection, id) {
|
|
319
|
-
|
|
354
|
+
async delete(collection, id, options) {
|
|
355
|
+
const params = new URLSearchParams();
|
|
356
|
+
if (options?.bypassRipple !== undefined) {
|
|
357
|
+
params.append("bypass_ripple", String(options.bypassRipple));
|
|
358
|
+
}
|
|
359
|
+
if (options?.transactionId) {
|
|
360
|
+
params.append("transaction_id", options.transactionId);
|
|
361
|
+
}
|
|
362
|
+
const url = params.toString()
|
|
363
|
+
? `/api/delete/${collection}/${id}?${params.toString()}`
|
|
364
|
+
: `/api/delete/${collection}/${id}`;
|
|
365
|
+
await this.makeRequest("DELETE", url);
|
|
320
366
|
}
|
|
321
367
|
/**
|
|
322
368
|
* Batch insert multiple documents
|
|
369
|
+
* @param collection - Collection name
|
|
370
|
+
* @param records - Array of documents to insert
|
|
371
|
+
* @param options - Optional parameters (bypassRipple, transactionId)
|
|
323
372
|
*/
|
|
324
|
-
async batchInsert(collection, records,
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
bypass_ripple
|
|
328
|
-
}
|
|
329
|
-
|
|
373
|
+
async batchInsert(collection, records, options) {
|
|
374
|
+
const params = new URLSearchParams();
|
|
375
|
+
if (options?.bypassRipple !== undefined) {
|
|
376
|
+
params.append("bypass_ripple", String(options.bypassRipple));
|
|
377
|
+
}
|
|
378
|
+
if (options?.transactionId) {
|
|
379
|
+
params.append("transaction_id", options.transactionId);
|
|
380
|
+
}
|
|
381
|
+
const inserts = records.map((data) => ({ data }));
|
|
382
|
+
const url = params.toString()
|
|
383
|
+
? `/api/batch/insert/${collection}?${params.toString()}`
|
|
384
|
+
: `/api/batch/insert/${collection}`;
|
|
385
|
+
return this.makeRequest("POST", url, { inserts });
|
|
330
386
|
}
|
|
331
387
|
/**
|
|
332
388
|
* Batch update multiple documents
|
|
@@ -438,6 +494,139 @@ class EkoDBClient {
|
|
|
438
494
|
async rollbackTransaction(transactionId) {
|
|
439
495
|
await this.makeRequest("POST", `/api/transactions/${encodeURIComponent(transactionId)}/rollback`, undefined, 0, true);
|
|
440
496
|
}
|
|
497
|
+
// ============================================================================
|
|
498
|
+
// Convenience Methods
|
|
499
|
+
// ============================================================================
|
|
500
|
+
/**
|
|
501
|
+
* Insert or update a record (upsert operation)
|
|
502
|
+
*
|
|
503
|
+
* Attempts to update the record first. If the record doesn't exist (404 error),
|
|
504
|
+
* it will be inserted instead. This provides atomic insert-or-update semantics.
|
|
505
|
+
*
|
|
506
|
+
* @param collection - Collection name
|
|
507
|
+
* @param id - Record ID
|
|
508
|
+
* @param record - Record data to insert or update
|
|
509
|
+
* @param bypassRipple - Optional flag to bypass ripple effects
|
|
510
|
+
* @returns The inserted or updated record
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* const record = { name: "John Doe", email: "john@example.com" };
|
|
515
|
+
* // Will update if exists, insert if not
|
|
516
|
+
* const result = await client.upsert("users", "user123", record);
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
/**
|
|
520
|
+
* Upsert a document (insert or update)
|
|
521
|
+
* @param collection - Collection name
|
|
522
|
+
* @param id - Document ID
|
|
523
|
+
* @param record - Document data
|
|
524
|
+
* @param options - Optional parameters (ttl, bypassRipple, transactionId, bypassCache)
|
|
525
|
+
*/
|
|
526
|
+
async upsert(collection, id, record, options) {
|
|
527
|
+
try {
|
|
528
|
+
// Try update first
|
|
529
|
+
return await this.update(collection, id, record, {
|
|
530
|
+
bypassRipple: options?.bypassRipple,
|
|
531
|
+
transactionId: options?.transactionId,
|
|
532
|
+
bypassCache: options?.bypassCache,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
// If not found, insert instead
|
|
537
|
+
if (error.message?.includes("404") ||
|
|
538
|
+
error.message?.includes("Not found")) {
|
|
539
|
+
return await this.insert(collection, record, {
|
|
540
|
+
ttl: options?.ttl,
|
|
541
|
+
bypassRipple: options?.bypassRipple,
|
|
542
|
+
transactionId: options?.transactionId,
|
|
543
|
+
bypassCache: options?.bypassCache,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Find a single record by field value
|
|
551
|
+
*
|
|
552
|
+
* Convenience method for finding one record matching a specific field value.
|
|
553
|
+
* Returns null if no record matches, or the first matching record.
|
|
554
|
+
*
|
|
555
|
+
* @param collection - Collection name
|
|
556
|
+
* @param field - Field name to search
|
|
557
|
+
* @param value - Value to match
|
|
558
|
+
* @returns The matching record or null if not found
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```typescript
|
|
562
|
+
* // Find user by email
|
|
563
|
+
* const user = await client.findOne("users", "email", "john@example.com");
|
|
564
|
+
* if (user) {
|
|
565
|
+
* console.log("Found user:", user);
|
|
566
|
+
* }
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
async findOne(collection, field, value) {
|
|
570
|
+
const query = new query_builder_1.QueryBuilder().eq(field, value).limit(1).build();
|
|
571
|
+
const results = await this.find(collection, query);
|
|
572
|
+
return results.length > 0 ? results[0] : null;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Check if a record exists by ID
|
|
576
|
+
*
|
|
577
|
+
* This is more efficient than fetching the record when you only need to check existence.
|
|
578
|
+
*
|
|
579
|
+
* @param collection - Collection name
|
|
580
|
+
* @param id - Record ID to check
|
|
581
|
+
* @returns true if the record exists, false if it doesn't
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* if (await client.exists("users", "user123")) {
|
|
586
|
+
* console.log("User exists");
|
|
587
|
+
* } else {
|
|
588
|
+
* console.log("User not found");
|
|
589
|
+
* }
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
async exists(collection, id) {
|
|
593
|
+
try {
|
|
594
|
+
await this.findById(collection, id);
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
if (error.message?.includes("404") ||
|
|
599
|
+
error.message?.includes("Not found")) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Paginate through records
|
|
607
|
+
*
|
|
608
|
+
* Convenience method for pagination with page numbers (1-indexed).
|
|
609
|
+
*
|
|
610
|
+
* @param collection - Collection name
|
|
611
|
+
* @param page - Page number (1-indexed, i.e., first page is 1)
|
|
612
|
+
* @param pageSize - Number of records per page
|
|
613
|
+
* @returns Array of records for the requested page
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* ```typescript
|
|
617
|
+
* // Get page 2 with 10 records per page
|
|
618
|
+
* const records = await client.paginate("users", 2, 10);
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
async paginate(collection, page, pageSize) {
|
|
622
|
+
// Page 1 = skip 0, Page 2 = skip pageSize, etc.
|
|
623
|
+
const skip = page > 0 ? (page - 1) * pageSize : 0;
|
|
624
|
+
const query = {
|
|
625
|
+
limit: pageSize,
|
|
626
|
+
skip: skip,
|
|
627
|
+
};
|
|
628
|
+
return this.find(collection, query);
|
|
629
|
+
}
|
|
441
630
|
/**
|
|
442
631
|
* List all collections
|
|
443
632
|
*/
|
|
@@ -451,6 +640,29 @@ class EkoDBClient {
|
|
|
451
640
|
async deleteCollection(collection) {
|
|
452
641
|
await this.makeRequest("DELETE", `/api/collections/${collection}`, undefined, 0, true);
|
|
453
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Restore a deleted record from trash
|
|
645
|
+
* Records remain in trash for 30 days before permanent deletion
|
|
646
|
+
*
|
|
647
|
+
* @param collection - Collection name
|
|
648
|
+
* @param id - Record ID to restore
|
|
649
|
+
* @returns true if restored successfully
|
|
650
|
+
*/
|
|
651
|
+
async restoreRecord(collection, id) {
|
|
652
|
+
const result = await this.makeRequest("POST", `/api/trash/${collection}/${id}`, undefined, 0, true);
|
|
653
|
+
return result.status === "restored";
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Restore all deleted records in a collection from trash
|
|
657
|
+
* Records remain in trash for 30 days before permanent deletion
|
|
658
|
+
*
|
|
659
|
+
* @param collection - Collection name
|
|
660
|
+
* @returns Number of records restored
|
|
661
|
+
*/
|
|
662
|
+
async restoreCollection(collection) {
|
|
663
|
+
const result = await this.makeRequest("POST", `/api/trash/${collection}`, undefined, 0, true);
|
|
664
|
+
return { recordsRestored: result.records_restored };
|
|
665
|
+
}
|
|
454
666
|
/**
|
|
455
667
|
* Create a collection with schema
|
|
456
668
|
*
|
|
@@ -523,11 +735,21 @@ class EkoDBClient {
|
|
|
523
735
|
* );
|
|
524
736
|
* ```
|
|
525
737
|
*/
|
|
526
|
-
async search(collection,
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
738
|
+
async search(collection, query) {
|
|
739
|
+
// Ensure all parameters from SearchQuery are sent to server
|
|
740
|
+
return this.makeRequest("POST", `/api/search/${collection}`, query, 0, true);
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Health check - verify the ekoDB server is responding
|
|
744
|
+
*/
|
|
745
|
+
async health() {
|
|
746
|
+
try {
|
|
747
|
+
const result = await this.makeRequest("GET", "/api/health", undefined, 0, true);
|
|
748
|
+
return result.status === "ok";
|
|
749
|
+
}
|
|
750
|
+
catch {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
531
753
|
}
|
|
532
754
|
// ========== Chat Methods ==========
|
|
533
755
|
/**
|
|
@@ -752,26 +974,29 @@ class EkoDBClient {
|
|
|
752
974
|
* Simplified text search with full-text matching, fuzzy search, and stemming.
|
|
753
975
|
*
|
|
754
976
|
* @param collection - Collection name to search
|
|
755
|
-
* @param
|
|
756
|
-
* @param
|
|
757
|
-
* @returns
|
|
977
|
+
* @param query - Search query text
|
|
978
|
+
* @param options - Additional search options
|
|
979
|
+
* @returns Search response with results and metadata
|
|
758
980
|
*
|
|
759
981
|
* @example
|
|
760
982
|
* ```typescript
|
|
761
983
|
* const results = await client.textSearch(
|
|
762
984
|
* "documents",
|
|
763
985
|
* "ownership system",
|
|
764
|
-
*
|
|
986
|
+
* {
|
|
987
|
+
* limit: 10,
|
|
988
|
+
* select_fields: ["title", "content"],
|
|
989
|
+
* exclude_fields: ["author"]
|
|
990
|
+
* }
|
|
765
991
|
* );
|
|
766
992
|
* ```
|
|
767
993
|
*/
|
|
768
|
-
async textSearch(collection,
|
|
994
|
+
async textSearch(collection, query, options) {
|
|
769
995
|
const searchQuery = {
|
|
770
|
-
query
|
|
771
|
-
|
|
996
|
+
query,
|
|
997
|
+
...options,
|
|
772
998
|
};
|
|
773
|
-
|
|
774
|
-
return response.results.map((r) => r.record);
|
|
999
|
+
return this.search(collection, searchQuery);
|
|
775
1000
|
}
|
|
776
1001
|
/**
|
|
777
1002
|
* Perform hybrid search combining text and vector search
|