kysely-hydrate 0.8.0 → 0.9.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 +48 -0
- package/dist/index.d.mts +40 -9
- package/dist/index.mjs +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,6 +130,7 @@ type Result = Array<{
|
|
|
130
130
|
- [Sorting with `.orderBy()`](#sorting-with-orderby)
|
|
131
131
|
- [Pagination and aggregation](#pagination-and-aggregation)
|
|
132
132
|
- [Inspect the SQL](#inspecting-the-sql)
|
|
133
|
+
- [Hydrating pre-fetched rows with `.hydrate()`](#hydrating-pre-fetched-rows-with-hydrate)
|
|
133
134
|
- [Mapped properties with `.mapFields()`](#mapped-properties-with-mapfields)
|
|
134
135
|
- [Computed properties with `.extras()`](#computed-properties-with-extras)
|
|
135
136
|
- [Computed properties with `.extend()`](#computed-properties-with-extend)
|
|
@@ -799,6 +800,53 @@ You can inspect the generated SQL using `.toQuery()`, `.toJoinedQuery()`, or `.t
|
|
|
799
800
|
- `toJoinedQuery()`: Returns the query with all joins applied (subject to row explosion).
|
|
800
801
|
- `toBaseQuery()`: Returns the base query without any joins (but with modifications).
|
|
801
802
|
|
|
803
|
+
### Hydrating pre-fetched rows with `.hydrate()`
|
|
804
|
+
|
|
805
|
+
Sometimes you already have the flat rows—from a separate query, a cache, a
|
|
806
|
+
transaction, or from calling `.toQuery().execute()` directly—and want to hydrate
|
|
807
|
+
them without re-executing the query.
|
|
808
|
+
|
|
809
|
+
The `.hydrate()` method applies the same hydration logic that `.execute()` uses,
|
|
810
|
+
including nested joins, `mapFields`, `extras`, `omit`, and `map`.
|
|
811
|
+
|
|
812
|
+
```ts
|
|
813
|
+
const qs = querySet(db)
|
|
814
|
+
.selectAs("user", db.selectFrom("users").select(["id", "username"]))
|
|
815
|
+
.leftJoinMany(
|
|
816
|
+
"posts",
|
|
817
|
+
({ eb, qs }) => qs(eb.selectFrom("posts").select(["id", "title", "userId"])),
|
|
818
|
+
"posts.userId",
|
|
819
|
+
"user.id",
|
|
820
|
+
);
|
|
821
|
+
|
|
822
|
+
// Fetch the flat rows yourself
|
|
823
|
+
const rows = await qs.toQuery().execute();
|
|
824
|
+
|
|
825
|
+
// Hydrate them
|
|
826
|
+
const users = await qs.hydrate(rows);
|
|
827
|
+
// ⬇
|
|
828
|
+
type Result = Array<{
|
|
829
|
+
id: number;
|
|
830
|
+
username: string;
|
|
831
|
+
posts: Array<{ id: number; title: string; userId: number }>;
|
|
832
|
+
}>;
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
It also accepts a single row and returns a single hydrated result:
|
|
836
|
+
|
|
837
|
+
```ts
|
|
838
|
+
const [row] = await qs.toQuery().execute();
|
|
839
|
+
const user = await qs.hydrate(row);
|
|
840
|
+
// ⬇ { id: number; username: string; posts: Array<...> }
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
For convenience, `.hydrate()` accepts a `Promise` as input, so you can skip
|
|
844
|
+
the intermediate `await`:
|
|
845
|
+
|
|
846
|
+
```ts
|
|
847
|
+
const users = await qs.hydrate(qs.toQuery().execute());
|
|
848
|
+
```
|
|
849
|
+
|
|
802
850
|
### Mapped properties with `.mapFields()`
|
|
803
851
|
|
|
804
852
|
Transform individual fields in the result set. This changes the output type for
|
package/dist/index.d.mts
CHANGED
|
@@ -613,7 +613,7 @@ interface TSelectQuerySet extends TQuerySet {
|
|
|
613
613
|
BaseQuery: TSelectQuery;
|
|
614
614
|
}
|
|
615
615
|
type QuerySetFor<T extends TQuerySet> = T["IsMapped"] extends true ? MappedQuerySet<T> : QuerySet<T>;
|
|
616
|
-
type
|
|
616
|
+
type THydrationInput<T extends TQuerySet> = T["JoinedQuery"]["O"];
|
|
617
617
|
type TOutput<T extends TQuerySet> = T["HydratedOutput"];
|
|
618
618
|
interface TMapped<in out T extends TQuerySet, in out Output> {
|
|
619
619
|
DB: T["DB"];
|
|
@@ -868,6 +868,37 @@ interface MappedQuerySet<in out T extends TQuerySet> extends k.Compilable, k.Ope
|
|
|
868
868
|
* ```
|
|
869
869
|
*/
|
|
870
870
|
toExistsQuery(): OpaqueExistsQueryBuilder;
|
|
871
|
+
/**
|
|
872
|
+
* Hydrates pre-fetched raw joined rows into nested output objects without
|
|
873
|
+
* executing a query.
|
|
874
|
+
*
|
|
875
|
+
* This is useful when you already have the flat SQL result (e.g. from a
|
|
876
|
+
* separate query, a cache, or a transaction) and want to apply the same
|
|
877
|
+
* hydration logic that `.execute()` uses.
|
|
878
|
+
*
|
|
879
|
+
* **Example - single row:**
|
|
880
|
+
* ```ts
|
|
881
|
+
* const qs = querySet(db)
|
|
882
|
+
* .selectAs("user", db.selectFrom("users").select(["id", "username"]))
|
|
883
|
+
* .leftJoinMany("posts", ...);
|
|
884
|
+
*
|
|
885
|
+
* const row = await qs.toQuery().executeTakeFirstOrThrow();
|
|
886
|
+
* const user = await qs.hydrate(row);
|
|
887
|
+
* ```
|
|
888
|
+
*
|
|
889
|
+
* **Example - multiple rows:**
|
|
890
|
+
* ```ts
|
|
891
|
+
* const rows = await qs.toQuery().execute();
|
|
892
|
+
* const users = await qs.hydrate(rows);
|
|
893
|
+
* ```
|
|
894
|
+
*
|
|
895
|
+
* @param input - A single flat row or an iterable of flat rows matching the
|
|
896
|
+
* joined query output shape.
|
|
897
|
+
* @returns The hydrated output(s).
|
|
898
|
+
*/
|
|
899
|
+
hydrate(input: THydrationInput<T> | Promise<THydrationInput<T>>): Promise<TOutput<T>>;
|
|
900
|
+
hydrate(input: Iterable<THydrationInput<T>> | Promise<Iterable<THydrationInput<T>>>): Promise<TOutput<T>[]>;
|
|
901
|
+
hydrate(input: THydrationInput<T> | Iterable<THydrationInput<T>> | Promise<THydrationInput<T>> | Promise<Iterable<THydrationInput<T>>>): Promise<TOutput<T> | TOutput<T>[]>;
|
|
871
902
|
/**
|
|
872
903
|
* Executes the query and returns an array of hydrated rows.
|
|
873
904
|
*
|
|
@@ -1378,7 +1409,7 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1378
1409
|
* the field value from the entire row.
|
|
1379
1410
|
* @returns A new HydratedQueryBuilder with the extras applied.
|
|
1380
1411
|
*/
|
|
1381
|
-
extras<E extends Extras<
|
|
1412
|
+
extras<E extends Extras<THydrationInput<T>>>(extras: E): QuerySet<TWithExtendedOutput<T, InferExtras<THydrationInput<T>, E>>>;
|
|
1382
1413
|
/**
|
|
1383
1414
|
* Adds computed fields to the hydrated output by spreading the return value
|
|
1384
1415
|
* of a function. Unlike `.extras()` which defines one field at a time,
|
|
@@ -1409,7 +1440,7 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1409
1440
|
* computed properties.
|
|
1410
1441
|
* @returns A new HydratedQueryBuilder with the extender applied.
|
|
1411
1442
|
*/
|
|
1412
|
-
extend<F extends Extender<
|
|
1443
|
+
extend<F extends Extender<THydrationInput<T>>>(fn: F): QuerySet<TWithExtendedOutput<T, InferExtender<THydrationInput<T>, F>>>;
|
|
1413
1444
|
/**
|
|
1414
1445
|
* Transforms already-selected field values in the hydrated output. Fields
|
|
1415
1446
|
* not mentioned in the mappings will still be included as-is.
|
|
@@ -1431,7 +1462,7 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1431
1462
|
* functions.
|
|
1432
1463
|
* @returns A new HydratedQueryBuilder with the field transformations applied.
|
|
1433
1464
|
*/
|
|
1434
|
-
mapFields<M extends FieldMappings<
|
|
1465
|
+
mapFields<M extends FieldMappings<THydrationInput<T>>>(mappings: M): QuerySet<TWithExtendedOutput<T, InferFields<THydrationInput<T>, M>>>;
|
|
1435
1466
|
/**
|
|
1436
1467
|
* Omits specified fields from the hydrated output. Useful for excluding
|
|
1437
1468
|
* fields that were selected for internal use (e.g., for extras).
|
|
@@ -1453,7 +1484,7 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1453
1484
|
* @param keys - Field names to omit from the output.
|
|
1454
1485
|
* @returns A new HydratedQueryBuilder with the fields omitted.
|
|
1455
1486
|
*/
|
|
1456
|
-
omit<K$1 extends keyof
|
|
1487
|
+
omit<K$1 extends keyof THydrationInput<T>>(keys: readonly K$1[]): QuerySet<TWithOmit<T, K$1>>;
|
|
1457
1488
|
/**
|
|
1458
1489
|
* Extends this query builder's hydration configuration with another Hydrator.
|
|
1459
1490
|
* The other Hydrator's configuration takes precedence in case of conflicts.
|
|
@@ -1485,8 +1516,8 @@ interface QuerySet<in out T extends TQuerySet> extends MappedQuerySet<T> {
|
|
|
1485
1516
|
* @param hydrator - The Hydrator to extend with.
|
|
1486
1517
|
* @returns A new HydratedQueryBuilder with merged hydration configuration.
|
|
1487
1518
|
*/
|
|
1488
|
-
with<OtherInput extends StrictSubset<
|
|
1489
|
-
with<OtherInput extends StrictSubset<
|
|
1519
|
+
with<OtherInput extends StrictSubset<THydrationInput<T>, OtherInput>, OtherOutput>(hydrator: FullHydrator<OtherInput, OtherOutput>): QuerySet<TWithExtendedOutput<T, OtherOutput>>;
|
|
1520
|
+
with<OtherInput extends StrictSubset<THydrationInput<T>, OtherInput>, OtherOutput>(hydrator: MappedHydrator<OtherInput, OtherOutput>): QuerySet<TWithExtendedOutput<T, OtherOutput>>;
|
|
1490
1521
|
/**
|
|
1491
1522
|
* Attaches data from an external source (not via SQL joins) as a nested
|
|
1492
1523
|
* array. The `fetchFn` is called exactly once per query execution with all
|
|
@@ -2334,8 +2365,8 @@ interface ModifyCollectionReturnMap<T extends TQuerySet, Key extends string, TNe
|
|
|
2334
2365
|
AttachOneOrThrow: QuerySetWithAttach<T, Key, "AttachOneOrThrow", NewValue>;
|
|
2335
2366
|
AttachMany: QuerySetWithAttach<T, Key, "AttachMany", NewValue>;
|
|
2336
2367
|
}
|
|
2337
|
-
type ToFetchFn<T extends TQuerySet, FetchFnReturn extends SomeFetchFnReturn> = SomeFetchFn<
|
|
2338
|
-
type ToAttachedKeysArg<T extends TQuerySet, FetchFnReturn extends SomeFetchFnReturn> = AttachedKeysArg<
|
|
2368
|
+
type ToFetchFn<T extends TQuerySet, FetchFnReturn extends SomeFetchFnReturn> = SomeFetchFn<THydrationInput<T>, FetchFnReturn>;
|
|
2369
|
+
type ToAttachedKeysArg<T extends TQuerySet, FetchFnReturn extends SomeFetchFnReturn> = AttachedKeysArg<THydrationInput<T>, AttachedOutputFromFetchFnReturn<NoInfer<FetchFnReturn>>>;
|
|
2339
2370
|
interface AttachedOutputMap<in out FetchFnReturn extends SomeFetchFnReturn> {
|
|
2340
2371
|
AttachOne: AttachedOutputFromFetchFnReturn<FetchFnReturn> | null;
|
|
2341
2372
|
AttachOneOrThrow: AttachedOutputFromFetchFnReturn<FetchFnReturn>;
|
package/dist/index.mjs
CHANGED
|
@@ -856,13 +856,16 @@ var QuerySetImpl = class QuerySetImpl {
|
|
|
856
856
|
toOperationNode() {
|
|
857
857
|
return this.toQuery().toOperationNode();
|
|
858
858
|
}
|
|
859
|
-
async
|
|
860
|
-
|
|
861
|
-
return this.#props.hydrator.hydrate(rows, {
|
|
859
|
+
async hydrate(input) {
|
|
860
|
+
return this.#props.hydrator.hydrate(await input, {
|
|
862
861
|
[EnableAutoInclusion]: true,
|
|
863
862
|
sort: "nested"
|
|
864
863
|
});
|
|
865
864
|
}
|
|
865
|
+
async execute() {
|
|
866
|
+
const rows = await this.toQuery().execute();
|
|
867
|
+
return this.hydrate(rows);
|
|
868
|
+
}
|
|
866
869
|
async executeTakeFirst() {
|
|
867
870
|
const [result] = await this.execute();
|
|
868
871
|
return result;
|