@snowtop/ent 0.1.0-alpha122 → 0.1.0-alpha124
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/core/base.d.ts +8 -7
- package/core/clause.d.ts +2 -1
- package/core/clause.js +18 -1
- package/core/ent.d.ts +10 -6
- package/core/ent.js +17 -27
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/object_loader.d.ts +28 -8
- package/core/loaders/object_loader.js +176 -39
- package/core/loaders/query_loader.d.ts +1 -1
- package/package.json +1 -1
- package/parse_schema/parse.d.ts +1 -1
- package/parse_schema/parse.js +3 -2
- package/schema/field.js +19 -6
- package/schema/struct_field.js +22 -16
package/core/base.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as clause from "./clause";
|
|
2
|
+
import { ObjectLoaderFactory } from "./loaders";
|
|
2
3
|
export interface Loader<K, V> {
|
|
3
4
|
context?: Context;
|
|
4
5
|
load(key: K): Promise<V>;
|
|
@@ -95,21 +96,21 @@ export interface EditRowOptions extends CreateRowOptions {
|
|
|
95
96
|
whereClause: clause.Clause;
|
|
96
97
|
expressions?: Map<string, clause.Clause>;
|
|
97
98
|
}
|
|
98
|
-
interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
|
|
99
|
-
loaderFactory:
|
|
99
|
+
interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> {
|
|
100
|
+
loaderFactory: ObjectLoaderFactory<TData>;
|
|
100
101
|
ent: EntConstructor<TEnt, TViewer>;
|
|
101
102
|
}
|
|
102
|
-
export interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any,
|
|
103
|
+
export interface LoaderFactoryWithOptions<T extends Data = Data> extends LoaderFactoryWithLoaderMany<any, T | null> {
|
|
103
104
|
options?: SelectDataOptions;
|
|
104
105
|
}
|
|
105
|
-
export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
|
|
106
|
+
export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> extends LoadableEntOptions<TEnt, TViewer, TData>, SelectBaseDataOptions {
|
|
106
107
|
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
107
108
|
}
|
|
108
|
-
export interface SelectCustomDataOptions extends SelectBaseDataOptions {
|
|
109
|
-
loaderFactory:
|
|
109
|
+
export interface SelectCustomDataOptions<T extends Data = Data> extends SelectBaseDataOptions {
|
|
110
|
+
loaderFactory: ObjectLoaderFactory<T>;
|
|
110
111
|
prime?: boolean;
|
|
111
112
|
}
|
|
112
|
-
export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
|
|
113
|
+
export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer, TData extends Data = Data> extends SelectCustomDataOptions<TData> {
|
|
113
114
|
ent: EntConstructor<TEnt, TViewer>;
|
|
114
115
|
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
115
116
|
}
|
package/core/clause.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data } from "./base";
|
|
1
|
+
import { Data, SelectDataOptions } from "./base";
|
|
2
2
|
export interface Clause<T extends Data = Data, K = keyof T> {
|
|
3
3
|
clause(idx: number): string;
|
|
4
4
|
columns(): K[];
|
|
@@ -102,4 +102,5 @@ export declare function Subtract<T extends Data, K = keyof T>(col: K, value: any
|
|
|
102
102
|
export declare function Multiply<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
103
103
|
export declare function Divide<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
104
104
|
export declare function Modulo<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
105
|
+
export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Omit<SelectDataOptions, "key">, cls: Clause<V, K>): Clause<V, K>;
|
|
105
106
|
export {};
|
package/core/clause.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.inClause = void 0;
|
|
26
|
+
exports.getCombinedClause = exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.inClause = void 0;
|
|
27
27
|
const db_1 = __importStar(require("./db"));
|
|
28
28
|
function isSensitive(val) {
|
|
29
29
|
return (val !== null &&
|
|
@@ -731,3 +731,20 @@ function Modulo(col, value) {
|
|
|
731
731
|
return new simpleClause(col, value, "%", new isNullClause(col));
|
|
732
732
|
}
|
|
733
733
|
exports.Modulo = Modulo;
|
|
734
|
+
function getCombinedClause(options, cls) {
|
|
735
|
+
if (options.clause) {
|
|
736
|
+
let optionClause;
|
|
737
|
+
if (typeof options.clause === "function") {
|
|
738
|
+
optionClause = options.clause();
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
optionClause = options.clause;
|
|
742
|
+
}
|
|
743
|
+
if (optionClause) {
|
|
744
|
+
// @ts-expect-error different types
|
|
745
|
+
cls = And(cls, optionClause);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return cls;
|
|
749
|
+
}
|
|
750
|
+
exports.getCombinedClause = getCombinedClause;
|
package/core/ent.d.ts
CHANGED
|
@@ -23,13 +23,13 @@ export declare function loadEntsList<TEnt extends Ent<TViewer>, TViewer extends
|
|
|
23
23
|
* @deperecated use loadCustomEnts
|
|
24
24
|
*/
|
|
25
25
|
export declare function loadEntsFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, clause: clause.Clause, options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
|
|
26
|
-
export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer, TQueryData extends Data = Data, TResultData extends Data = TQueryData, TKey = keyof TQueryData>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer>, query: CustomQuery<TQueryData, TKey>): Promise<TEnt[]>;
|
|
26
|
+
export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer, TQueryData extends Data = Data, TResultData extends Data = TQueryData, TKey = keyof TQueryData>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer, TResultData>, query: CustomQuery<TQueryData, TKey>): Promise<TEnt[]>;
|
|
27
27
|
interface parameterizedQueryOptions {
|
|
28
28
|
query: string;
|
|
29
29
|
values?: any[];
|
|
30
30
|
logValues?: any[];
|
|
31
31
|
}
|
|
32
|
-
export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameterizedQueryOptions | clause.Clause<T, K> | QueryDataOptions
|
|
32
|
+
export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameterizedQueryOptions | clause.Clause<T, K> | QueryDataOptions<T, K>;
|
|
33
33
|
/**
|
|
34
34
|
* Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
|
|
35
35
|
* either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
|
|
@@ -53,11 +53,15 @@ export type CustomQuery<T extends Data = Data, K = keyof T> = string | parameter
|
|
|
53
53
|
* orderby: 'time',
|
|
54
54
|
* disableTransformations: false
|
|
55
55
|
* }) // doesn't change the query
|
|
56
|
+
*
|
|
57
|
+
* For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
|
|
58
|
+
* or parallel queries with the same clause are batched together.
|
|
59
|
+
*
|
|
60
|
+
* If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
|
|
61
|
+
* If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
|
|
56
62
|
*/
|
|
57
|
-
export declare function loadCustomData<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData>(options: SelectCustomDataOptions
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
export declare function loadCustomCount<T extends Data = Data, K = keyof T>(options: CustomCountOptions, query: CustomQuery<T, K>, context: Context | undefined): Promise<number>;
|
|
63
|
+
export declare function loadCustomData<TQueryData extends Data = Data, TResultData extends Data = TQueryData, K = keyof TQueryData>(options: SelectCustomDataOptions<TResultData>, query: CustomQuery<TQueryData, K>, context: Context | undefined): Promise<TResultData[]>;
|
|
64
|
+
export declare function loadCustomCount<T extends Data = Data, K = keyof T>(options: SelectCustomDataOptions<T>, query: CustomQuery<T, K>, context: Context | undefined): Promise<number>;
|
|
61
65
|
export declare function loadDerivedEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, data: Data, loader: new (viewer: TViewer, data: Data) => TEnt): Promise<TEnt | null>;
|
|
62
66
|
export declare function loadDerivedEntX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, data: Data, loader: new (viewer: TViewer, data: Data) => TEnt): Promise<TEnt>;
|
|
63
67
|
export declare function logQuery(query: string, logValues: any[]): void;
|
package/core/ent.js
CHANGED
|
@@ -397,6 +397,12 @@ function isParameterizedQuery(opts) {
|
|
|
397
397
|
* orderby: 'time',
|
|
398
398
|
* disableTransformations: false
|
|
399
399
|
* }) // doesn't change the query
|
|
400
|
+
*
|
|
401
|
+
* For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
|
|
402
|
+
* or parallel queries with the same clause are batched together.
|
|
403
|
+
*
|
|
404
|
+
* If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
|
|
405
|
+
* If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
|
|
400
406
|
*/
|
|
401
407
|
async function loadCustomData(options, query, context) {
|
|
402
408
|
const rows = await loadCustomDataImpl(options, query, context);
|
|
@@ -415,7 +421,10 @@ exports.loadCustomData = loadCustomData;
|
|
|
415
421
|
// NOTE: if you use a raw query or paramterized query with this,
|
|
416
422
|
// you should use `SELECT count(*) as count...`
|
|
417
423
|
async function loadCustomCount(options, query, context) {
|
|
418
|
-
//
|
|
424
|
+
// if clause, we'll use the loader and strong typing/coalescing it provides
|
|
425
|
+
if (typeof query !== "string" && isClause(query)) {
|
|
426
|
+
return options.loaderFactory.createCountLoader(context).load(query);
|
|
427
|
+
}
|
|
419
428
|
const rows = await loadCustomDataImpl({
|
|
420
429
|
...options,
|
|
421
430
|
fields: ["count(1) as count"],
|
|
@@ -430,50 +439,31 @@ function isPrimableLoader(loader) {
|
|
|
430
439
|
return loader != undefined;
|
|
431
440
|
}
|
|
432
441
|
async function loadCustomDataImpl(options, query, context) {
|
|
433
|
-
function getClause(cls) {
|
|
434
|
-
let optClause = options.loaderFactory?.options?.clause;
|
|
435
|
-
if (typeof optClause === "function") {
|
|
436
|
-
optClause = optClause();
|
|
437
|
-
}
|
|
438
|
-
if (!optClause) {
|
|
439
|
-
return cls;
|
|
440
|
-
}
|
|
441
|
-
// @ts-expect-error string|ID mismatch
|
|
442
|
-
return clause.And(cls, optClause);
|
|
443
|
-
}
|
|
444
442
|
if (typeof query === "string") {
|
|
445
443
|
// no caching, perform raw query
|
|
446
444
|
return performRawQuery(query, [], []);
|
|
447
|
-
// @ts-ignore
|
|
448
445
|
}
|
|
449
446
|
else if (isClause(query)) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
// this will have rudimentary caching but nothing crazy
|
|
455
|
-
return loadRows({
|
|
456
|
-
...options,
|
|
457
|
-
// @ts-ignore
|
|
458
|
-
clause: getClause(query),
|
|
459
|
-
context: context,
|
|
460
|
-
});
|
|
447
|
+
const r = await options.loaderFactory
|
|
448
|
+
.createTypedLoader(context)
|
|
449
|
+
.load(query);
|
|
450
|
+
return r;
|
|
461
451
|
}
|
|
462
452
|
else if (isParameterizedQuery(query)) {
|
|
463
453
|
// no caching, perform raw query
|
|
464
454
|
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
465
455
|
}
|
|
466
456
|
else {
|
|
457
|
+
// this will have rudimentary caching but nothing crazy
|
|
467
458
|
let cls = query.clause;
|
|
468
459
|
if (!query.disableTransformations) {
|
|
469
|
-
|
|
470
|
-
cls = getClause(cls);
|
|
460
|
+
cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
|
|
471
461
|
}
|
|
472
|
-
// this will have rudimentary caching but nothing crazy
|
|
473
462
|
return loadRows({
|
|
474
463
|
...query,
|
|
475
464
|
...options,
|
|
476
465
|
context: context,
|
|
466
|
+
// @ts-expect-error
|
|
477
467
|
clause: cls,
|
|
478
468
|
});
|
|
479
469
|
}
|
|
@@ -21,7 +21,7 @@ export declare class AssocDirectEdgeLoader<T extends AssocEdge> implements Loade
|
|
|
21
21
|
private edgeCtr;
|
|
22
22
|
private options?;
|
|
23
23
|
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
24
|
-
constructor(edgeType: string, edgeCtr: AssocEdgeConstructor<T>, options?: Partial<Pick<import("../base").QueryableDataOptions, "
|
|
24
|
+
constructor(edgeType: string, edgeCtr: AssocEdgeConstructor<T>, options?: Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
|
|
25
25
|
load(id: ID): Promise<T[]>;
|
|
26
26
|
loadEdgeForID2(id: ID, id2: ID): Promise<T | undefined>;
|
|
27
27
|
clearAll(): void;
|
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
import { ID, Data, SelectDataOptions, Context, Loader, LoaderFactory } from "../base";
|
|
2
|
-
|
|
2
|
+
import * as clause from "../clause";
|
|
3
|
+
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> {
|
|
3
4
|
private options;
|
|
4
5
|
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
5
6
|
private toPrime?;
|
|
6
|
-
private
|
|
7
|
+
private idLoader;
|
|
8
|
+
private clauseLoader;
|
|
7
9
|
private primedLoaders;
|
|
8
10
|
private memoizedInitPrime;
|
|
9
|
-
constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, toPrime?: ObjectLoaderFactory<
|
|
11
|
+
constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, toPrime?: ObjectLoaderFactory<TResultData>[] | undefined);
|
|
10
12
|
getOptions(): SelectDataOptions;
|
|
11
13
|
private initPrime;
|
|
12
|
-
load(key: ID): Promise<
|
|
14
|
+
load(key: ID): Promise<TResultData | null>;
|
|
15
|
+
load(key: clause.Clause<TQueryData, K>): Promise<TResultData[] | null>;
|
|
16
|
+
private loadID;
|
|
17
|
+
private loadClause;
|
|
18
|
+
clearAll(): void;
|
|
19
|
+
loadMany(keys: ID[]): Promise<Array<TResultData | null>>;
|
|
20
|
+
loadMany(keys: clause.Clause<TQueryData, K>[]): Promise<Array<TResultData[] | null>>;
|
|
21
|
+
private loadIDMany;
|
|
22
|
+
private loadClauseMany;
|
|
23
|
+
prime(data: TResultData): void;
|
|
24
|
+
primeAll(data: TResultData): void;
|
|
25
|
+
}
|
|
26
|
+
export declare class ObjectCountLoader<V extends Data = Data, K = keyof V> implements Loader<clause.Clause<V, K>, number> {
|
|
27
|
+
private options;
|
|
28
|
+
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
29
|
+
private loader;
|
|
30
|
+
constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
|
|
31
|
+
getOptions(): SelectDataOptions;
|
|
32
|
+
load(key: clause.Clause<V, K>): Promise<number>;
|
|
13
33
|
clearAll(): void;
|
|
14
|
-
loadMany(keys:
|
|
15
|
-
prime(data: V): void;
|
|
16
|
-
primeAll(data: V): void;
|
|
34
|
+
loadMany(keys: clause.Clause<V, K>[]): Promise<Array<number>>;
|
|
17
35
|
}
|
|
18
36
|
interface ObjectLoaderOptions extends SelectDataOptions {
|
|
19
37
|
instanceKey?: string;
|
|
20
38
|
}
|
|
21
|
-
export declare class ObjectLoaderFactory<V = Data> implements LoaderFactory<ID, V | null> {
|
|
39
|
+
export declare class ObjectLoaderFactory<V extends Data = Data> implements LoaderFactory<ID, V | null>, LoaderFactory<clause.Clause<V>, V[] | null> {
|
|
22
40
|
options: ObjectLoaderOptions;
|
|
23
41
|
name: string;
|
|
24
42
|
private toPrime;
|
|
25
43
|
constructor(options: ObjectLoaderOptions);
|
|
26
44
|
createLoader(context?: Context): ObjectLoader<V>;
|
|
45
|
+
createTypedLoader<TQueryData extends Data = Data, TResultData extends Data = Data, K = keyof TQueryData>(context?: Context): ObjectLoader<TQueryData, TResultData, K>;
|
|
46
|
+
createCountLoader<K = keyof V>(context?: Context): ObjectCountLoader<V, K>;
|
|
27
47
|
addToPrime(factory: ObjectLoaderFactory<V>): this;
|
|
28
48
|
}
|
|
29
49
|
export {};
|
|
@@ -26,29 +26,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.ObjectLoaderFactory = exports.ObjectLoader = void 0;
|
|
29
|
+
exports.ObjectLoaderFactory = exports.ObjectCountLoader = exports.ObjectLoader = void 0;
|
|
30
30
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
31
31
|
const ent_1 = require("../ent");
|
|
32
32
|
const clause = __importStar(require("../clause"));
|
|
33
33
|
const logger_1 = require("../logger");
|
|
34
|
+
const clause_1 = require("../clause");
|
|
34
35
|
const loader_1 = require("./loader");
|
|
35
36
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
36
|
-
async function
|
|
37
|
+
async function loadRowsForIDLoader(options, ids, context) {
|
|
37
38
|
let col = options.key;
|
|
38
|
-
|
|
39
|
-
if (options.clause) {
|
|
40
|
-
let optionClause;
|
|
41
|
-
if (typeof options.clause === "function") {
|
|
42
|
-
optionClause = options.clause();
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
optionClause = options.clause;
|
|
46
|
-
}
|
|
47
|
-
if (optionClause) {
|
|
48
|
-
// @ts-expect-error id/string mismatch
|
|
49
|
-
cls = clause.And(cls, optionClause);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
39
|
+
const cls = (0, clause_1.getCombinedClause)(options, clause.In(col, ...ids));
|
|
52
40
|
const rowOptions = {
|
|
53
41
|
...options,
|
|
54
42
|
clause: cls,
|
|
@@ -75,6 +63,29 @@ async function loadRowsForLoader(options, ids, context) {
|
|
|
75
63
|
}
|
|
76
64
|
return result;
|
|
77
65
|
}
|
|
66
|
+
async function loadRowsForClauseLoader(options, clause) {
|
|
67
|
+
const rowOptions = {
|
|
68
|
+
...options,
|
|
69
|
+
// @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
|
|
70
|
+
clause: (0, clause_1.getCombinedClause)(options, clause),
|
|
71
|
+
};
|
|
72
|
+
return (await (0, ent_1.loadRows)(rowOptions));
|
|
73
|
+
}
|
|
74
|
+
async function loadCountForClauseLoader(options, clause) {
|
|
75
|
+
const rowOptions = {
|
|
76
|
+
...options,
|
|
77
|
+
// @ts-expect-error clause in LoadRowOptions doesn't take templatized version of Clause
|
|
78
|
+
clause: (0, clause_1.getCombinedClause)(options, clause),
|
|
79
|
+
};
|
|
80
|
+
const row = await (0, ent_1.loadRow)({
|
|
81
|
+
...rowOptions,
|
|
82
|
+
fields: ["count(*) as count"],
|
|
83
|
+
});
|
|
84
|
+
if (!row) {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
return parseInt(row.count, 10);
|
|
88
|
+
}
|
|
78
89
|
// optional clause...
|
|
79
90
|
// so ObjectLoaderFactory and createDataLoader need to take a new optional field which is a clause that's always added here
|
|
80
91
|
// and we need a disableTransform which skips loader completely and uses loadRow...
|
|
@@ -89,9 +100,66 @@ function createDataLoader(options) {
|
|
|
89
100
|
return [];
|
|
90
101
|
}
|
|
91
102
|
// context not needed because we're creating a loader which has its own cache which is being used here
|
|
92
|
-
return
|
|
103
|
+
return loadRowsForIDLoader(options, ids);
|
|
93
104
|
}, loaderOptions);
|
|
94
105
|
}
|
|
106
|
+
class clauseCacheMap {
|
|
107
|
+
constructor(options, count) {
|
|
108
|
+
this.options = options;
|
|
109
|
+
this.count = count;
|
|
110
|
+
this.m = new Map();
|
|
111
|
+
}
|
|
112
|
+
get(key) {
|
|
113
|
+
const key2 = key.instanceKey();
|
|
114
|
+
const ret = this.m.get(key2);
|
|
115
|
+
if (ret) {
|
|
116
|
+
(0, logger_1.log)("cache", {
|
|
117
|
+
"dataloader-cache-hit": key2 + (this.count ? ":count" : ""),
|
|
118
|
+
"tableName": this.options.tableName,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return ret;
|
|
122
|
+
}
|
|
123
|
+
set(key, value) {
|
|
124
|
+
return this.m.set(key.instanceKey(), value);
|
|
125
|
+
}
|
|
126
|
+
delete(key) {
|
|
127
|
+
return this.m.delete(key.instanceKey());
|
|
128
|
+
}
|
|
129
|
+
clear() {
|
|
130
|
+
return this.m.clear();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function createClauseDataLoder(options) {
|
|
134
|
+
return new dataloader_1.default(async (clauses) => {
|
|
135
|
+
if (!clauses.length) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
const ret = [];
|
|
139
|
+
for await (const clause of clauses) {
|
|
140
|
+
const data = await loadRowsForClauseLoader(options, clause);
|
|
141
|
+
ret.push(data);
|
|
142
|
+
}
|
|
143
|
+
return ret;
|
|
144
|
+
}, {
|
|
145
|
+
cacheMap: new clauseCacheMap(options),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function createClauseCountDataLoader(options) {
|
|
149
|
+
return new dataloader_1.default(async (clauses) => {
|
|
150
|
+
if (!clauses.length) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
const ret = [];
|
|
154
|
+
for await (const clause of clauses) {
|
|
155
|
+
const data = await loadCountForClauseLoader(options, clause);
|
|
156
|
+
ret.push(data);
|
|
157
|
+
}
|
|
158
|
+
return ret;
|
|
159
|
+
}, {
|
|
160
|
+
cacheMap: new clauseCacheMap(options, true),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
95
163
|
class ObjectLoader {
|
|
96
164
|
constructor(options, context, toPrime) {
|
|
97
165
|
this.options = options;
|
|
@@ -101,7 +169,8 @@ class ObjectLoader {
|
|
|
101
169
|
console.trace();
|
|
102
170
|
}
|
|
103
171
|
if (context) {
|
|
104
|
-
this.
|
|
172
|
+
this.idLoader = createDataLoader(options);
|
|
173
|
+
this.clauseLoader = createClauseDataLoder(options);
|
|
105
174
|
}
|
|
106
175
|
this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
|
|
107
176
|
}
|
|
@@ -123,11 +192,17 @@ class ObjectLoader {
|
|
|
123
192
|
this.primedLoaders = primedLoaders;
|
|
124
193
|
}
|
|
125
194
|
async load(key) {
|
|
195
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
196
|
+
return this.loadID(key);
|
|
197
|
+
}
|
|
198
|
+
return this.loadClause(key);
|
|
199
|
+
}
|
|
200
|
+
async loadID(key) {
|
|
126
201
|
// simple case. we get parallelization etc
|
|
127
|
-
if (this.
|
|
202
|
+
if (this.idLoader) {
|
|
128
203
|
this.memoizedInitPrime();
|
|
129
204
|
// prime the result if we got primable loaders
|
|
130
|
-
const result = await this.
|
|
205
|
+
const result = await this.idLoader.load(key);
|
|
131
206
|
if (result && this.primedLoaders) {
|
|
132
207
|
for (const [key, loader] of this.primedLoaders) {
|
|
133
208
|
const value = result[key];
|
|
@@ -138,19 +213,7 @@ class ObjectLoader {
|
|
|
138
213
|
}
|
|
139
214
|
return result;
|
|
140
215
|
}
|
|
141
|
-
|
|
142
|
-
if (this.options.clause) {
|
|
143
|
-
let optionClause;
|
|
144
|
-
if (typeof this.options.clause === "function") {
|
|
145
|
-
optionClause = this.options.clause();
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
optionClause = this.options.clause;
|
|
149
|
-
}
|
|
150
|
-
if (optionClause) {
|
|
151
|
-
cls = clause.And(cls, optionClause);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
216
|
+
const cls = (0, clause_1.getCombinedClause)(this.options, clause.Eq(this.options.key, key));
|
|
154
217
|
const rowOptions = {
|
|
155
218
|
...this.options,
|
|
156
219
|
clause: cls,
|
|
@@ -158,22 +221,50 @@ class ObjectLoader {
|
|
|
158
221
|
};
|
|
159
222
|
return (0, ent_1.loadRow)(rowOptions);
|
|
160
223
|
}
|
|
224
|
+
async loadClause(key) {
|
|
225
|
+
if (this.clauseLoader) {
|
|
226
|
+
return this.clauseLoader.load(key);
|
|
227
|
+
}
|
|
228
|
+
return loadRowsForClauseLoader(this.options, key);
|
|
229
|
+
}
|
|
161
230
|
clearAll() {
|
|
162
|
-
this.
|
|
231
|
+
this.idLoader && this.idLoader.clearAll();
|
|
232
|
+
this.clauseLoader && this.clauseLoader.clearAll();
|
|
163
233
|
}
|
|
164
234
|
async loadMany(keys) {
|
|
165
|
-
if (
|
|
235
|
+
if (!keys.length) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
if (typeof keys[0] === "string" || typeof keys[0] === "number") {
|
|
239
|
+
return this.loadIDMany(keys);
|
|
240
|
+
}
|
|
241
|
+
return this.loadClauseMany(keys);
|
|
242
|
+
}
|
|
243
|
+
loadIDMany(keys) {
|
|
244
|
+
if (this.idLoader) {
|
|
166
245
|
// @ts-expect-error TODO?
|
|
167
|
-
return this.
|
|
246
|
+
return this.idLoader.loadMany(keys);
|
|
247
|
+
}
|
|
248
|
+
return loadRowsForIDLoader(this.options, keys, this.context);
|
|
249
|
+
}
|
|
250
|
+
async loadClauseMany(keys) {
|
|
251
|
+
if (this.clauseLoader) {
|
|
252
|
+
// @ts-expect-error TODO?
|
|
253
|
+
return this.clauseLoader.loadMany(keys);
|
|
254
|
+
}
|
|
255
|
+
const res = [];
|
|
256
|
+
for await (const key of keys) {
|
|
257
|
+
const rows = await loadRowsForClauseLoader(this.options, key);
|
|
258
|
+
res.push(rows);
|
|
168
259
|
}
|
|
169
|
-
return
|
|
260
|
+
return res;
|
|
170
261
|
}
|
|
171
262
|
prime(data) {
|
|
172
263
|
// we have this data from somewhere else, prime it in the c
|
|
173
|
-
if (this.
|
|
264
|
+
if (this.idLoader) {
|
|
174
265
|
const col = this.options.key;
|
|
175
266
|
const key = data[col];
|
|
176
|
-
this.
|
|
267
|
+
this.idLoader.prime(key, data);
|
|
177
268
|
}
|
|
178
269
|
}
|
|
179
270
|
// prime this loader and any other loaders it's aware of
|
|
@@ -190,6 +281,43 @@ class ObjectLoader {
|
|
|
190
281
|
}
|
|
191
282
|
}
|
|
192
283
|
exports.ObjectLoader = ObjectLoader;
|
|
284
|
+
class ObjectCountLoader {
|
|
285
|
+
constructor(options, context) {
|
|
286
|
+
this.options = options;
|
|
287
|
+
this.context = context;
|
|
288
|
+
if (context) {
|
|
289
|
+
this.loader = createClauseCountDataLoader(options);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
getOptions() {
|
|
293
|
+
return this.options;
|
|
294
|
+
}
|
|
295
|
+
async load(key) {
|
|
296
|
+
if (this.loader) {
|
|
297
|
+
return this.loader.load(key);
|
|
298
|
+
}
|
|
299
|
+
return loadCountForClauseLoader(this.options, key);
|
|
300
|
+
}
|
|
301
|
+
clearAll() {
|
|
302
|
+
this.loader && this.loader.clearAll();
|
|
303
|
+
}
|
|
304
|
+
async loadMany(keys) {
|
|
305
|
+
if (!keys.length) {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
if (this.loader) {
|
|
309
|
+
// @ts-expect-error
|
|
310
|
+
return this.loader.loadMany(keys);
|
|
311
|
+
}
|
|
312
|
+
const res = [];
|
|
313
|
+
for await (const key of keys) {
|
|
314
|
+
const r = await loadCountForClauseLoader(this.options, key);
|
|
315
|
+
res.push(r);
|
|
316
|
+
}
|
|
317
|
+
return res;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
exports.ObjectCountLoader = ObjectCountLoader;
|
|
193
321
|
// NOTE: if not querying for all columns
|
|
194
322
|
// have to query for the id field as one of the fields
|
|
195
323
|
// because it's used to maintain sort order of the queried ids
|
|
@@ -213,6 +341,15 @@ class ObjectLoaderFactory {
|
|
|
213
341
|
return new ObjectLoader(this.options, context, this.toPrime);
|
|
214
342
|
}, context);
|
|
215
343
|
}
|
|
344
|
+
createTypedLoader(context) {
|
|
345
|
+
const loader = this.createLoader(context);
|
|
346
|
+
return loader;
|
|
347
|
+
}
|
|
348
|
+
createCountLoader(context) {
|
|
349
|
+
return (0, loader_1.getCustomLoader)(`${this.name}:count_loader`, () => {
|
|
350
|
+
return new ObjectCountLoader(this.options, context);
|
|
351
|
+
}, context);
|
|
352
|
+
}
|
|
216
353
|
// keep track of loaders to prime. needs to be done not in the constructor
|
|
217
354
|
// because there's usually self references here
|
|
218
355
|
addToPrime(factory) {
|
|
@@ -8,7 +8,7 @@ declare class QueryDirectLoader<K extends any> implements Loader<K, Data[]> {
|
|
|
8
8
|
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
9
9
|
private memoizedInitPrime;
|
|
10
10
|
private primedLoaders;
|
|
11
|
-
constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "
|
|
11
|
+
constructor(options: QueryOptions, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby">> | undefined, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
|
|
12
12
|
private initPrime;
|
|
13
13
|
load(id: K): Promise<Data[]>;
|
|
14
14
|
clearAll(): void;
|
package/package.json
CHANGED
package/parse_schema/parse.d.ts
CHANGED
package/parse_schema/parse.js
CHANGED
|
@@ -358,9 +358,10 @@ async function parseGlobalSchema(s) {
|
|
|
358
358
|
const ret = {
|
|
359
359
|
globalEdges: [],
|
|
360
360
|
extraEdgeFields: [],
|
|
361
|
-
|
|
361
|
+
init: !!s.extraEdgeFields ||
|
|
362
362
|
s.transformEdgeRead !== undefined ||
|
|
363
|
-
s.transformEdgeWrite !== undefined
|
|
363
|
+
s.transformEdgeWrite !== undefined ||
|
|
364
|
+
s.fields !== undefined,
|
|
364
365
|
};
|
|
365
366
|
if (s.extraEdgeFields) {
|
|
366
367
|
ret.extraEdgeFields = await processFields(s.extraEdgeFields);
|
package/schema/field.js
CHANGED
|
@@ -32,6 +32,7 @@ const base_1 = require("../core/base");
|
|
|
32
32
|
const db_1 = __importStar(require("../core/db"));
|
|
33
33
|
const schema_1 = require("./schema");
|
|
34
34
|
const global_schema_1 = require("../core/global_schema");
|
|
35
|
+
const logger_1 = require("../core/logger");
|
|
35
36
|
class BaseField {
|
|
36
37
|
logValue(val) {
|
|
37
38
|
if (this.sensitive) {
|
|
@@ -568,10 +569,16 @@ class EnumField extends BaseField {
|
|
|
568
569
|
async valid(val) {
|
|
569
570
|
if (this.type.globalType) {
|
|
570
571
|
const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
|
|
571
|
-
if (f
|
|
572
|
-
|
|
572
|
+
if (f) {
|
|
573
|
+
if (f.valid) {
|
|
574
|
+
return f.valid(val);
|
|
575
|
+
}
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
(0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
|
|
580
|
+
return false;
|
|
573
581
|
}
|
|
574
|
-
return false;
|
|
575
582
|
}
|
|
576
583
|
// lookup table enum and indicated via presence of foreignKey
|
|
577
584
|
if (!this.values && !this.map) {
|
|
@@ -648,10 +655,16 @@ class IntegerEnumField extends BaseField {
|
|
|
648
655
|
async valid(val) {
|
|
649
656
|
if (this.type?.globalType) {
|
|
650
657
|
const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
|
|
651
|
-
if (f
|
|
652
|
-
|
|
658
|
+
if (f) {
|
|
659
|
+
if (f.valid) {
|
|
660
|
+
return f.valid(val);
|
|
661
|
+
}
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
(0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
|
|
666
|
+
return false;
|
|
653
667
|
}
|
|
654
|
-
return false;
|
|
655
668
|
}
|
|
656
669
|
// lookup table enum and indicated via presence of foreignKey
|
|
657
670
|
for (const k in this.map) {
|
package/schema/struct_field.js
CHANGED
|
@@ -5,6 +5,7 @@ const camel_case_1 = require("camel-case");
|
|
|
5
5
|
const field_1 = require("./field");
|
|
6
6
|
const schema_1 = require("./schema");
|
|
7
7
|
const global_schema_1 = require("../core/global_schema");
|
|
8
|
+
const logger_1 = require("../core/logger");
|
|
8
9
|
class StructField extends field_1.BaseField {
|
|
9
10
|
constructor(options, jsonAsList) {
|
|
10
11
|
super();
|
|
@@ -137,26 +138,31 @@ class StructField extends field_1.BaseField {
|
|
|
137
138
|
async valid(obj) {
|
|
138
139
|
if (this.type.globalType) {
|
|
139
140
|
const f = (0, global_schema_1.__getGlobalSchemaField)(this.type.globalType);
|
|
140
|
-
// list and global type is not valid.
|
|
141
|
-
if (f
|
|
142
|
-
if (
|
|
143
|
-
JSON.stringify(
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
141
|
+
// list and global type is not valid.
|
|
142
|
+
if (f) {
|
|
143
|
+
if (f.valid) {
|
|
144
|
+
if (JSON.stringify(this.type.listElemType) !==
|
|
145
|
+
JSON.stringify(f?.type.listElemType)) {
|
|
146
|
+
if (this.jsonAsList) {
|
|
147
|
+
if (!Array.isArray(obj)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
const valid = await Promise.all(obj.map((v) => f.valid(v)));
|
|
152
|
+
return valid.every((b) => b);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return f.valid([obj]);
|
|
147
156
|
}
|
|
148
|
-
// @ts-ignore
|
|
149
|
-
const valid = await Promise.all(obj.map((v) => f.valid(v)));
|
|
150
|
-
return valid.every((b) => b);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
// TODO handle test
|
|
154
|
-
return f.valid([obj]);
|
|
155
157
|
}
|
|
158
|
+
return f.valid(obj);
|
|
156
159
|
}
|
|
157
|
-
return
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
(0, logger_1.log)("error", `globalType ${this.type.globalType} not found in global schema`);
|
|
164
|
+
return false;
|
|
158
165
|
}
|
|
159
|
-
return false;
|
|
160
166
|
}
|
|
161
167
|
if (this.jsonAsList) {
|
|
162
168
|
if (!Array.isArray(obj)) {
|