@simplysm/orm-common 13.0.98 → 13.0.100

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/docs/queryable.md CHANGED
@@ -1,504 +1,155 @@
1
1
  # Queryable / Executable
2
2
 
3
- Type-safe query builder and stored procedure executor.
3
+ ## `Queryable`
4
4
 
5
- Source: `src/exec/queryable.ts`, `src/exec/executable.ts`, `src/exec/search-parser.ts`
6
-
7
- ## Queryable
8
-
9
- Immutable chaining query builder for SELECT, INSERT, UPDATE, DELETE, and UPSERT operations. Each method returns a new `Queryable` instance.
5
+ Type-safe query builder class. Constructs SELECT, INSERT, UPDATE, DELETE queries on tables/views in a chaining manner.
10
6
 
11
7
  ```typescript
12
- class Queryable<TData extends DataRecord, TFrom extends TableBuilder<any, any> | never> {
8
+ class Queryable<
9
+ TData extends DataRecord,
10
+ TFrom extends TableBuilder<any, any> | never,
11
+ > {
13
12
  constructor(readonly meta: QueryableMeta<TData>);
14
- // ... chaining and execution methods
15
- }
16
- ```
17
-
18
- ### Column Selection
19
-
20
- #### select
21
-
22
- Specify columns to SELECT. Returns a new column structure.
23
-
24
- ```typescript
25
- select<R extends Record<string, any>>(
26
- fn: (columns: QueryableRecord<TData>) => R,
27
- ): Queryable<UnwrapQueryableRecord<R>, never>;
28
- ```
29
-
30
- ```typescript
31
- db.user().select((u) => ({
32
- userName: u.name,
33
- userEmail: u.email,
34
- }))
35
- ```
36
-
37
- #### distinct
38
-
39
- Apply DISTINCT to remove duplicate rows.
40
-
41
- ```typescript
42
- distinct(): Queryable<TData, never>;
43
- ```
44
-
45
- #### lock
46
-
47
- Apply row lock (FOR UPDATE) for exclusive access within a transaction.
48
-
49
- ```typescript
50
- lock(): Queryable<TData, TFrom>;
51
- ```
52
-
53
- ### Pagination
54
-
55
- #### top
56
-
57
- Select only the top N rows (can be used without ORDER BY).
58
-
59
- ```typescript
60
- top(count: number): Queryable<TData, TFrom>;
61
- ```
62
-
63
- #### limit
64
-
65
- Set LIMIT/OFFSET for pagination. Requires a preceding `orderBy()`.
66
-
67
- ```typescript
68
- limit(skip: number, take: number): Queryable<TData, TFrom>;
69
- ```
70
-
71
- ### Sorting
72
-
73
- #### orderBy
74
-
75
- Add an ORDER BY clause. Multiple calls apply in order.
76
-
77
- ```typescript
78
- orderBy(
79
- fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>,
80
- orderBy?: "ASC" | "DESC",
81
- ): Queryable<TData, TFrom>;
82
- ```
83
-
84
- ```typescript
85
- db.user()
86
- .orderBy((u) => u.name)
87
- .orderBy((u) => u.age, "DESC")
88
- ```
89
-
90
- ### Filtering
91
-
92
- #### where
93
-
94
- Add a WHERE condition. Multiple calls are combined with AND.
95
-
96
- ```typescript
97
- where(
98
- predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[],
99
- ): Queryable<TData, TFrom>;
100
- ```
101
-
102
- ```typescript
103
- db.user()
104
- .where((u) => [expr.eq(u.isActive, true)])
105
- .where((u) => [expr.gte(u.age, 18)])
106
- ```
107
-
108
- #### search
109
-
110
- Perform text search across multiple columns. Supports OR, +must, and -exclude syntax.
111
-
112
- ```typescript
113
- search(
114
- fn: (columns: QueryableRecord<TData>) => ExprUnit<string | undefined>[],
115
- searchText: string,
116
- ): Queryable<TData, TFrom>;
117
- ```
118
-
119
- ```typescript
120
- db.user()
121
- .search((u) => [u.name, u.email], "John Doe -withdrawn")
122
- ```
123
-
124
- ### Grouping
125
-
126
- #### groupBy
127
-
128
- Add a GROUP BY clause.
129
-
130
- ```typescript
131
- groupBy(
132
- fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>[],
133
- ): Queryable<TData, never>;
134
- ```
135
-
136
- #### having
137
-
138
- Add a HAVING clause (filtering after GROUP BY).
139
-
140
- ```typescript
141
- having(
142
- predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[],
143
- ): Queryable<TData, never>;
144
- ```
145
-
146
- ```typescript
147
- db.order()
148
- .select((o) => ({
149
- userId: o.userId,
150
- totalAmount: expr.sum(o.amount),
151
- }))
152
- .groupBy((o) => [o.userId])
153
- .having((o) => [expr.gte(o.totalAmount, 10000)])
154
- ```
155
-
156
- ### Joins
157
-
158
- #### join
159
-
160
- LEFT OUTER JOIN for 1:N relations (result added as array).
161
-
162
- ```typescript
163
- join<A extends string, R extends DataRecord>(
164
- as: A,
165
- fn: (qr: JoinQueryable, cols: QueryableRecord<TData>) => Queryable<R, any>,
166
- ): Queryable<TData & { [K in A]?: R[] }, TFrom>;
167
- ```
168
-
169
- ```typescript
170
- db.user()
171
- .join("posts", (qr, u) =>
172
- qr.from(Post).where((p) => [expr.eq(p.userId, u.id)])
173
- )
174
- // Result: { id, name, posts: [{ id, title }, ...] }
175
- ```
176
-
177
- #### joinSingle
178
-
179
- LEFT OUTER JOIN for N:1 or 1:1 relations (result added as single object).
180
-
181
- ```typescript
182
- joinSingle<A extends string, R extends DataRecord>(
183
- as: A,
184
- fn: (qr: JoinQueryable, cols: QueryableRecord<TData>) => Queryable<R, any>,
185
- ): Queryable<TData & { [K in A]?: R }, TFrom>;
186
- ```
187
-
188
- ```typescript
189
- db.post()
190
- .joinSingle("user", (qr, p) =>
191
- qr.from(User).where((u) => [expr.eq(u.id, p.userId)])
192
- )
193
- // Result: { id, title, user: { id, name } | undefined }
194
- ```
195
-
196
- #### include
197
-
198
- Automatically JOIN related tables based on FK/FKT relations defined in `TableBuilder`.
199
-
200
- ```typescript
201
- include(fn: (item: PathProxy<TData>) => PathProxy<any>): Queryable<TData, TFrom>;
202
- ```
203
-
204
- ```typescript
205
- // Single relation
206
- db.post().include((p) => p.author)
207
-
208
- // Nested relation
209
- db.post().include((p) => p.author.company)
210
-
211
- // Multiple relations
212
- db.user()
213
- .include((u) => u.company)
214
- .include((u) => u.posts)
215
- ```
216
-
217
- ### Subqueries
218
-
219
- #### wrap
220
-
221
- Wrap the current Queryable as a subquery. Required when using `count()` after `distinct()` or `groupBy()`.
222
-
223
- ```typescript
224
- wrap(): Queryable<TData, never>;
225
- ```
226
-
227
- #### union (static)
228
-
229
- Combine multiple Queryables with UNION (removes duplicates). Requires at least 2 queryables.
230
-
231
- ```typescript
232
- static union<TData extends DataRecord>(
233
- ...queries: Queryable<TData, any>[]
234
- ): Queryable<TData, never>;
235
- ```
236
-
237
- ```typescript
238
- const combined = Queryable.union(
239
- db.user().where((u) => [expr.eq(u.type, "admin")]),
240
- db.user().where((u) => [expr.eq(u.type, "manager")]),
241
- );
242
- ```
243
-
244
- ### Recursive CTE
245
-
246
- #### recursive
247
13
 
