@ekodb/ekodb-client 0.1.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/src/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ export { EkoDBClient, WebSocketClient, MergeStrategy, RateLimitError } from './client';
2
+ export { QueryBuilder, SortOrder } from './query-builder';
3
+ export { SearchQueryBuilder } from './search';
4
+ export { SchemaBuilder, FieldTypeSchemaBuilder, VectorIndexAlgorithm, DistanceMetric } from './schema';
5
+ export { JoinBuilder } from './join';
6
+ export type { SearchQuery, SearchResult, SearchResponse } from './search';
7
+ export type { Schema, FieldTypeSchema, IndexConfig, CollectionMetadata } from './schema';
8
+ export type { JoinConfig } from './join';
9
+ export type {
10
+ Record,
11
+ Query,
12
+ BatchOperationResult,
13
+ ClientConfig,
14
+ RateLimitInfo,
15
+ CollectionConfig,
16
+ ChatRequest,
17
+ CreateChatSessionRequest,
18
+ ChatMessageRequest,
19
+ TokenUsage,
20
+ ChatResponse,
21
+ ChatSession,
22
+ ChatSessionResponse,
23
+ ListSessionsQuery,
24
+ ListSessionsResponse,
25
+ GetMessagesQuery,
26
+ GetMessagesResponse,
27
+ UpdateSessionRequest,
28
+ MergeSessionsRequest
29
+ } from './client';
package/src/join.ts ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Join support for multi-collection queries
3
+ *
4
+ * This module provides support for joining data across multiple collections,
5
+ * similar to SQL joins but with document-oriented semantics.
6
+ */
7
+
8
+ /**
9
+ * Configuration for joining collections
10
+ */
11
+ export interface JoinConfig {
12
+ /** Target collections to join with */
13
+ collections: string[];
14
+ /** Field in the current collection */
15
+ local_field: string;
16
+ /** Field in the target collection(s) */
17
+ foreign_field: string;
18
+ /** Name of the field to store joined data */
19
+ as_field: string;
20
+ }
21
+
22
+ /**
23
+ * Builder for constructing join configurations
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Single collection join
28
+ * const join = JoinBuilder.single("users", "user_id", "id", "user");
29
+ *
30
+ * // Multi-collection join
31
+ * const join = JoinBuilder.multi(
32
+ * ["users", "profiles", "settings"],
33
+ * "user_id",
34
+ * "id",
35
+ * "user_info"
36
+ * );
37
+ *
38
+ * // Use in query
39
+ * const query = new QueryBuilder()
40
+ * .eq("status", "active")
41
+ * .join(join)
42
+ * .build();
43
+ * ```
44
+ */
45
+ export class JoinBuilder {
46
+ private config: JoinConfig;
47
+
48
+ private constructor(
49
+ collections: string[],
50
+ localField: string,
51
+ foreignField: string,
52
+ asField: string
53
+ ) {
54
+ this.config = {
55
+ collections,
56
+ local_field: localField,
57
+ foreign_field: foreignField,
58
+ as_field: asField,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Create a join with a single collection
64
+ *
65
+ * @param collection - Target collection name
66
+ * @param localField - Field in the current collection
67
+ * @param foreignField - Field in the target collection
68
+ * @param asField - Name of the field to store joined data
69
+ */
70
+ static single(
71
+ collection: string,
72
+ localField: string,
73
+ foreignField: string,
74
+ asField: string
75
+ ): JoinConfig {
76
+ return new JoinBuilder([collection], localField, foreignField, asField).build();
77
+ }
78
+
79
+ /**
80
+ * Create a join with multiple collections
81
+ *
82
+ * @param collections - Array of target collection names
83
+ * @param localField - Field in the current collection
84
+ * @param foreignField - Field in the target collections
85
+ * @param asField - Name of the field to store joined data
86
+ */
87
+ static multi(
88
+ collections: string[],
89
+ localField: string,
90
+ foreignField: string,
91
+ asField: string
92
+ ): JoinConfig {
93
+ return new JoinBuilder(collections, localField, foreignField, asField).build();
94
+ }
95
+
96
+ /**
97
+ * Build the final JoinConfig
98
+ */
99
+ build(): JoinConfig {
100
+ return this.config;
101
+ }
102
+ }
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Query Builder for constructing complex queries with fluent API
3
+ */
4
+
5
+ export enum SortOrder {
6
+ Asc = "asc",
7
+ Desc = "desc",
8
+ }
9
+
10
+ export interface Query {
11
+ filter?: any;
12
+ sort?: Array<{ field: string; ascending: boolean }>;
13
+ limit?: number;
14
+ skip?: number;
15
+ join?: any;
16
+ bypass_cache?: boolean;
17
+ bypass_ripple?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Fluent API for building complex database queries
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const query = new QueryBuilder()
26
+ * .eq("status", "active")
27
+ * .gt("age", 18)
28
+ * .sortDesc("created_at")
29
+ * .limit(10)
30
+ * .build();
31
+ * ```
32
+ */
33
+ export class QueryBuilder {
34
+ private filters: any[] = [];
35
+ private sortFields: Array<{ field: string; order: SortOrder }> = [];
36
+ private _limit?: number;
37
+ private _skip?: number;
38
+ private _join?: any;
39
+ private _bypassCache: boolean = false;
40
+ private _bypassRipple: boolean = false;
41
+
42
+ // ========================================================================
43
+ // Comparison Operators
44
+ // ========================================================================
45
+
46
+ /**
47
+ * Add an equality filter ($eq)
48
+ */
49
+ eq(field: string, value: any): this {
50
+ this.filters.push({
51
+ type: "Condition",
52
+ content: {
53
+ field,
54
+ operator: "Eq",
55
+ value,
56
+ },
57
+ });
58
+ return this;
59
+ }
60
+
61
+ /**
62
+ * Add a not-equal filter ($ne)
63
+ */
64
+ ne(field: string, value: any): this {
65
+ this.filters.push({
66
+ type: "Condition",
67
+ content: {
68
+ field,
69
+ operator: "Ne",
70
+ value,
71
+ },
72
+ });
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * Add a greater-than filter ($gt)
78
+ */
79
+ gt(field: string, value: any): this {
80
+ this.filters.push({
81
+ type: "Condition",
82
+ content: {
83
+ field,
84
+ operator: "Gt",
85
+ value,
86
+ },
87
+ });
88
+ return this;
89
+ }
90
+
91
+ /**
92
+ * Add a greater-than-or-equal filter ($gte)
93
+ */
94
+ gte(field: string, value: any): this {
95
+ this.filters.push({
96
+ type: "Condition",
97
+ content: {
98
+ field,
99
+ operator: "Gte",
100
+ value,
101
+ },
102
+ });
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * Add a less-than filter ($lt)
108
+ */
109
+ lt(field: string, value: any): this {
110
+ this.filters.push({
111
+ type: "Condition",
112
+ content: {
113
+ field,
114
+ operator: "Lt",
115
+ value,
116
+ },
117
+ });
118
+ return this;
119
+ }
120
+
121
+ /**
122
+ * Add a less-than-or-equal filter ($lte)
123
+ */
124
+ lte(field: string, value: any): this {
125
+ this.filters.push({
126
+ type: "Condition",
127
+ content: {
128
+ field,
129
+ operator: "Lte",
130
+ value,
131
+ },
132
+ });
133
+ return this;
134
+ }
135
+
136
+ /**
137
+ * Add an in-array filter ($in)
138
+ */
139
+ in(field: string, values: any[]): this {
140
+ this.filters.push({
141
+ type: "Condition",
142
+ content: {
143
+ field,
144
+ operator: "In",
145
+ value: values,
146
+ },
147
+ });
148
+ return this;
149
+ }
150
+
151
+ /**
152
+ * Add a not-in-array filter ($nin)
153
+ */
154
+ nin(field: string, values: any[]): this {
155
+ this.filters.push({
156
+ type: "Condition",
157
+ content: {
158
+ field,
159
+ operator: "NotIn",
160
+ value: values,
161
+ },
162
+ });
163
+ return this;
164
+ }
165
+
166
+ // ========================================================================
167
+ // String Operators
168
+ // ========================================================================
169
+
170
+ /**
171
+ * Add a contains filter (substring match)
172
+ */
173
+ contains(field: string, substring: string): this {
174
+ this.filters.push({
175
+ type: "Condition",
176
+ content: {
177
+ field,
178
+ operator: "Contains",
179
+ value: substring,
180
+ },
181
+ });
182
+ return this;
183
+ }
184
+
185
+ /**
186
+ * Add a starts-with filter
187
+ */
188
+ startsWith(field: string, prefix: string): this {
189
+ this.filters.push({
190
+ type: "Condition",
191
+ content: {
192
+ field,
193
+ operator: "StartsWith",
194
+ value: prefix,
195
+ },
196
+ });
197
+ return this;
198
+ }
199
+
200
+ /**
201
+ * Add an ends-with filter
202
+ */
203
+ endsWith(field: string, suffix: string): this {
204
+ this.filters.push({
205
+ type: "Condition",
206
+ content: {
207
+ field,
208
+ operator: "EndsWith",
209
+ value: suffix,
210
+ },
211
+ });
212
+ return this;
213
+ }
214
+
215
+ /**
216
+ * Add a regex filter
217
+ */
218
+ regex(field: string, pattern: string): this {
219
+ this.filters.push({
220
+ type: "Condition",
221
+ content: {
222
+ field,
223
+ operator: "Regex",
224
+ value: pattern,
225
+ },
226
+ });
227
+ return this;
228
+ }
229
+
230
+ // ========================================================================
231
+ // Logical Operators
232
+ // ========================================================================
233
+
234
+ /**
235
+ * Combine filters with AND logic
236
+ */
237
+ and(conditions: any[]): this {
238
+ this.filters.push({
239
+ type: "Logical",
240
+ content: {
241
+ operator: "And",
242
+ expressions: conditions,
243
+ },
244
+ });
245
+ return this;
246
+ }
247
+
248
+ /**
249
+ * Combine filters with OR logic
250
+ */
251
+ or(conditions: any[]): this {
252
+ this.filters.push({
253
+ type: "Logical",
254
+ content: {
255
+ operator: "Or",
256
+ expressions: conditions,
257
+ },
258
+ });
259
+ return this;
260
+ }
261
+
262
+ /**
263
+ * Negate a filter
264
+ */
265
+ not(condition: any): this {
266
+ this.filters.push({
267
+ type: "Logical",
268
+ content: {
269
+ operator: "Not",
270
+ expressions: [condition],
271
+ },
272
+ });
273
+ return this;
274
+ }
275
+
276
+ /**
277
+ * Add a raw filter expression
278
+ */
279
+ rawFilter(filter: any): this {
280
+ this.filters.push(filter);
281
+ return this;
282
+ }
283
+
284
+ // ========================================================================
285
+ // Sorting
286
+ // ========================================================================
287
+
288
+ /**
289
+ * Add a sort field in ascending order
290
+ */
291
+ sortAsc(field: string): this {
292
+ this.sortFields.push({ field, order: SortOrder.Asc });
293
+ return this;
294
+ }
295
+
296
+ /**
297
+ * Add a sort field in descending order
298
+ */
299
+ sortDesc(field: string): this {
300
+ this.sortFields.push({ field, order: SortOrder.Desc });
301
+ return this;
302
+ }
303
+
304
+ // ========================================================================
305
+ // Pagination
306
+ // ========================================================================
307
+
308
+ /**
309
+ * Set the maximum number of results
310
+ */
311
+ limit(limit: number): this {
312
+ this._limit = limit;
313
+ return this;
314
+ }
315
+
316
+ /**
317
+ * Set the number of results to skip (for pagination)
318
+ */
319
+ skip(skip: number): this {
320
+ this._skip = skip;
321
+ return this;
322
+ }
323
+
324
+ /**
325
+ * Set page number and page size (convenience method)
326
+ */
327
+ page(page: number, pageSize: number): this {
328
+ this._skip = page * pageSize;
329
+ this._limit = pageSize;
330
+ return this;
331
+ }
332
+
333
+ // ========================================================================
334
+ // Joins
335
+ // ========================================================================
336
+
337
+ /**
338
+ * Add a join configuration
339
+ */
340
+ join(joinConfig: any): this {
341
+ this._join = joinConfig;
342
+ return this;
343
+ }
344
+
345
+ // ========================================================================
346
+ // Performance Flags
347
+ // ========================================================================
348
+
349
+ /**
350
+ * Bypass cache for this query
351
+ */
352
+ bypassCache(bypass: boolean = true): this {
353
+ this._bypassCache = bypass;
354
+ return this;
355
+ }
356
+
357
+ /**
358
+ * Bypass ripple for this query
359
+ */
360
+ bypassRipple(bypass: boolean = true): this {
361
+ this._bypassRipple = bypass;
362
+ return this;
363
+ }
364
+
365
+ // ========================================================================
366
+ // Build
367
+ // ========================================================================
368
+
369
+ /**
370
+ * Build the final Query object
371
+ */
372
+ build(): Query {
373
+ const query: Query = {};
374
+
375
+ // Combine all filters with AND logic if multiple filters exist
376
+ if (this.filters.length > 0) {
377
+ query.filter =
378
+ this.filters.length === 1
379
+ ? this.filters[0]
380
+ : {
381
+ type: "Logical",
382
+ content: {
383
+ operator: "And",
384
+ expressions: this.filters,
385
+ },
386
+ };
387
+ }
388
+
389
+ // Build sort expression
390
+ if (this.sortFields.length > 0) {
391
+ query.sort = this.sortFields.map(({ field, order }) => ({
392
+ field,
393
+ ascending: order === SortOrder.Asc,
394
+ }));
395
+ }
396
+
397
+ if (this._limit !== undefined) {
398
+ query.limit = this._limit;
399
+ }
400
+
401
+ if (this._skip !== undefined) {
402
+ query.skip = this._skip;
403
+ }
404
+
405
+ if (this._join !== undefined) {
406
+ query.join = this._join;
407
+ }
408
+
409
+ if (this._bypassCache) {
410
+ query.bypass_cache = true;
411
+ }
412
+
413
+ if (this._bypassRipple) {
414
+ query.bypass_ripple = true;
415
+ }
416
+
417
+ return query;
418
+ }
419
+ }