@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.
- package/action/executor.js +4 -4
- package/action/operations.js +3 -0
- package/action/orchestrator.js +10 -12
- package/action/topological_sort.d.ts +9 -0
- package/action/topological_sort.js +46 -0
- package/core/async_utils.d.ts +1 -0
- package/core/async_utils.js +29 -0
- package/core/base.d.ts +12 -5
- package/core/clause.d.ts +3 -5
- package/core/clause.js +32 -0
- package/core/config.d.ts +28 -2
- package/core/config.js +14 -1
- package/core/context.d.ts +3 -1
- package/core/context.js +90 -26
- package/core/db.d.ts +12 -2
- package/core/db.js +102 -7
- package/core/dev_schema.d.ts +9 -0
- package/core/dev_schema.js +306 -0
- package/core/ent.d.ts +5 -7
- package/core/ent.js +33 -48
- package/core/extensions.d.ts +25 -0
- package/core/extensions.js +220 -0
- package/core/loaders/assoc_count_loader.js +3 -6
- package/core/loaders/assoc_edge_loader.d.ts +3 -0
- package/core/loaders/assoc_edge_loader.js +48 -19
- package/core/loaders/index.d.ts +2 -1
- package/core/loaders/index.js +5 -1
- package/core/loaders/loader.d.ts +31 -0
- package/core/loaders/loader.js +141 -2
- package/core/loaders/object_loader.d.ts +2 -2
- package/core/loaders/object_loader.js +39 -57
- package/core/loaders/query_loader.d.ts +2 -5
- package/core/loaders/query_loader.js +45 -24
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.js +12 -14
- package/core/memoize.d.ts +1 -0
- package/core/memoize.js +15 -0
- package/core/metrics.d.ts +22 -0
- package/core/metrics.js +31 -0
- package/core/query/custom_clause_query.js +5 -1
- package/core/query/query.d.ts +1 -1
- package/core/query/query.js +10 -7
- package/core/query_expression.d.ts +6 -0
- package/core/query_expression.js +2 -0
- package/core/query_impl.d.ts +19 -3
- package/core/query_impl.js +148 -35
- package/index.d.ts +7 -2
- package/index.js +12 -2
- package/package.json +1 -7
- package/parse_schema/parse.d.ts +2 -12
- package/parse_schema/parse.js +22 -41
- package/schema/index.d.ts +1 -1
- package/schema/schema.d.ts +20 -1
- package/scripts/custom_graphql.js +12 -5
- package/scripts/fix_action_exports.js +1 -1
- package/scripts/migrate_v0.1.js +2 -5
- package/scripts/move_types.js +1 -1
- package/scripts/read_schema.js +2 -5
- package/testutils/builder.js +1 -2
- package/testutils/parse_sql.js +1 -1
- package/tsc/compilerOptions.d.ts +2 -2
- package/tsc/compilerOptions.js +12 -18
- package/tsc/move_generated.js +2 -2
- package/tsc/transform.d.ts +1 -1
- package/tsc/transform.js +16 -2
- package/tsc/transform_action.d.ts +1 -1
- package/tsc/transform_action.js +1 -1
- package/tsc/transform_ent.d.ts +1 -1
- package/tsc/transform_ent.js +1 -1
- package/tsc/transform_schema.d.ts +1 -1
- package/tsc/transform_schema.js +2 -2
- /package/core/{loaders/cache_utils.d.ts → cache_utils.d.ts} +0 -0
- /package/core/{loaders/cache_utils.js → cache_utils.js} +0 -0
package/core/loaders/loader.d.ts
CHANGED
|
@@ -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 {};
|
package/core/loaders/loader.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
190
|
-
return
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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:
|
|
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
|
|
45
|
-
const
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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,
|
|
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 (
|
|
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,
|
|
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):
|
|
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;
|