248
- Generate a recursive CTE (Common Table Expression) for hierarchical data.
249
-
250
- ```typescript
251
- recursive(
252
- fn: (qr: RecursiveQueryable<TData>) => Queryable<TData, any>,
253
- ): Queryable<TData, never>;
254
- ```
255
-
256
- ```typescript
257
- db.employee()
258
- .where((e) => [expr.null(e.managerId)])
259
- .recursive((cte) =>
260
- cte.from(Employee)
261
- .where((e) => [expr.eq(e.managerId, e.self![0].id)])
262
- )
263
- ```
264
-
265
- ### Execution Methods
266
-
267
- #### execute
268
-
269
- Execute a SELECT query and return the result array.
270
-
271
- ```typescript
272
- async execute(): Promise<TData[]>;
273
- ```
274
-
275
- #### single
276
-
277
- Return a single result. Throws if more than one result is returned.
278
-
279
- ```typescript
280
- async single(): Promise<TData | undefined>;
281
- ```
282
-
283
- #### first
284
-
285
- Return the first result (only the first even if multiple exist).
286
-
287
- ```typescript
288
- async first(): Promise<TData | undefined>;
289
- ```
290
-
291
- #### count
292
-
293
- Return the number of result rows. Cannot be called directly after `distinct()` or `groupBy()` -- use `wrap()` first.
294
-
295
- ```typescript
296
- async count(
297
- fn?: (cols: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>,
298
- ): Promise<number>;
299
- ```
300
-
301
- #### exists
302
-
303
- Check whether any data matching the conditions exists.
304
-
305
- ```typescript
306
- async exists(): Promise<boolean>;
307
- ```
308
-
309
- ### Mutation Methods
310
-
311
- #### insert
312
-
313
- Execute an INSERT query. Automatically splits into chunks of 1000 for MSSQL.
314
-
315
- ```typescript
316
- async insert(records: TFrom["$inferInsert"][]): Promise<void>;
317
- async insert<K extends keyof TFrom["$inferColumns"] & string>(
318
- records: TFrom["$inferInsert"][],
319
- outputColumns: K[],
320
- ): Promise<Pick<TFrom["$inferColumns"], K>[]>;
321
- ```
322
-
323
- ```typescript
324
- // Simple insert
325
- await db.user().insert([{ name: "Alice", createdAt: DateTime.now() }]);
326
-
327
- // Insert with output
328
- const [inserted] = await db.user().insert(
329
- [{ name: "Alice" }],
330
- ["id"],
331
- );
332
- ```
333
-
334
- #### insertIfNotExists
335
-
336
- INSERT only if no data matches the current WHERE condition.
337
-
338
- ```typescript
339
- async insertIfNotExists(record: TFrom["$inferInsert"]): Promise<void>;
340
- async insertIfNotExists<K extends keyof TFrom["$inferColumns"] & string>(
341
- record: TFrom["$inferInsert"],
342
- outputColumns: K[],
343
- ): Promise<Pick<TFrom["$inferColumns"], K>>;
344
- ```
345
-
346
- #### insertInto
347
-
348
- INSERT INTO ... SELECT -- insert the current SELECT results into another table.
349
-
350
- ```typescript
351
- async insertInto<TTable extends TableBuilder<DataToColumnBuilderRecord<TData>, any>>(
352
- targetTable: TTable,
353
- ): Promise<void>;
354
- async insertInto<TTable, TOut extends keyof TTable["$inferColumns"] & string>(
355
- targetTable: TTable,
356
- outputColumns: TOut[],
357
- ): Promise<Pick<TData, TOut>[]>;
358
- ```
359
-
360
- #### update
361
-
362
- Execute an UPDATE query.
363
-
364
- ```typescript
365
- async update(
366
- recordFwd: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
367
- ): Promise<void>;
368
- async update<K extends keyof TFrom["$columns"] & string>(
369
- recordFwd: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
370
- outputColumns: K[],
371
- ): Promise<Pick<TFrom["$columns"], K>[]>;
372
- ```
373
-
374
- ```typescript
375
- await db.user()
376
- .where((u) => [expr.eq(u.id, 1)])
377
- .update((u) => ({
378
- name: expr.val("string", "New Name"),
379
- }));
380
- ```
381
-
382
- #### delete
383
-
384
- Execute a DELETE query.
385
-
386
- ```typescript
387
- async delete(): Promise<void>;
388
- async delete<K extends keyof TFrom["$columns"] & string>(
389
- outputColumns: K[],
390
- ): Promise<Pick<TFrom["$columns"], K>[]>;
391
- ```
392
-
393
- #### upsert
394
-
395
- Execute an UPSERT (UPDATE or INSERT) query.
396
-
397
- ```typescript
398
- async upsert(
399
- updateFn: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
400
- ): Promise<void>;
401
- async upsert<U extends QueryableWriteRecord<TFrom["$inferUpdate"]>>(
402
- updateFn: (cols: QueryableRecord<TData>) => U,
403
- insertFn: (updateRecord: U) => QueryableWriteRecord<TFrom["$inferInsert"]>,
404
- ): Promise<void>;
405
- ```
406
-
407
- ```typescript
408
- // Same data for update and insert
409
- await db.user()
410
- .where((u) => [expr.eq(u.email, "test@test.com")])
411
- .upsert(() => ({
412
- name: expr.val("string", "Test"),
413
- email: expr.val("string", "test@test.com"),
414
- }));
415
-
416
- // Different data for update vs insert
417
- await db.user()
418
- .where((u) => [expr.eq(u.email, "test@test.com")])
419
- .upsert(
420
- () => ({ loginCount: expr.val("number", 1) }),
421
- (update) => ({ ...update, email: expr.val("string", "test@test.com") }),
422
- );
423
- ```
424
-
425
- ### DDL Helper
426
-
427
- #### switchFk
428
-
429
- Enable or disable FK constraints on the table (usable within a transaction).
430
-
431
- ```typescript
432
- async switchFk(enabled: boolean): Promise<void>;
14
+ // SELECT / DISTINCT / LOCK
15
+ select<R extends Record<string, any>>(
16
+ fn: (columns: QueryableRecord<TData>) => R,
17
+ ): Queryable<UnwrapQueryableRecord<R>, never>;
18
+ distinct(): Queryable<TData, never>;
19
+ lock(): Queryable<TData, TFrom>;
20
+
21
+ // TOP / LIMIT
22
+ top(count: number): Queryable<TData, TFrom>;
23
+ limit(skip: number, take: number): Queryable<TData, TFrom>;
24
+
25
+ // ORDER BY
26
+ orderBy(
27
+ fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>,
28
+ orderBy?: "ASC" | "DESC",
29
+ ): Queryable<TData, TFrom>;
30
+
31
+ // WHERE
32
+ where(predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[]): Queryable<TData, TFrom>;
33
+ search(
34
+ fn: (columns: QueryableRecord<TData>) => ExprUnit<string | undefined>[],
35
+ searchText: string,
36
+ ): Queryable<TData, TFrom>;
37
+
38
+ // GROUP BY / HAVING
39
+ groupBy<R extends Record<string, any>>(
40
+ fn: (columns: QueryableRecord<TData>) => R,
41
+ ): Queryable<UnwrapQueryableRecord<R>, never>;
42
+ having(predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[]): Queryable<TData, TFrom>;
43
+
44
+ // JOIN
45
+ join<TKey extends string, TJoinData extends DataRecord>(
46
+ key: TKey,
47
+ joinFn: (j: JoinQueryable) => Queryable<TJoinData, any>,
48
+ on?: (own: QueryableRecord<TData>, join: QueryableRecord<TJoinData>) => WhereExprUnit[],
49
+ ): Queryable<TData & { [K in TKey]?: TJoinData[] }, TFrom>;
50
+ joinSingle<TKey extends string, TJoinData extends DataRecord>(
51
+ key: TKey,
52
+ joinFn: (j: JoinQueryable) => Queryable<TJoinData, any>,
53
+ on?: (own: QueryableRecord<TData>, join: QueryableRecord<TJoinData>) => WhereExprUnit[],
54
+ ): Queryable<TData & { [K in TKey]?: TJoinData }, TFrom>;
55
+ include<TKey extends string & keyof TData>(key: TKey): Queryable<TData, TFrom>;
56
+
57
+ // RECURSIVE
58
+ recursive<TBaseData extends DataRecord>(
59
+ baseQueryFn: (q: Queryable<TData, TFrom>) => Queryable<TBaseData, any>,
60
+ recursiveBodyFn: (cte: RecursiveQueryable<TBaseData>) => Queryable<any, any>,
61
+ ): Queryable<TBaseData, never>;
62
+
63
+ // EXECUTE (SELECT)
64
+ execute(): Promise<TData[]>;
65
+ single(): Promise<TData | undefined>;
66
+ count(): Promise<number>;
67
+ exists(): Promise<boolean>;
68
+
69
+ // QueryDef generation
70
+ getSelectQueryDef(): SelectQueryDef;
71
+ getResultMeta(): ResultMeta;
72
+
73
+ // CUD (requires TFrom to be TableBuilder)
74
+ insert(records: TFrom["$inferInsert"][], options?: { overrideIdentity?: boolean }): Promise<TFrom["$inferColumns"][]>;
75
+ insertIfNotExists(record: TFrom["$inferInsert"][]): Promise<TFrom["$inferColumns"][]>;
76
+ insertInto(subQuery: Queryable<any, any>, options?: { overrideIdentity?: boolean }): Promise<TFrom["$inferColumns"][]>;
77
+ update(fn: (columns: QueryableRecord<TData>) => Partial<...>): Promise<TFrom["$inferColumns"][]>;
78
+ upsert(insertRecord: TFrom["$inferInsert"], updateFn: (columns: ...) => Partial<...>): Promise<TFrom["$inferColumns"][]>;
79
+ delete(): Promise<TFrom["$inferColumns"][]>;
80
+ }
433
81
  ```
