@snowtop/ent 0.2.6 → 0.2.8

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.
Files changed (73) hide show
  1. package/action/executor.js +4 -4
  2. package/action/operations.js +3 -0
  3. package/action/orchestrator.js +10 -12
  4. package/action/topological_sort.d.ts +9 -0
  5. package/action/topological_sort.js +46 -0
  6. package/core/async_utils.d.ts +1 -0
  7. package/core/async_utils.js +29 -0
  8. package/core/base.d.ts +12 -5
  9. package/core/clause.d.ts +3 -5
  10. package/core/clause.js +32 -0
  11. package/core/config.d.ts +28 -2
  12. package/core/config.js +14 -1
  13. package/core/context.d.ts +3 -1
  14. package/core/context.js +90 -26
  15. package/core/db.d.ts +12 -2
  16. package/core/db.js +102 -7
  17. package/core/dev_schema.d.ts +9 -0
  18. package/core/dev_schema.js +306 -0
  19. package/core/ent.d.ts +5 -7
  20. package/core/ent.js +33 -48
  21. package/core/extensions.d.ts +25 -0
  22. package/core/extensions.js +220 -0
  23. package/core/loaders/assoc_count_loader.js +3 -6
  24. package/core/loaders/assoc_edge_loader.d.ts +3 -0
  25. package/core/loaders/assoc_edge_loader.js +48 -19
  26. package/core/loaders/index.d.ts +2 -1
  27. package/core/loaders/index.js +5 -1
  28. package/core/loaders/loader.d.ts +31 -0
  29. package/core/loaders/loader.js +141 -2
  30. package/core/loaders/object_loader.d.ts +2 -2
  31. package/core/loaders/object_loader.js +39 -57
  32. package/core/loaders/query_loader.d.ts +2 -5
  33. package/core/loaders/query_loader.js +45 -24
  34. package/core/loaders/raw_count_loader.d.ts +2 -2
  35. package/core/loaders/raw_count_loader.js +12 -14
  36. package/core/memoize.d.ts +1 -0
  37. package/core/memoize.js +15 -0
  38. package/core/metrics.d.ts +22 -0
  39. package/core/metrics.js +31 -0
  40. package/core/query/custom_clause_query.js +5 -1
  41. package/core/query/query.d.ts +1 -1
  42. package/core/query/query.js +10 -7
  43. package/core/query_expression.d.ts +6 -0
  44. package/core/query_expression.js +2 -0
  45. package/core/query_impl.d.ts +19 -3
  46. package/core/query_impl.js +148 -35
  47. package/index.d.ts +7 -2
  48. package/index.js +12 -2
  49. package/package.json +1 -7
  50. package/parse_schema/parse.d.ts +2 -12
  51. package/parse_schema/parse.js +22 -41
  52. package/schema/index.d.ts +1 -1
  53. package/schema/schema.d.ts +20 -1
  54. package/scripts/custom_graphql.js +12 -5
  55. package/scripts/fix_action_exports.js +1 -1
  56. package/scripts/migrate_v0.1.js +2 -5
  57. package/scripts/move_types.js +1 -1
  58. package/scripts/read_schema.js +2 -5
  59. package/testutils/builder.js +1 -2
  60. package/testutils/parse_sql.js +1 -1
  61. package/tsc/compilerOptions.d.ts +2 -2
  62. package/tsc/compilerOptions.js +12 -18
  63. package/tsc/move_generated.js +2 -2
  64. package/tsc/transform.d.ts +1 -1
  65. package/tsc/transform.js +16 -2
  66. package/tsc/transform_action.d.ts +1 -1
  67. package/tsc/transform_action.js +1 -1
  68. package/tsc/transform_ent.d.ts +1 -1
  69. package/tsc/transform_ent.js +1 -1
  70. package/tsc/transform_schema.d.ts +1 -1
  71. package/tsc/transform_schema.js +2 -2
  72. /package/core/{loaders/cache_utils.d.ts → cache_utils.d.ts} +0 -0
  73. /package/core/{loaders/cache_utils.js → cache_utils.js} +0 -0
@@ -1,8 +1,38 @@
1
+ import DataLoader from "dataloader";
1
2
  import { Loader, LoaderFactory, Context, DataOptions } from "../base";
2
3
  export declare function getLoaderMaxBatchSize(): number;
3
4
  export declare function setLoaderMaxBatchSize(size?: number | null): void;
5
+ export declare function getLoaderCacheMaxEntries(): number;
6
+ export declare function setLoaderCacheMaxEntries(size?: number | null): void;
4
7
  export declare function getLoader<K, V>(factory: LoaderFactory<K, V>, create: () => Loader<K, V>, context?: Context): Loader<K, V>;
