@davidtkramer/convex-relations 0.4.0 → 0.4.1
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/index.d.ts +4 -2
- package/dist/index.js +84 -32
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GenericDataModel, TableNamesInDataModel, DocumentByName, GenericTableInfo, FilterBuilder, ExpressionOrValue, NamedTableInfo, IndexNames, IndexRangeBuilder, NamedIndex, IndexRange, GenericDatabaseReader } from 'convex/server';
|
|
1
|
+
import { GenericDataModel, TableNamesInDataModel, DocumentByName, GenericTableInfo, FilterBuilder, ExpressionOrValue, NamedTableInfo, IndexNames, IndexRangeBuilder, NamedIndex, IndexRange, GenericDatabaseReader, SchemaDefinition } from 'convex/server';
|
|
2
2
|
import { GenericId } from 'convex/values';
|
|
3
3
|
|
|
4
4
|
type Simplify<T> = {
|
|
@@ -165,17 +165,19 @@ type QuerySourcePlan = {
|
|
|
165
165
|
table: string;
|
|
166
166
|
index?: string;
|
|
167
167
|
selector?: unknown;
|
|
168
|
+
indexFields?: readonly string[];
|
|
168
169
|
} | {
|
|
169
170
|
kind: 'batch';
|
|
170
171
|
table: string;
|
|
171
172
|
index: string;
|
|
172
173
|
values: unknown[];
|
|
174
|
+
indexFields?: readonly string[];
|
|
173
175
|
};
|
|
174
176
|
type QueryPlan = {
|
|
175
177
|
source: QuerySourcePlan;
|
|
176
178
|
modifiers: QueryModifier[];
|
|
177
179
|
expanders: AnyWithBuilder<any, any>[];
|
|
178
180
|
};
|
|
179
|
-
declare function createQueryFacade<DataModel extends GenericDataModel>(db: GenericDatabaseReader<DataModel>): QueryFacade<DataModel>;
|
|
181
|
+
declare function createQueryFacade<DataModel extends GenericDataModel>(db: GenericDatabaseReader<DataModel>, schema: SchemaDefinition<any, boolean>): QueryFacade<DataModel>;
|
|
180
182
|
|
|
181
183
|
export { type AppDoc, type AppTable, type QueryFacade, type RootIndexValueArg, type StrictRootIndexValueArg, type UserIndex, createQueryFacade };
|
package/dist/index.js
CHANGED
|
@@ -67,16 +67,15 @@ function withExpander(plan, expander) {
|
|
|
67
67
|
expanders: [...plan.expanders, expander]
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
function normalizeIndexValues(index, value) {
|
|
70
|
+
function normalizeIndexValues(index, value, indexFields) {
|
|
71
71
|
if (Array.isArray(value)) {
|
|
72
|
-
|
|
73
|
-
return Object.fromEntries(fieldNames.map((field, index2) => [field, value[index2]]));
|
|
72
|
+
return Object.fromEntries(indexFields.map((field, index2) => [field, value[index2]]));
|
|
74
73
|
}
|
|
75
74
|
if (isPlainObject(value)) {
|
|
76
75
|
return value;
|
|
77
76
|
}
|
|
78
77
|
return {
|
|
79
|
-
[
|
|
78
|
+
[index === "by_id" ? "_id" : indexFields[0]]: value
|
|
80
79
|
};
|
|
81
80
|
}
|
|
82
81
|
function isPlainObject(value) {
|
|
@@ -89,19 +88,16 @@ function applyIndexValues(query, values) {
|
|
|
89
88
|
}
|
|
90
89
|
return current;
|
|
91
90
|
}
|
|
92
|
-
function
|
|
93
|
-
return inferFieldNamesFromIndex(index)[0];
|
|
94
|
-
}
|
|
95
|
-
function inferFieldNamesFromIndex(index) {
|
|
91
|
+
function requireIndexFields(index, indexFields) {
|
|
96
92
|
if (index === "by_id") {
|
|
97
93
|
return ["_id"];
|
|
98
94
|
}
|
|
99
|
-
if (
|
|
100
|
-
|
|
95
|
+
if (indexFields === void 0) {
|
|
96
|
+
throw new Error(`Missing schema metadata for index ${index}`);
|
|
101
97
|
}
|
|
102
|
-
|
|
98
|
+
return indexFields;
|
|
103
99
|
}
|
|
104
|
-
function createIndexedQuery(db, table, index, selector) {
|
|
100
|
+
function createIndexedQuery(db, table, index, selector, indexFields = []) {
|
|
105
101
|
const baseQuery = db.query(table);
|
|
106
102
|
if (index === void 0) {
|
|
107
103
|
return baseQuery;
|
|
@@ -115,9 +111,10 @@ function createIndexedQuery(db, table, index, selector) {
|
|
|
115
111
|
if (index === "by_id") {
|
|
116
112
|
return baseQuery.withIndex(index, (q) => q.eq("_id", selector));
|
|
117
113
|
}
|
|
114
|
+
const fields = requireIndexFields(index, indexFields);
|
|
118
115
|
return baseQuery.withIndex(
|
|
119
116
|
index,
|
|
120
|
-
(q) => applyIndexValues(q, normalizeIndexValues(index, selector))
|
|
117
|
+
(q) => applyIndexValues(q, normalizeIndexValues(index, selector, fields))
|
|
121
118
|
);
|
|
122
119
|
}
|
|
123
120
|
function sourceDescription(plan) {
|
|
@@ -154,7 +151,13 @@ async function* iterateDecoratedSourceItems(db, plan) {
|
|
|
154
151
|
const runtime = createPlanRuntime(db, plan);
|
|
155
152
|
const source = plan.source;
|
|
156
153
|
const query = buildQuery(
|
|
157
|
-
() => createIndexedQuery(
|
|
154
|
+
() => createIndexedQuery(
|
|
155
|
+
db,
|
|
156
|
+
source.table,
|
|
157
|
+
source.index,
|
|
158
|
+
source.selector,
|
|
159
|
+
source.indexFields
|
|
160
|
+
),
|
|
158
161
|
plan.modifiers
|
|
159
162
|
);
|
|
160
163
|
for await (const rawItem of query) {
|
|
@@ -191,18 +194,28 @@ function createPlanRuntime(db, plan) {
|
|
|
191
194
|
return {
|
|
192
195
|
many: async () => (await Promise.all(
|
|
193
196
|
source.values.map(
|
|
194
|
-
async (value) => await
|
|
197
|
+
async (value) => await queryManyByIndex(
|
|
198
|
+
db,
|
|
199
|
+
source.table,
|
|
200
|
+
source.index,
|
|
201
|
+
value,
|
|
202
|
+
source.indexFields
|
|
203
|
+
)
|
|
195
204
|
)
|
|
196
|
-
)).
|
|
197
|
-
(doc) => doc !== null
|
|
198
|
-
),
|
|
205
|
+
)).flat(),
|
|
199
206
|
mapOne: async (rawItem) => rawItem,
|
|
200
207
|
mapMany: async (rawItems) => rawItems,
|
|
201
208
|
missingMessages: {}
|
|
202
209
|
};
|
|
203
210
|
case "query": {
|
|
204
211
|
const runQuery = () => buildQuery(
|
|
205
|
-
() => createIndexedQuery(
|
|
212
|
+
() => createIndexedQuery(
|
|
213
|
+
db,
|
|
214
|
+
source.table,
|
|
215
|
+
source.index,
|
|
216
|
+
source.selector,
|
|
217
|
+
source.indexFields
|
|
218
|
+
),
|
|
206
219
|
plan.modifiers
|
|
207
220
|
);
|
|
208
221
|
return {
|
|
@@ -593,20 +606,22 @@ function createIdPlan(table, id) {
|
|
|
593
606
|
id
|
|
594
607
|
});
|
|
595
608
|
}
|
|
596
|
-
function createQueryPlan(table, index, selector) {
|
|
609
|
+
function createQueryPlan(table, index, selector, indexFields) {
|
|
597
610
|
return createPlan({
|
|
598
611
|
kind: "query",
|
|
599
612
|
table,
|
|
600
613
|
index,
|
|
601
|
-
selector
|
|
614
|
+
selector,
|
|
615
|
+
indexFields
|
|
602
616
|
});
|
|
603
617
|
}
|
|
604
|
-
function createBatchPlan(table, index, values) {
|
|
618
|
+
function createBatchPlan(table, index, values, indexFields) {
|
|
605
619
|
return createPlan({
|
|
606
620
|
kind: "batch",
|
|
607
621
|
table,
|
|
608
622
|
index,
|
|
609
|
-
values
|
|
623
|
+
values,
|
|
624
|
+
indexFields
|
|
610
625
|
});
|
|
611
626
|
}
|
|
612
627
|
function isCollectionSource(value) {
|
|
@@ -627,7 +642,7 @@ function normalizeIndexSelectorArgs(args) {
|
|
|
627
642
|
}
|
|
628
643
|
return args;
|
|
629
644
|
}
|
|
630
|
-
function createTableNamespace(db, table) {
|
|
645
|
+
function createTableNamespace(db, table, resolveIndexFields) {
|
|
631
646
|
const rootFacade = createCollectionFacade(db, createQueryPlan(table));
|
|
632
647
|
const target = {
|
|
633
648
|
...rootFacade,
|
|
@@ -685,18 +700,49 @@ function createTableNamespace(db, table) {
|
|
|
685
700
|
createQueryPlan(
|
|
686
701
|
table,
|
|
687
702
|
prop,
|
|
688
|
-
normalizeIndexSelectorArgs(args)
|
|
703
|
+
normalizeIndexSelectorArgs(args),
|
|
704
|
+
resolveIndexFields(table, prop)
|
|
689
705
|
)
|
|
690
706
|
));
|
|
691
707
|
indexMethod.in = (values) => createBatchFacade(
|
|
692
708
|
db,
|
|
693
|
-
createBatchPlan(
|
|
709
|
+
createBatchPlan(
|
|
710
|
+
table,
|
|
711
|
+
prop,
|
|
712
|
+
values,
|
|
713
|
+
resolveIndexFields(table, prop)
|
|
714
|
+
)
|
|
694
715
|
);
|
|
695
716
|
return indexMethod;
|
|
696
717
|
}
|
|
697
718
|
});
|
|
698
719
|
}
|
|
699
|
-
function
|
|
720
|
+
function createIndexFieldResolver(schema) {
|
|
721
|
+
const cache = /* @__PURE__ */ new Map();
|
|
722
|
+
return (table, index) => {
|
|
723
|
+
if (index === "by_id") {
|
|
724
|
+
return ["_id"];
|
|
725
|
+
}
|
|
726
|
+
const cacheKey = `${table}:${index}`;
|
|
727
|
+
const cached = cache.get(cacheKey);
|
|
728
|
+
if (cached !== void 0) {
|
|
729
|
+
return cached;
|
|
730
|
+
}
|
|
731
|
+
const tableDefinition = schema.tables[table];
|
|
732
|
+
const indexes = tableDefinition?.[" indexes"]();
|
|
733
|
+
const match = indexes?.find(
|
|
734
|
+
(candidate) => candidate.indexDescriptor === index
|
|
735
|
+
);
|
|
736
|
+
if (match !== void 0 && Array.isArray(match.fields)) {
|
|
737
|
+
const fields = [...match.fields];
|
|
738
|
+
cache.set(cacheKey, fields);
|
|
739
|
+
return fields;
|
|
740
|
+
}
|
|
741
|
+
throw new Error(`Missing schema metadata for index ${table}.${index}`);
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function createQueryFacade(db, schema) {
|
|
745
|
+
const resolveIndexFields = createIndexFieldResolver(schema);
|
|
700
746
|
return new Proxy(
|
|
701
747
|
{},
|
|
702
748
|
{
|
|
@@ -704,19 +750,25 @@ function createQueryFacade(db) {
|
|
|
704
750
|
if (typeof prop !== "string" || RESERVED_PROMISE_KEYS.has(prop)) {
|
|
705
751
|
return void 0;
|
|
706
752
|
}
|
|
707
|
-
return createTableNamespace(
|
|
753
|
+
return createTableNamespace(
|
|
754
|
+
db,
|
|
755
|
+
prop,
|
|
756
|
+
resolveIndexFields
|
|
757
|
+
);
|
|
708
758
|
}
|
|
709
759
|
}
|
|
710
760
|
);
|
|
711
761
|
}
|
|
712
|
-
async function
|
|
762
|
+
async function queryManyByIndex(db, table, index, value, indexFields) {
|
|
713
763
|
if (index === "by_id") {
|
|
714
|
-
|
|
764
|
+
const doc = await db.query(table).withIndex("by_id", (q) => q.eq("_id", value)).unique();
|
|
765
|
+
return doc ? [doc] : [];
|
|
715
766
|
}
|
|
767
|
+
const fields = requireIndexFields(index, indexFields);
|
|
716
768
|
return await db.query(table).withIndex(
|
|
717
769
|
index,
|
|
718
|
-
(q) => applyIndexValues(q, normalizeIndexValues(index, value))
|
|
719
|
-
).
|
|
770
|
+
(q) => applyIndexValues(q, normalizeIndexValues(index, value, fields))
|
|
771
|
+
).collect();
|
|
720
772
|
}
|
|
721
773
|
export {
|
|
722
774
|
createQueryFacade
|