434
82
 
435
- ### QueryDef Generators
436
-
437
- These methods return the raw `QueryDef` JSON AST without executing:
438
-
439
- - `getSelectQueryDef(): SelectQueryDef`
440
- - `getInsertQueryDef(records, outputColumns?): InsertQueryDef`
441
- - `getInsertIfNotExistsQueryDef(record, outputColumns?): InsertIfNotExistsQueryDef`
442
- - `getInsertIntoQueryDef(targetTable, outputColumns?): InsertIntoQueryDef`
443
- - `getUpdateQueryDef(recordFwd, outputColumns?): UpdateQueryDef`
444
- - `getDeleteQueryDef(outputColumns?): DeleteQueryDef`
445
- - `getUpsertQueryDef(updateRecordFn, insertRecordFn, outputColumns?): UpsertQueryDef`
446
- - `getResultMeta(outputColumns?): ResultMeta`
447
-
448
- ---
449
-
450
- ## queryable (factory function)
451
-
452
- Create a Queryable factory function for a table or view. A new alias is assigned on each call.
453
-
454
- ```typescript
455
- function queryable<TBuilder extends TableBuilder<any, any> | ViewBuilder<any, any, any>>(
83
+ ### Key Methods
84
+
85
+ | Method | Description |
86
+ |--------|-------------|
87
+ | `select()` | Specify columns to SELECT |
88
+ | `distinct()` | Remove duplicate rows |
89
+ | `lock()` | Apply row lock (FOR UPDATE) |
90
+ | `top()` | Select only top N rows |
91
+ | `limit()` | Set LIMIT/OFFSET for pagination (requires `orderBy()`) |
92
+ | `orderBy()` | Add sorting condition (multiple calls apply in order) |
93
+ | `where()` | Add WHERE condition (multiple calls combined with AND) |
94
+ | `search()` | Perform text search with LIKE patterns |
95
+ | `groupBy()` | Group results |
96
+ | `having()` | Add HAVING condition |
97
+ | `join()` | LEFT JOIN with 1:N result (array) |
98
+ | `joinSingle()` | LEFT JOIN with 1:1 result (single object) |
99
+ | `include()` | Include pre-defined relation from schema |
100
+ | `recursive()` | Build recursive CTE query |
101
+ | `execute()` | Execute SELECT and return results |
102
+ | `single()` | Execute SELECT and return first result |
103
+ | `count()` | Return record count |
104
+ | `exists()` | Check if records exist |
105
+ | `insert()` | INSERT records |
106
+ | `insertIfNotExists()` | INSERT only if not exists |
107
+ | `insertInto()` | INSERT from subquery |
108
+ | `update()` | UPDATE matching records |
109
+ | `upsert()` | INSERT or UPDATE |
110
+ | `delete()` | DELETE matching records |
111
+
112
+ ## `queryable`
113
+
114
+ Factory function to create a Queryable accessor for DbContext.
115
+
116
+ ```typescript
117
+ function queryable<T extends TableBuilder<any, any> | ViewBuilder<any, any, any>>(
456
118
  db: DbContextBase,
457
- tableOrView: TBuilder,
458
- as?: string,
459
- ): () => Queryable<TBuilder["$inferSelect"], TBuilder extends TableBuilder<any, any> ? TBuilder : never>;
119
+ builder: T,
120
+ alias?: string,
121
+ ): () => Queryable<T["$inferSelect"], T extends TableBuilder<any, any> ? T : never>;
460
122
  ```
461
123
 
462
- ---
463
-
464
- ## getMatchedPrimaryKeys
124
+ ## `Executable`
465
125
 
466
- Match FK column array with the target table's PK and return PK column name array.
126
+ Stored procedure execution wrapper class.
467
127
 
468
128
  ```typescript