5
8
  export declare function getCustomLoader<K, V>(key: string, create: () => Loader<K, V>, context?: Context): Loader<K, V>;
9
+ export type CacheMapLike<K, V> = {
10
+ get(key: K): V | undefined;
11
+ set(key: K, value: V): any;
12
+ delete(key: K): any;
13
+ clear(): any;
14
+ };
15
+ type CacheKeyFn<K> = (key: K) => any;
16
+ type BatchLoadFn<K, V> = (keys: readonly K[]) => PromiseLike<ArrayLike<V | Error>>;
17
+ export declare class InstrumentedDataLoader<K, V> extends DataLoader<K, V> {
18
+ constructor(loaderName: string, batchLoadFn: BatchLoadFn<K, V>, options: DataLoader.Options<K, V>, tableName?: string, cacheKeyFn?: (key: K) => unknown);
19
+ }
20
+ export declare class BoundedCacheMap<K, V> {
21
+ private cacheMap;
22
+ private maxEntries;
23
+ private cacheKeyFn?;
24
+ private order;
25
+ constructor(cacheMap: CacheMapLike<K, V>, maxEntries: number, cacheKeyFn?: CacheKeyFn<K> | undefined);
26
+ private normalizeKey;
27
+ private touch;
28
+ private evictIfNeeded;
29
+ get(key: K): V | undefined;
30
+ set(key: K, value: V): any;
31
+ delete(key: K): any;
32
+ clear(): any;
33
+ }
34
+ export declare function createBoundedCacheMap<K, V>(cacheMap: CacheMapLike<K, V>, cacheKeyFn?: CacheKeyFn<K>): CacheMapLike<K, V>;
35
+ export declare function createLoaderCacheMap<K, V>(options: DataOptions): CacheMapLike<K, V>;
6
36
  export declare class CacheMap {
7
37
  private options;
8
38
  private m;
@@ -12,3 +42,4 @@ export declare class CacheMap {
12
42
  delete(key: any): boolean;
13
43
  clear(): void;
14
44
  }
45
+ export {};
@@ -1,13 +1,24 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CacheMap = void 0;
6
+ exports.CacheMap = exports.BoundedCacheMap = exports.InstrumentedDataLoader = void 0;
4
7
  exports.getLoaderMaxBatchSize = getLoaderMaxBatchSize;
5
8
  exports.setLoaderMaxBatchSize = setLoaderMaxBatchSize;
9
+ exports.getLoaderCacheMaxEntries = getLoaderCacheMaxEntries;
10
+ exports.setLoaderCacheMaxEntries = setLoaderCacheMaxEntries;
6
11
  exports.getLoader = getLoader;
7
12
  exports.getCustomLoader = getCustomLoader;
13
+ exports.createBoundedCacheMap = createBoundedCacheMap;
14
+ exports.createLoaderCacheMap = createLoaderCacheMap;
15
+ const dataloader_1 = __importDefault(require("dataloader"));
8
16
  const logger_1 = require("../logger");
17
+ const metrics_1 = require("../metrics");
9
18
  const DEFAULT_MAX_BATCH_SIZE = 1000;
10
19
  let loaderMaxBatchSize = DEFAULT_MAX_BATCH_SIZE;
20
+ const DEFAULT_MAX_CACHE_ENTRIES = 1000;
21
+ let loaderCacheMaxEntries = DEFAULT_MAX_CACHE_ENTRIES;
11
22
  function getLoaderMaxBatchSize() {
12
23
  return loaderMaxBatchSize;
13
24
  }
@@ -21,6 +32,19 @@ function setLoaderMaxBatchSize(size) {
21
32
  }
22
33
  loaderMaxBatchSize = Math.floor(size);
23
34
  }
35
+ function getLoaderCacheMaxEntries() {
36
+ return loaderCacheMaxEntries;
37
+ }
38
+ function setLoaderCacheMaxEntries(size) {
39
+ if (size === undefined || size === null) {
40
+ loaderCacheMaxEntries = DEFAULT_MAX_CACHE_ENTRIES;
41
+ return;
42
+ }
43
+ if (!Number.isFinite(size) || size < 0) {
44
+ throw new Error(`maxCacheEntries must be a non-negative number`);
45
+ }
46
+ loaderCacheMaxEntries = Math.floor(size);
47
+ }
24
48
  // this is like factory factory FML
25
49
  // helper function to handle context vs not