469
- function getMatchedPrimaryKeys(
470
- fkCols: string[],
471
- targetTable: TableBuilder<any, any>,
472
- ): string[];
473
- ```
474
-
475
- Throws if FK/PK column count does not match.
476
-
477
- ---
478
-
479
- ## Executable
129
+ class Executable<TParams extends ColumnBuilderRecord, TReturns extends ColumnBuilderRecord> {
130
+ constructor(
131
+ private readonly _db: DbContextBase,
132
+ private readonly _builder: ProcedureBuilder<TParams, TReturns>,
133
+ );
480
134
 
481
- Stored procedure execution wrapper.
135
+ getExecProcQueryDef(params?: InferColumnExprs<TParams>): {
136
+ type: "execProc";
137
+ procedure: { database?: string; schema?: string; name: string };
138
+ params?: Record<string, Expr>;
139
+ };
482
140
 
483
- ```typescript
484
- class Executable<
485
- TParams extends ColumnBuilderRecord,
486
- TReturns extends ColumnBuilderRecord,
487
- > {
488
- getExecProcQueryDef(params?: InferColumnExprs<TParams>): ExecProcQueryDef;
489
- async execute(params: InferColumnExprs<TParams>): Promise<InferColumnExprs<TReturns>[][]>;
141
+ execute(params: InferColumnExprs<TParams>): Promise<InferColumnExprs<TReturns>[][]>;
490
142
  }