26
50
  // and to keep the API clean for clients who shouldn't have to worry about this
@@ -42,6 +66,121 @@ function getCustomLoader(key, create, context) {
42
66
  // g|set from context cache
43
67
  return context.cache.getLoader(key, create);
44
68
  }
69
+ function instrumentCacheMap(cacheMap, tableName, cacheKeyFn) {
70
+ if (!cacheMap || !tableName) {
71
+ return cacheMap;
72
+ }
73
+ return {
74
+ get(key) {
75
+ const value = cacheMap.get(key);
76
+ if (value !== undefined) {
77
+ const hook = (0, metrics_1.getOnDataLoaderCacheHit)();
78
+ if (hook) {
79
+ hook({
80
+ tableName,
81
+ key: cacheKeyFn ? cacheKeyFn(key) : key,
82
+ });
83
+ }
84
+ }
85
+ return value;
86
+ },
87
+ set(key, value) {
88
+ return cacheMap.set(key, value);
89
+ },
90
+ delete(key) {
91
+ return cacheMap.delete(key);
92
+ },
93
+ clear() {
94
+ return cacheMap.clear();
95
+ },
96
+ };
97
+ }
98
+ class InstrumentedDataLoader extends dataloader_1.default {
99
+ constructor(loaderName, batchLoadFn, options, tableName, cacheKeyFn) {
100
+ const wrappedBatchFn = async (keys) => {
101
+ if (keys.length) {
102
+ const hook = (0, metrics_1.getOnDataLoaderBatch)();
103
+ if (hook) {
104
+ hook({
105
+ loaderName,
106
+ batchSize: keys.length,
107
+ });
108
+ }
109
+ }
110
+ return batchLoadFn(keys);
111
+ };
112
+ const cacheMap = instrumentCacheMap(options.cacheMap, tableName, cacheKeyFn);
113
+ const loaderOptions = cacheMap === options.cacheMap ? options : { ...options, cacheMap };
114
+ super(wrappedBatchFn, loaderOptions);
115
+ }
116
+ }
117
+ exports.InstrumentedDataLoader = InstrumentedDataLoader;
118
+ class BoundedCacheMap {
119
+ constructor(cacheMap, maxEntries, cacheKeyFn) {
120
+ this.cacheMap = cacheMap;
121
+ this.maxEntries = maxEntries;
122
+ this.cacheKeyFn = cacheKeyFn;
123
+ this.order = new Map();
124
+ }
125
+ normalizeKey(key) {
126
+ return this.cacheKeyFn ? this.cacheKeyFn(key) : key;
127
+ }
128
+ touch(normalizedKey, key) {
129
+ if (this.order.has(normalizedKey)) {
130
+ this.order.delete(normalizedKey);
131
+ }
132
+ this.order.set(normalizedKey, key);
133
+ }
134
+ evictIfNeeded() {
135
+ while (this.order.size > this.maxEntries) {
136
+ const oldest = this.order.entries().next().value;
137
+ if (!oldest) {
138
+ return;
139
+ }
140
+ const [normalizedKey, key] = oldest;
141
+ this.order.delete(normalizedKey);
142
+ this.cacheMap.delete(key);
143
+ }
144
+ }
145
+ get(key) {
146
+ const value = this.cacheMap.get(key);
147
+ if (value !== undefined) {
148
+ const normalizedKey = this.normalizeKey(key);
149
+ this.touch(normalizedKey, key);
150
+ }
151
+ return value;
152
+ }
153
+ set(key, value) {
154
+ const normalizedKey = this.normalizeKey(key);
155
+ this.touch(normalizedKey, key);
156
+ const result = this.cacheMap.set(key, value);
157
+ this.evictIfNeeded();
158
+ return result;
159
+ }
160
+ delete(key) {
161
+ const normalizedKey = this.normalizeKey(key);
162
+ this.order.delete(normalizedKey);
163
+ return this.cacheMap.delete(key);
164
+ }
165
+ clear() {
166
+ this.order.clear();
167
+ return this.cacheMap.clear();
168
+ }
169
+ }
170
+ exports.BoundedCacheMap = BoundedCacheMap;
171
+ function createBoundedCacheMap(cacheMap, cacheKeyFn) {
172
+ const maxEntries = getLoaderCacheMaxEntries();
173
+ if (maxEntries <= 0) {
174
+ return cacheMap;
175
+ }
176
+ return new BoundedCacheMap(cacheMap, maxEntries, cacheKeyFn);
177
+ }
178
+ function createLoaderCacheMap(options) {
179
+ const baseMap = (0, logger_1.logEnabled)("query")
180
+ ? new CacheMap(options)
181
+ : new Map();
182
+ return createBoundedCacheMap(baseMap);
183
+ }
45
184
  class CacheMap {
46
185
  constructor(options) {
47
186
  this.options = options;
@@ -56,7 +195,7 @@ class CacheMap {
56
195
  // was designed for ObjectLoader time. Now we have different needs e.g. count, assoc etc
57
196
  (0, logger_1.log)("cache", {
58
197
  "dataloader-cache-hit": key,
59
- "tableName": this.options.tableName,
198
+ tableName: this.options.tableName,
60
199
  });
61
200
  }
62
201
  return ret;
@@ -1,7 +1,8 @@
1
1
  import { ID, Data, SelectDataOptions, Context, Loader, LoaderFactory } from "../base";
2
+ import { mapWithConcurrency } from "../async_utils";
2
3
  import * as clause from "../clause";
3
4
  export declare function setClauseLoaderConcurrency(limit: number): void;
4
- export declare function mapWithConcurrency<T, R>(items: T[], limit: number, mapper: (item: T, index: number) => Promise<R>): Promise<R[]>;
5
+ export { mapWithConcurrency };
5
6
  export declare class ObjectLoader<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData> implements Loader<ID, TResultData | null>, Loader<clause.Clause<TQueryData, K>, TResultData[] | null> {
6
7
  private options;
7
8
  context?: Context | undefined;
@@ -48,4 +49,3 @@ export declare class ObjectLoaderFactory<V extends Data = Data> implements Loade
48
49
  createCountLoader<K = keyof V>(context?: Context): ObjectCountLoader<V, K>;
49
50
  addToPrime(factory: ObjectLoaderFactory<V>): this;
50
51
  }
51
- export {};
@@ -32,20 +32,17 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ObjectLoaderFactory = exports.ObjectCountLoader = exports.ObjectLoader = void 0;
36
+ exports.ObjectLoaderFactory = exports.ObjectCountLoader = exports.ObjectLoader = exports.mapWithConcurrency = void 0;
40
37
  exports.setClauseLoaderConcurrency = setClauseLoaderConcurrency;
41
- exports.mapWithConcurrency = mapWithConcurrency;
42
- const dataloader_1 = __importDefault(require("dataloader"));
43
38
  const ent_1 = require("../ent");
39
+ const async_utils_1 = require("../async_utils");
40
+ Object.defineProperty(exports, "mapWithConcurrency", { enumerable: true, get: function () { return async_utils_1.mapWithConcurrency; } });
44
41
  const clause = __importStar(require("../clause"));
45
42
  const logger_1 = require("../logger");
46
43
  const clause_1 = require("../clause");
47
44
  const loader_1 = require("./loader");
48
- const memoizee_1 = __importDefault(require("memoizee"));
45
+ const memoize_1 = require("../memoize");
49
46
  const DEFAULT_CLAUSE_LOADER_CONCURRENCY = 10;
50
47
  let clauseLoaderConcurrency = DEFAULT_CLAUSE_LOADER_CONCURRENCY;
51
48
  function setClauseLoaderConcurrency(limit) {
@@ -55,26 +52,6 @@ function setClauseLoaderConcurrency(limit) {
55
52
  }
56
53
  clauseLoaderConcurrency = Math.floor(limit);
57
54
  }
58
- async function mapWithConcurrency(items, limit, mapper) {
59
- if (!items.length) {
60
- return [];
61
- }
62
- const results = new Array(items.length);
63
- const workerCount = Math.min(items.length, Math.max(1, limit));
64
- let nextIndex = 0;
65
- const workers = Array.from({ length: workerCount }, async () => {
66
- while (true) {
67
- const currentIndex = nextIndex;
68
- nextIndex += 1;
69
- if (currentIndex >= items.length) {
70
- return;
71
- }
72
- results[currentIndex] = await mapper(items[currentIndex], currentIndex);
73
- }
74
- });
75
- await Promise.all(workers);
76
- return results;
77
- }
78
55
  async function loadRowsForIDLoader(options, ids, context) {
79
56
  let col = options.key;
80
57
  const typ = options.keyType || "uuid";
@@ -117,19 +94,21 @@ async function loadRowsForIDLoader(options, ids, context) {
117
94
  }
118
95
  return result;
119
96
  }
120
- async function loadRowsForClauseLoader(options, clause) {
97
+ async function loadRowsForClauseLoader(options, clause, context) {
121
98
  const rowOptions = {
122
99
  ...options,
123
100
  // @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
124
101
  clause: (0, clause_1.getCombinedClause)(options, clause, true),
102
+ context,
125
103
  };
126
104
  return (await (0, ent_1.loadRows)(rowOptions));
127
105
  }
128
- async function loadCountForClauseLoader(options, clause) {
106
+ async function loadCountForClauseLoader(options, clause, context) {
129
107
  const rowOptions = {
130
108
  ...options,
131
109
  // @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
132
110
  clause: (0, clause_1.getCombinedClause)(options, clause, true),
111
+ context,
133
112
  };
134
113
  const row = await (0, ent_1.loadRow)({
135
114
  ...rowOptions,
@@ -143,21 +122,19 @@ async function loadCountForClauseLoader(options, clause) {
143
122
  // optional clause...
144
123
  // so ObjectLoaderFactory and createDataLoader need to take a new optional field which is a clause that's always added here
145
124
  // and we need a disableTransform which skips loader completely and uses loadRow...
146
- function createDataLoader(options) {
125
+ function createDataLoader(options, context) {
126
+ const loaderName = `objectLoader:${options.tableName}:${options.key}`;
147
127
  const loaderOptions = {
148
128
  maxBatchSize: (0, loader_1.getLoaderMaxBatchSize)(),
129
+ cacheMap: (0, loader_1.createLoaderCacheMap)(options),
149
130
  };
150
- // if query logging is enabled, we should log what's happening with loader
151
- if ((0, logger_1.logEnabled)("query")) {
152
- loaderOptions.cacheMap = new loader_1.CacheMap(options);
153
- }
154
- return new dataloader_1.default(async (ids) => {
131
+ return new loader_1.InstrumentedDataLoader(loaderName, async (ids) => {
155
132
  if (!ids.length) {
156
133
  return [];
157
134
  }
158
- // context not needed because we're creating a loader which has its own cache which is being used here
159
- return loadRowsForIDLoader(options, ids);
160
- }, loaderOptions);
135
+ // pass context along so ContextCache is primed alongside DataLoader caching
136
+ return loadRowsForIDLoader(options, ids, context);
137
+ }, loaderOptions, options.tableName);
161
138
  }
162
139
  class clauseCacheMap {
163
140
  constructor(options, count) {
@@ -171,7 +148,7 @@ class clauseCacheMap {
171
148
  if (ret) {
172
149
  (0, logger_1.log)("cache", {
173
150
  "dataloader-cache-hit": key2 + (this.count ? ":count" : ""),
174
- "tableName": this.options.tableName,
151
+ tableName: this.options.tableName,
175
152
  });
176
153
  }
177
154
  return ret;
@@ -186,27 +163,32 @@ class clauseCacheMap {
186
163
  return this.m.clear();
187
164
  }
188
165
  }
189
- function createClauseDataLoder(options) {
190
- return new dataloader_1.default(async (clauses) => {
166
+ function createClauseCacheMap(options, count) {
167
+ return (0, loader_1.createBoundedCacheMap)(new clauseCacheMap(options, count), (key) => key.instanceKey());
168
+ }
169
+ function createClauseDataLoder(options, context) {
170
+ const loaderName = `objectLoader:clause:${options.tableName}`;
171
+ return new loader_1.InstrumentedDataLoader(loaderName, async (clauses) => {
191
172
  if (!clauses.length) {
192
173
  return [];
193
174
  }
194
- return mapWithConcurrency(clauses, clauseLoaderConcurrency, (clauseItem) => loadRowsForClauseLoader(options, clauseItem));
175
+ return (0, async_utils_1.mapWithConcurrency)(clauses, clauseLoaderConcurrency, (clauseItem) => loadRowsForClauseLoader(options, clauseItem, context));
195
176
  }, {
196
- cacheMap: new clauseCacheMap(options),
177
+ cacheMap: createClauseCacheMap(options),
197
178
  maxBatchSize: (0, loader_1.getLoaderMaxBatchSize)(),
198
- });
179
+ }, options.tableName, (key) => key.instanceKey());
199
180
  }
200
- function createClauseCountDataLoader(options) {
201
- return new dataloader_1.default(async (clauses) => {
181
+ function createClauseCountDataLoader(options, context) {
182
+ const loaderName = `objectLoader:count:${options.tableName}`;
183
+ return new loader_1.InstrumentedDataLoader(loaderName, async (clauses) => {
202
184
  if (!clauses.length) {
203
185
  return [];
204
186
  }
205
- return mapWithConcurrency(clauses, clauseLoaderConcurrency, (clauseItem) => loadCountForClauseLoader(options, clauseItem));
187
+ return (0, async_utils_1.mapWithConcurrency)(clauses, clauseLoaderConcurrency, (clauseItem) => loadCountForClauseLoader(options, clauseItem, context));
206
188
  }, {
207
- cacheMap: new clauseCacheMap(options, true),
189
+ cacheMap: createClauseCacheMap(options, true),
208
190
  maxBatchSize: (0, loader_1.getLoaderMaxBatchSize)(),
209
- });
191
+ }, options.tableName, (key) => `${key.instanceKey()}:count`);
210
192
  }
211
193
  class ObjectLoader {
212
194
  constructor(options, context, toPrime) {
@@ -217,10 +199,10 @@ class ObjectLoader {
217
199
  console.trace();
218
200
  }
219
201
  if (context) {
220
- this.idLoader = createDataLoader(options);
221
- this.clauseLoader = createClauseDataLoder(options);
202
+ this.idLoader = createDataLoader(options, context);
203
+ this.clauseLoader = createClauseDataLoder(options, context);
222
204
  }
223
- this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
205
+ this.memoizedInitPrime = (0, memoize_1.memoizeNoArgs)(this.initPrime.bind(this));
224
206
  }
225
207
  getOptions() {
226
208
  return this.options;
@@ -273,7 +255,7 @@ class ObjectLoader {
273
255
  if (this.clauseLoader) {
274
256
  return this.clauseLoader.load(key);
275
257
  }
276
- return loadRowsForClauseLoader(this.options, key);
258
+ return loadRowsForClauseLoader(this.options, key, this.context);
277
259
  }
278
260
  clearAll() {
279
261
  this.idLoader && this.idLoader.clearAll();
@@ -300,7 +282,7 @@ class ObjectLoader {
300
282
  // @ts-expect-error TODO?
301
283
  return this.clauseLoader.loadMany(keys);
302
284
  }
303
- return mapWithConcurrency(keys, clauseLoaderConcurrency, (key) => loadRowsForClauseLoader(this.options, key));
285
+ return (0, async_utils_1.mapWithConcurrency)(keys, clauseLoaderConcurrency, (key) => loadRowsForClauseLoader(this.options, key, this.context));
304
286
  }
305
287
  prime(data) {
306
288
  // we have this data from somewhere else, prime it in the c
@@ -329,7 +311,7 @@ class ObjectCountLoader {
329
311
  this.options = options;
330
312
  this.context = context;
331
313
  if (context) {
332
- this.loader = createClauseCountDataLoader(options);
314
+ this.loader = createClauseCountDataLoader(options, context);
333
315
  }
334
316
  }
335
317
  getOptions() {
@@ -339,7 +321,7 @@ class ObjectCountLoader {
339
321
  if (this.loader) {
340
322
  return this.loader.load(key);
341
323
  }
342
- return loadCountForClauseLoader(this.options, key);
324
+ return loadCountForClauseLoader(this.options, key, this.context);
343
325
  }
344
326
  clearAll() {
345
327
  this.loader && this.loader.clearAll();
@@ -352,7 +334,7 @@ class ObjectCountLoader {
352
334
  // @ts-expect-error
353
335
  return this.loader.loadMany(keys);
354
336
  }
355
- return mapWithConcurrency(keys, clauseLoaderConcurrency, (key) => loadCountForClauseLoader(this.options, key));
337
+ return (0, async_utils_1.mapWithConcurrency)(keys, clauseLoaderConcurrency, (key) => loadCountForClauseLoader(this.options, key, this.context));
356
338
  }
357
339
  }
358
340
  exports.ObjectCountLoader = ObjectCountLoader;
@@ -1,4 +1,4 @@
1
- import { Context, Data, EdgeQueryableDataOptions, Loader, LoaderFactory } from "../base";
1
+ import { Context, Data, EdgeQueryableDataOptions, Loader, LoaderFactory, SelectBaseDataOptions } from "../base";
2
2
  import * as clause from "../clause";
3
3
  import { OrderBy } from "../query_impl";
4
4
  import { ObjectLoaderFactory } from "./object_loader";
@@ -14,10 +14,7 @@ declare class QueryDirectLoader<K extends any> implements Loader<K, Data[]> {
14
14
  clearAll(): void;
15
15
  }
16
16
  interface QueryOptions {
17
- fields: (string | {
18
- alias: string;
19
- column: string;
20
- })[];
17
+ fields: SelectBaseDataOptions["fields"];
21
18
  tableName: string;
22
19
  groupCol?: string;
23
20
  clause?: clause.Clause;
@@ -32,17 +32,12 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.QueryLoaderFactory = void 0;
40
- const dataloader_1 = __importDefault(require("dataloader"));
41
- const memoizee_1 = __importDefault(require("memoizee"));
42
37
  const clause = __importStar(require("../clause"));
43
38
  const ent_1 = require("../ent");
44
- const logger_1 = require("../logger");
45
- const cache_utils_1 = require("./cache_utils");
39
+ const memoize_1 = require("../memoize");
40
+ const query_impl_1 = require("../query_impl");
46
41
  const loader_1 = require("./loader");
47
42
  function getOrderByLocal(options, queryOptions) {
48
43
  return (options.orderby ??
@@ -53,7 +48,7 @@ function getOrderByLocal(options, queryOptions) {
53
48
  },
54
49
  ]);
55
50
  }
56
- async function simpleCase(options, id, queryOptions) {
51
+ async function simpleCase(options, id, queryOptions, context) {
57
52
  let cls;
58
53
  if (options.groupCol) {
59
54
  cls = clause.Eq(options.groupCol, id);
@@ -76,24 +71,27 @@ async function simpleCase(options, id, queryOptions) {
76
71
  clause: cls,
77
72
  orderby: getOrderByLocal(options, queryOptions),
78
73
  limit: queryOptions?.limit || (0, ent_1.getDefaultLimit)(),
74
+ context,
79
75
  });
80
76
  }
81
- function createLoader(options, queryOptions) {
77
+ function createLoader(options, queryOptions, context) {
78
+ const loaderName = options.groupCol
79
+ ? `queryLoader:${options.tableName}:${options.groupCol}`
80
+ : options.clause
81
+ ? `queryLoader:${options.tableName}:${options.clause.instanceKey()}`
82
+ : `queryLoader:${options.tableName}`;
82
83
  const loaderOptions = {
83
84
  maxBatchSize: (0, loader_1.getLoaderMaxBatchSize)(),
85
+ cacheMap: (0, loader_1.createLoaderCacheMap)(options),
84
86
  };
85
- // if query logging is enabled, we should log what's happening with loader
86
- if ((0, logger_1.logEnabled)("query")) {
87
- loaderOptions.cacheMap = new loader_1.CacheMap(options);
88
- }
89
- return new dataloader_1.default(async (keys) => {
87
+ return new loader_1.InstrumentedDataLoader(loaderName, async (keys) => {
90
88
  if (!keys.length) {
91
89
  return [];
92
90
  }
93
91
  // keep query simple if we're only fetching for one id
94
92
  // or can't group because no groupCol...
95
93
  if (keys.length == 1 || !options.groupCol) {
96
- const rows = await simpleCase(options, keys[0], queryOptions);
94
+ const rows = await simpleCase(options, keys[0], queryOptions, context);
97
95
  return [rows];
98
96
  }
99
97
  let m = new Map();
@@ -104,6 +102,10 @@ function createLoader(options, queryOptions) {
104
102
  m.set(keys[i], i);
105
103
  }
106
104
  const col = options.groupCol;
105
+ const effectiveOrderBy = getOrderByLocal(options, queryOptions);
106
+ if ((0, query_impl_1.orderByHasExpressions)(effectiveOrderBy)) {
107
+ throw new Error("grouped query loaders do not support computed order expressions");
108
+ }
107
109
  let extraClause;
108
110
  if (options.clause && queryOptions?.clause) {
109
111
  extraClause = clause.And(options.clause, queryOptions.clause);
@@ -118,7 +120,7 @@ function createLoader(options, queryOptions) {
118
120
  tableName: options.tableName,
119
121
  fields: options.fields,
120
122
  values: keys,
121
- orderby: getOrderByLocal(options, queryOptions),
123
+ orderby: effectiveOrderBy,
122
124
  limit: queryOptions?.limit || (0, ent_1.getDefaultLimit)(),
123
125
  groupColumn: col,
124
126
  clause: extraClause,
@@ -134,14 +136,14 @@ function createLoader(options, queryOptions) {
134
136
  result[idx].push(row);
135
137
  }
136
138
  return result;
137
- }, loaderOptions);
139
+ }, loaderOptions, options.tableName);
138
140
  }
139
141
  class QueryDirectLoader {
140
142
  constructor(options, queryOptions, context) {
141
143
  this.options = options;
142
144
  this.queryOptions = queryOptions;
143
145
  this.context = context;
144
- this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
146
+ this.memoizedInitPrime = (0, memoize_1.memoizeNoArgs)(this.initPrime.bind(this));
145
147
  }
146
148
  initPrime() {
147
149
  if (!this.context || !this.options?.toPrime) {
@@ -158,7 +160,7 @@ class QueryDirectLoader {
158
160
  this.primedLoaders = primedLoaders;
159
161
  }
160
162
  async load(id) {
161
- const rows = await simpleCase(this.options, id, this.queryOptions);
163
+ const rows = await simpleCase(this.options, id, this.queryOptions, this.context);
162
164
  if (this.context) {
163
165
  this.memoizedInitPrime();
164
166
  if (this.primedLoaders) {
@@ -184,9 +186,9 @@ class QueryLoader {
184
186
  this.context = context;
185
187
  this.queryOptions = queryOptions;
186
188
  if (context) {
187
- this.loader = createLoader(options, queryOptions);
189
+ this.loader = createLoader(options, queryOptions, context);
188
190
  }
189
- this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
191
+ this.memoizedInitPrime = (0, memoize_1.memoizeNoArgs)(this.initPrime.bind(this));
190
192
  }
191
193
  initPrime() {
192
194
  if (!this.context || !this.options?.toPrime) {
@@ -218,7 +220,7 @@ class QueryLoader {
218
220
  }
219
221
  return rows;
220
222
  }
221
- return simpleCase(this.options, id, this.queryOptions);
223
+ return simpleCase(this.options, id, this.queryOptions, this.context);
222
224
  }
223
225
  clearAll() {
224
226
  this.loader && this.loader.clearAll();
@@ -244,13 +246,32 @@ class QueryLoaderFactory {
244
246
  return QueryLoaderFactory.createConfigurableLoader(this.name, this.options, options, context);
245
247
  }
246
248
  static createConfigurableLoader(name, queryOptions, options, context) {
247
- if (options.clause || !context) {
249
+ if (!context) {
248
250
  return new QueryDirectLoader(queryOptions, options, context);
249
251
  }
252
+ if (options.clause) {
253
+ const effectiveOrderBy = getOrderByLocal(queryOptions, options);
254
+ const effectiveLimit = options.limit || (0, ent_1.getDefaultLimit)();
255
+ const disableTransformations = options.disableTransformations ?? false;
256
+ const keyParts = [
257
+ name,
258
+ `clause:${options.clause.instanceKey()}`,
259
+ queryOptions.clause
260
+ ? `baseClause:${queryOptions.clause.instanceKey()}`
261
+ : undefined,
262
+ `limit:${effectiveLimit}`,
263
+ `orderby:${(0, query_impl_1.getOrderByKey)(effectiveOrderBy)}`,
264
+ `disableTransformations:${disableTransformations}`,
265
+ ];
266
+ const key = keyParts
267
+ .filter((part) => Boolean(part))
268
+ .join(":");
269
+ return (0, loader_1.getCustomLoader)(key, () => new QueryDirectLoader(queryOptions, options, context), context);
270
+ }
250
271
  const effectiveOrderBy = getOrderByLocal(queryOptions, options);
251
272
  const effectiveLimit = options.limit || (0, ent_1.getDefaultLimit)();
252
273
  const disableTransformations = options.disableTransformations ?? false;
253
- const key = `${name}:limit:${effectiveLimit}:orderby:${(0, cache_utils_1.stableStringify)(effectiveOrderBy)}:disableTransformations:${disableTransformations}`;
274
+ const key = `${name}:limit:${effectiveLimit}:orderby:${(0, query_impl_1.getOrderByKey)(effectiveOrderBy)}:disableTransformations:${disableTransformations}`;
254
275
  return (0, loader_1.getCustomLoader)(key, () => new QueryLoader(queryOptions, context, options), context);
255
276
  }
256
277
  }
@@ -1,13 +1,13 @@
1
- import DataLoader from "dataloader";
2
1
  import { ID, Context, Loader, LoaderFactory, SelectBaseDataOptions } from "../base";
3
2
  import * as clause from "../clause";
3
+ import { InstrumentedDataLoader } from "./loader";
4
4
  interface QueryCountOptions {
5
5
  tableName: string;
6
6
  groupCol?: string;
7
7
  groupColType?: string;
8
8
  clause?: clause.Clause;
9
9
  }
10
- export declare function createCountDataLoader<K extends any>(options: QueryCountOptions): DataLoader<K, number, K>;
10
+ export declare function createCountDataLoader<K extends any>(options: QueryCountOptions, context?: Context): InstrumentedDataLoader<K, number>;
11
11
  export declare class RawCountLoader<K extends any> implements Loader<K, number> {
12
12
  private options;
13
13
  context?: Context | undefined;