491
143
  ```
492
144
 
493
- ```typescript
494
- const result = await db.getUserById().execute({ userId: 1n });
495
- ```
145
+ | Method | Description |
146
+ |--------|-------------|
147
+ | `getExecProcQueryDef()` | Generate procedure execution QueryDef |
148
+ | `execute()` | Execute the stored procedure |
496
149
 
497
- ---
150
+ ## `executable`
498
151
 
499
- ## executable (factory function)
500
-
501
- Create an Executable factory function for a procedure.
152
+ Factory function to create an Executable accessor for DbContext.
502
153
 
503
154
  ```typescript
504
155
  function executable<
@@ -510,29 +161,14 @@ function executable<
510
161
  ): () => Executable<TParams, TReturns>;
511
162
  ```
512
163
 
513
- ---
514
-
515
- ## parseSearchQuery
164
+ ## `parseSearchQuery`
516
165
 
517
- Parse a search query string into SQL LIKE patterns.
166
+ Parse a search query string and convert to SQL LIKE patterns.
518
167
 
519
168
  ```typescript
520
169
  function parseSearchQuery(searchText: string): ParsedSearchQuery;
521
170
  ```
522
171
 
523
- ### ParsedSearchQuery
524
-
525
- ```typescript
526
- interface ParsedSearchQuery {
527
- /** General search terms (OR condition) - LIKE pattern */
528
- or: string[];
529
- /** Required search terms (AND condition, + prefix or quotes) - LIKE pattern */
530
- must: string[];
531
- /** Excluded search terms (NOT condition, - prefix) - LIKE pattern */
532
- not: string[];
533
- }
534
- ```
535
-
536
172
  ### Search Syntax
537
173
 
538
174
  | Syntax | Meaning | Example |
@@ -543,36 +179,20 @@ interface ParsedSearchQuery {
543
179
  | `"exact phrase"` | Exact match (required) | `"delicious fruit"` |
544
180
  | `*` | Wildcard | `app*` -> `app%` |
545
181
 
546
- ### Escape Sequences
547
-
548
- | Input | Meaning |
549
- |-------|---------|
550
- | `\\` | Literal `\` |
551
- | `\*` | Literal `*` |
552
- | `\%` | Literal `%` |
553
- | `\"` | Literal `"` |
554
- | `\+` | Literal `+` |
555
- | `\-` | Literal `-` |
182
+ ## `ParsedSearchQuery`
556
183
 
557
- **Example:**
184
+ Search query parsing result.
558
185
 
559
186
  ```typescript
560
- parseSearchQuery('apple "delicious fruit" -banana +strawberry')
561
- // {
562
- // or: ["%apple%"],
563
- // must: ["%delicious fruit%", "%strawberry%"],
564
- // not: ["%banana%"]
565
- // }
187
+ interface ParsedSearchQuery {
188
+ or: string[];
189
+ must: string[];
190
+ not: string[];
191
+ }
566
192
  ```
567
193
 
568
- ---
569
-
570
- ## Type Helpers
571
-
572
- | Type | Description |
573
- |------|-------------|
574
- | `QueryableRecord<TData>` | Maps `TData` fields to `ExprUnit` wrappers (for column references in callbacks) |
575
- | `QueryableWriteRecord<TData>` | Maps `TData` fields to `ExprInput` (for UPDATE/INSERT value callbacks) |
576
- | `NullableQueryableRecord<TData>` | Like `QueryableRecord` but all primitives are `| undefined` (LEFT JOIN null propagation) |
577
- | `UnwrapQueryableRecord<R>` | Reverse-transform from `QueryableRecord` back to `DataRecord` |
578
- | `PathProxy<TObject>` | Type-safe path proxy for `include()` -- only non-primitive fields are accessible |
194
+ | Field | Type | Description |
195
+ |-------|------|-------------|
196
+ | `or` | `string[]` | General search terms (OR condition) - LIKE pattern |
197
+ | `must` | `string[]` | Required search terms (AND condition, + prefix or quotes) - LIKE pattern |
198
+ | `not` | `string[]` | Excluded search terms (NOT condition, - prefix) - LIKE pattern |