@snowtop/ent 0.2.0-alpha.1 → 0.2.0-alpha.10-test1
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/orchestrator.js +13 -7
- package/core/base.d.ts +1 -0
- package/core/clause.d.ts +44 -0
- package/core/clause.js +75 -2
- package/core/db.d.ts +1 -1
- package/core/db.js +2 -2
- package/core/ent.d.ts +10 -2
- package/core/ent.js +11 -18
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/assoc_edge_loader.js +3 -3
- package/core/query/assoc_query.d.ts +2 -2
- package/core/query/assoc_query.js +14 -2
- package/core/query/custom_clause_query.d.ts +8 -6
- package/core/query/custom_clause_query.js +22 -7
- package/core/query/custom_query.d.ts +2 -2
- package/core/query/custom_query.js +1 -1
- package/core/query/query.d.ts +3 -7
- package/core/query/query.js +73 -199
- package/core/query/shared_test.d.ts +3 -3
- package/core/query/shared_test.js +31 -23
- package/core/query_impl.d.ts +0 -1
- package/core/query_impl.js +18 -1
- package/graphql/graphql.js +3 -0
- package/graphql/query/edge_connection.d.ts +5 -4
- package/graphql/query/edge_connection.js +34 -14
- package/graphql/query/shared_edge_connection.d.ts +2 -2
- package/graphql/query/shared_edge_connection.js +26 -13
- package/package.json +1 -1
- package/schema/base_schema.js +2 -2
- package/schema/field.js +1 -0
- package/schema/schema.d.ts +2 -0
- package/testutils/builder.js +1 -1
- package/testutils/db/value.d.ts +1 -0
- package/testutils/db/value.js +2 -1
- package/testutils/db_mock.d.ts +1 -1
- package/testutils/db_mock.js +1 -1
- package/testutils/ent-graphql-tests/index.d.ts +1 -1
- package/testutils/ent-graphql-tests/index.js +5 -5
- package/testutils/fake_data/fake_contact.js +2 -2
- package/testutils/fake_data/fake_event.js +1 -1
- package/testutils/fake_data/user_query.d.ts +5 -5
- package/testutils/fake_data/user_query.js +11 -14
- package/tsc/compilerOptions.js +4 -1
package/action/orchestrator.js
CHANGED
|
@@ -621,17 +621,23 @@ class Orchestrator {
|
|
|
621
621
|
}
|
|
622
622
|
if (transformed.data) {
|
|
623
623
|
updateInput = true;
|
|
624
|
-
for (const k
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
624
|
+
for (const [k, field] of schemaFields) {
|
|
625
|
+
const inputKey = this.getInputKey(k);
|
|
626
|
+
const storageKey = this.getStorageKey(k);
|
|
627
|
+
let val = transformed.data[inputKey];
|
|
628
|
+
if (val === undefined) {
|
|
629
|
+
val = transformed.data[storageKey];
|
|
630
|
+
}
|
|
631
|
+
if (val === undefined) {
|
|
632
|
+
continue;
|
|
628
633
|
}
|
|
629
|
-
let val = transformed.data[k];
|
|
630
634
|
if (field.format) {
|
|
631
|
-
val = field.format(transformed.data[k]);
|
|
635
|
+
val = field.format(transformed.data[k], true);
|
|
632
636
|
}
|
|
633
637
|
data[this.getStorageKey(k)] = val;
|
|
634
|
-
|
|
638
|
+
if (!field.immutable) {
|
|
639
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = val;
|
|
640
|
+
}
|
|
635
641
|
// hmm do we need this?
|
|
636
642
|
// TODO how to do this for local tests?
|
|
637
643
|
// this.defaultFieldsByFieldName[k] = val;
|
package/core/base.d.ts
CHANGED
|
@@ -86,6 +86,7 @@ interface JoinOptions<T2 extends Data = Data, K2 = keyof T2> {
|
|
|
86
86
|
tableName: string;
|
|
87
87
|
alias?: string;
|
|
88
88
|
clause: clause.Clause<T2, K2>;
|
|
89
|
+
type?: "inner" | "outer" | "left" | "right";
|
|
89
90
|
}
|
|
90
91
|
export interface QueryDataOptions<T extends Data = Data, K = keyof T> {
|
|
91
92
|
distinct?: boolean;
|
package/core/clause.d.ts
CHANGED
|
@@ -126,6 +126,50 @@ export declare function JSONKeyExists<T extends Data, K = keyof T>(dbCol: K, val
|
|
|
126
126
|
export declare function JSONBKeyInList<T extends Data, K = keyof T>(dbCol: K, jsonCol: string, val: any, overrideAlias?: string): Clause<T, K>;
|
|
127
127
|
export declare function JSONKeyInList<T extends Data, K = keyof T>(dbCol: K, jsonCol: string, val: any, overrideAlias?: string): Clause<T, K>;
|
|
128
128
|
export declare function PaginationMultipleColsSubQuery<T extends Data, K = keyof T>(col: K, op: string, tableName: string, uniqueCol: K, val: any, overrideAlias?: string): Clause<T, K>;
|
|
129
|
+
export type PaginationUnboundColsQueryOrdering<T extends Data, K = keyof T> = {
|
|
130
|
+
sortCol: K;
|
|
131
|
+
direction: "ASC" | "DESC";
|
|
132
|
+
sortValue: string | number | null;
|
|
133
|
+
nullsPlacement?: "first" | "last";
|
|
134
|
+
overrideAlias?: string;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* When paginating over multiple ordered columns, we need to construct a nested
|
|
138
|
+
* set of clauses to correctly filter. The resulting query will be structured as
|
|
139
|
+
* a group of nested ANDs and ORs.
|
|
140
|
+
*
|
|
141
|
+
* The four cases for each ordering entry are:
|
|
142
|
+
*
|
|
143
|
+
* 1) When sorting in ASC order with null placement LAST (e.g., [1,2,3,null,null])
|
|
144
|
+
* a) If the sort value is not null, then it needs to check for values greater than the
|
|
145
|
+
* sort value OR null values.
|
|
146
|
+
* b) If the sort value is null, then it needs to check for null values only.
|
|
147
|
+
* 2) When sorting in ASC order with null placement FIRST (e.g., [null,null,1,2,3])
|
|
148
|
+
* a) If the sort value is not null, then it needs to check for values greater than the
|
|
149
|
+
* sort value.
|
|
150
|
+
* b) If the sort value is null, then there will be no check for this value since all
|
|
151
|
+
* possible values are greater than or equal to null.
|
|
152
|
+
* 3) When sorting in DESC order with null placement LAST (e.g., [1,2,3,null,null])
|
|
153
|
+
* a) If the sort value is not null, then it needs to check for values less than the
|
|
154
|
+
* sort value OR null values.
|
|
155
|
+
* b) If the sort value is null, then it needs to check for null values only.
|
|
156
|
+
* 4) When sorting in DESC order with null placement FIRST (e.g., [null,null,1,2,3])
|
|
157
|
+
* a) If the sort value is not null, then it needs to check for values less than the
|
|
158
|
+
* sort value.
|
|
159
|
+
* b) If the sort value is null, then there will be no check for this value since all
|
|
160
|
+
* possible values are less than or equal to null.
|
|
161
|
+
*
|
|
162
|
+
* Each ordering entry will do the above check OR'ed with the following ordering entry and
|
|
163
|
+
* an equals check. For instance, given a set of ordering [{col1, less1}, {col2, greater2}],
|
|
164
|
+
* the function will construct a query that looks like:
|
|
165
|
+
*
|
|
166
|
+
* col1 < $1 OR (col1 = $1 AND col2 > $2)
|
|
167
|
+
*
|
|
168
|
+
* In english: each ordering entry will check for values lesser/greater than the supplied value,
|
|
169
|
+
* OR equal to the supplied value AND matching the next ordering entry. For the last ordering entry,
|
|
170
|
+
* it will not check for values equal to the supplied value.
|
|
171
|
+
*/
|
|
172
|
+
export declare function PaginationUnboundColsQuery<T extends Data, K = keyof T>(ordering: PaginationUnboundColsQueryOrdering<T, K>[]): Clause<T, K> | undefined;
|
|
129
173
|
export declare function PaginationMultipleColsQuery<T extends Data, K = keyof T>(sortCol: K, cursorCol: K, less: boolean, // if true, <, if false, >
|
|
130
174
|
sortValue: any, cursorValue: any, overrideAlias?: string): Clause<T, K>;
|
|
131
175
|
export declare function Add<T extends Data, K = keyof T>(col: K, value: any, overrideAlias?: string): Clause<T, K>;
|
package/core/clause.js
CHANGED
|
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.JSONKeyInList = exports.JSONBKeyInList = exports.JSONKeyExists = 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.DBTypeNotIn = exports.TextNotIn = exports.IntegerNotIn = exports.UuidNotIn = exports.DBTypeIn = exports.TextIn = exports.IntegerIn = exports.UuidIn = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.ContainsIgnoreCase = exports.EndsWithIgnoreCase = exports.StartsWithIgnoreCase = exports.Contains = exports.EndsWith = exports.StartsWith = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.notInClause = exports.inClause = void 0;
|
|
27
|
-
exports.Expression = exports.getCombinedClause = exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsQuery = exports.PaginationMultipleColsSubQuery = void 0;
|
|
27
|
+
exports.Expression = exports.getCombinedClause = exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsQuery = exports.PaginationUnboundColsQuery = exports.PaginationMultipleColsSubQuery = void 0;
|
|
28
28
|
const db_1 = __importStar(require("./db"));
|
|
29
29
|
const query_impl_1 = require("./query_impl");
|
|
30
30
|
function isSensitive(val) {
|
|
@@ -632,7 +632,7 @@ function ContainsIgnoreCase(col, value, overrideAlias) {
|
|
|
632
632
|
exports.ContainsIgnoreCase = ContainsIgnoreCase;
|
|
633
633
|
function NotEq(col, value, overrideAlias) {
|
|
634
634
|
return new simpleClause(col, value, "!=", {
|
|
635
|
-
handleNull: new isNotNullClause(col),
|
|
635
|
+
handleNull: new isNotNullClause(col, overrideAlias),
|
|
636
636
|
overrideAlias,
|
|
637
637
|
});
|
|
638
638
|
}
|
|
@@ -943,6 +943,79 @@ function PaginationMultipleColsSubQuery(col, op, tableName, uniqueCol, val, over
|
|
|
943
943
|
return new paginationMultipleColumnsSubQueryClause(col, op, tableName, uniqueCol, val, overrideAlias);
|
|
944
944
|
}
|
|
945
945
|
exports.PaginationMultipleColsSubQuery = PaginationMultipleColsSubQuery;
|
|
946
|
+
/**
|
|
947
|
+
* When paginating over multiple ordered columns, we need to construct a nested
|
|
948
|
+
* set of clauses to correctly filter. The resulting query will be structured as
|
|
949
|
+
* a group of nested ANDs and ORs.
|
|
950
|
+
*
|
|
951
|
+
* The four cases for each ordering entry are:
|
|
952
|
+
*
|
|
953
|
+
* 1) When sorting in ASC order with null placement LAST (e.g., [1,2,3,null,null])
|
|
954
|
+
* a) If the sort value is not null, then it needs to check for values greater than the
|
|
955
|
+
* sort value OR null values.
|
|
956
|
+
* b) If the sort value is null, then it needs to check for null values only.
|
|
957
|
+
* 2) When sorting in ASC order with null placement FIRST (e.g., [null,null,1,2,3])
|
|
958
|
+
* a) If the sort value is not null, then it needs to check for values greater than the
|
|
959
|
+
* sort value.
|
|
960
|
+
* b) If the sort value is null, then there will be no check for this value since all
|
|
961
|
+
* possible values are greater than or equal to null.
|
|
962
|
+
* 3) When sorting in DESC order with null placement LAST (e.g., [1,2,3,null,null])
|
|
963
|
+
* a) If the sort value is not null, then it needs to check for values less than the
|
|
964
|
+
* sort value OR null values.
|
|
965
|
+
* b) If the sort value is null, then it needs to check for null values only.
|
|
966
|
+
* 4) When sorting in DESC order with null placement FIRST (e.g., [null,null,1,2,3])
|
|
967
|
+
* a) If the sort value is not null, then it needs to check for values less than the
|
|
968
|
+
* sort value.
|
|
969
|
+
* b) If the sort value is null, then there will be no check for this value since all
|
|
970
|
+
* possible values are less than or equal to null.
|
|
971
|
+
*
|
|
972
|
+
* Each ordering entry will do the above check OR'ed with the following ordering entry and
|
|
973
|
+
* an equals check. For instance, given a set of ordering [{col1, less1}, {col2, greater2}],
|
|
974
|
+
* the function will construct a query that looks like:
|
|
975
|
+
*
|
|
976
|
+
* col1 < $1 OR (col1 = $1 AND col2 > $2)
|
|
977
|
+
*
|
|
978
|
+
* In english: each ordering entry will check for values lesser/greater than the supplied value,
|
|
979
|
+
* OR equal to the supplied value AND matching the next ordering entry. For the last ordering entry,
|
|
980
|
+
* it will not check for values equal to the supplied value.
|
|
981
|
+
*/
|
|
982
|
+
function PaginationUnboundColsQuery(ordering) {
|
|
983
|
+
if (ordering.length === 0) {
|
|
984
|
+
throw new Error("Must provide at least one ordering.");
|
|
985
|
+
}
|
|
986
|
+
let nesting;
|
|
987
|
+
// We reverse the order of the ordering array so that we can build the query from the inner-most
|
|
988
|
+
// clause and work our way outwards
|
|
989
|
+
ordering
|
|
990
|
+
.reverse()
|
|
991
|
+
.forEach(({ sortCol, direction, sortValue, nullsPlacement, overrideAlias }) => {
|
|
992
|
+
const nullsOrder = nullsPlacement ?? (direction === "DESC" ? "first" : "last");
|
|
993
|
+
const clauseFn = direction === "DESC" ? Less : Greater;
|
|
994
|
+
const baseClause = clauseFn(sortCol, sortValue, overrideAlias);
|
|
995
|
+
const compoundClause = sortValue !== null
|
|
996
|
+
? nullsOrder === "last" // There is a non-null value to compare against
|
|
997
|
+
? Or(baseClause, Eq(sortCol, null, overrideAlias)) // If nulls are last, then we need to compare the value and accept nulls
|
|
998
|
+
: And(baseClause, NotEq(sortCol, null, overrideAlias)) // If nulls are first, then we need to compare the value and reject nulls
|
|
999
|
+
: nullsOrder === "last" // The value to compare against is null
|
|
1000
|
+
? Eq(sortCol, null, overrideAlias) // If nulls are last, then we only accept nulls
|
|
1001
|
+
: undefined; // If nulls first then any all values are accepted
|
|
1002
|
+
// This will connect the compound clause to the previous clause for when the matching DB value equals the sort value
|
|
1003
|
+
// E.g. if compoundClause is `col1 < $1` and nesting is `col2 < $2`, then the result will be
|
|
1004
|
+
// `col1 < $1 OR (col1 = $1 AND col2 < $2)`
|
|
1005
|
+
if (compoundClause) {
|
|
1006
|
+
if (sortValue !== null) {
|
|
1007
|
+
nesting = nesting
|
|
1008
|
+
? Or(compoundClause, And(Eq(sortCol, sortValue, overrideAlias), nesting))
|
|
1009
|
+
: compoundClause;
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
nesting = nesting ? And(compoundClause, nesting) : compoundClause;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
return nesting;
|
|
1017
|
+
}
|
|
1018
|
+
exports.PaginationUnboundColsQuery = PaginationUnboundColsQuery;
|
|
946
1019
|
function PaginationMultipleColsQuery(sortCol, cursorCol, less, // if true, <, if false, >
|
|
947
1020
|
sortValue, cursorValue, overrideAlias) {
|
|
948
1021
|
const clauseFn = less ? Less : Greater;
|
package/core/db.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export default class DB {
|
|
|
41
41
|
static getDialect(): Dialect;
|
|
42
42
|
static initDB(args?: clientConfigArgs): void;
|
|
43
43
|
}
|
|
44
|
-
export declare const defaultTimestampParser:
|
|
44
|
+
export declare const defaultTimestampParser: (value: string) => string;
|
|
45
45
|
export interface Queryer {
|
|
46
46
|
query(query: string, values?: any[]): Promise<QueryResult<QueryResultRow>>;
|
|
47
47
|
queryAll(query: string, values?: any[]): Promise<QueryResult<QueryResultRow>>;
|
package/core/db.js
CHANGED
|
@@ -24,11 +24,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.PostgresClient = exports.Postgres = exports.Sqlite = exports.defaultTimestampParser = exports.Dialect = void 0;
|
|
27
|
-
const pg_1 = __importStar(require("pg"));
|
|
28
27
|
const fs = __importStar(require("fs"));
|
|
29
28
|
const js_yaml_1 = require("js-yaml");
|
|
30
|
-
const logger_1 = require("./logger");
|
|
31
29
|
const luxon_1 = require("luxon");
|
|
30
|
+
const pg_1 = __importStar(require("pg"));
|
|
31
|
+
const logger_1 = require("./logger");
|
|
32
32
|
function isDbDict(v) {
|
|
33
33
|
return (v["production"] !== undefined ||
|
|
34
34
|
v["development"] !== undefined ||
|
package/core/ent.d.ts
CHANGED
|
@@ -135,8 +135,16 @@ export declare class AssocEdge {
|
|
|
135
135
|
}
|
|
136
136
|
export interface cursorOptions {
|
|
137
137
|
row: Data;
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
/**
|
|
139
|
+
* The keys that will be stored in the cursor. This should be the DB columns.
|
|
140
|
+
*/
|
|
141
|
+
cursorKeys: string[];
|
|
142
|
+
/**
|
|
143
|
+
* The keys that will be used to get the values from the row. This should be
|
|
144
|
+
* the row object keys. If not provided, the cursorKeys will be used. This is
|
|
145
|
+
* only used for tests internally.
|
|
146
|
+
*/
|
|
147
|
+
rowKeys?: string[];
|
|
140
148
|
}
|
|
141
149
|
export declare function getCursor(opts: cursorOptions): string;
|
|
142
150
|
export declare class AssocEdgeData {
|
package/core/ent.js
CHANGED
|
@@ -869,36 +869,29 @@ class AssocEdge {
|
|
|
869
869
|
getCursor() {
|
|
870
870
|
return getCursor({
|
|
871
871
|
row: this,
|
|
872
|
-
|
|
872
|
+
cursorKeys: ["time", "id2"],
|
|
873
873
|
});
|
|
874
874
|
}
|
|
875
875
|
}
|
|
876
876
|
exports.AssocEdge = AssocEdge;
|
|
877
877
|
// TODO eventually update this for sortCol time unique keys
|
|
878
878
|
function getCursor(opts) {
|
|
879
|
-
const { row,
|
|
879
|
+
const { row, cursorKeys, rowKeys } = opts;
|
|
880
880
|
// row: Data, col: string, conv?: (any) => any) {
|
|
881
881
|
if (!row) {
|
|
882
882
|
throw new Error(`no row passed to getCursor`);
|
|
883
883
|
}
|
|
884
|
-
if (
|
|
885
|
-
throw new Error("length of cursorKeys should match
|
|
884
|
+
if (rowKeys?.length && rowKeys.length !== cursorKeys.length) {
|
|
885
|
+
throw new Error("length of cursorKeys should match rowKeys");
|
|
886
886
|
}
|
|
887
|
-
const convert = (d) =>
|
|
888
|
-
if (d instanceof Date) {
|
|
889
|
-
return d.getTime();
|
|
890
|
-
}
|
|
891
|
-
return d;
|
|
892
|
-
};
|
|
887
|
+
const convert = (d) => (d instanceof Date ? d.toISOString() : d);
|
|
893
888
|
const parts = [];
|
|
894
|
-
for (let i = 0; i <
|
|
895
|
-
const
|
|
896
|
-
const
|
|
897
|
-
parts.push(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const str = parts.join(":");
|
|
901
|
-
return Buffer.from(str, "ascii").toString("base64");
|
|
889
|
+
for (let i = 0; i < cursorKeys.length; i++) {
|
|
890
|
+
const cursorKey = cursorKeys[i];
|
|
891
|
+
const rowKey = rowKeys?.[i] || cursorKey;
|
|
892
|
+
parts.push([cursorKey, convert(row[rowKey])]);
|
|
893
|
+
}
|
|
894
|
+
return btoa(JSON.stringify(parts));
|
|
902
895
|
}
|
|
903
896
|
exports.getCursor = getCursor;
|
|
904
897
|
class AssocEdgeData {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, EdgeQueryableDataOptions, ID, Loader, LoaderFactory } from "../base";
|
|
2
2
|
import { AssocEdge, AssocEdgeConstructor } from "../ent";
|
|
3
3
|
export interface AssocLoader<T extends AssocEdge> extends Loader<ID, T[]> {
|
|
4
4
|
loadEdgeForID2(id: ID, id2: ID): Promise<T | undefined>;
|
|
@@ -28,11 +28,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.AssocEdgeLoaderFactory = exports.AssocDirectEdgeLoader = exports.AssocEdgeLoader = void 0;
|
|
30
30
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
31
|
-
const
|
|
31
|
+
const memoizee_1 = __importDefault(require("memoizee"));
|
|
32
32
|
const clause = __importStar(require("../clause"));
|
|
33
|
+
const ent_1 = require("../ent");
|
|
33
34
|
const logger_1 = require("../logger");
|
|
34
35
|
const loader_1 = require("./loader");
|
|
35
|
-
const memoizee_1 = __importDefault(require("memoizee"));
|
|
36
36
|
function createLoader(options, edgeType, edgeCtr, edgeData) {
|
|
37
37
|
const loaderOptions = {};
|
|
38
38
|
if ((0, logger_1.logEnabled)("query")) {
|
|
@@ -208,7 +208,7 @@ class AssocEdgeLoaderFactory {
|
|
|
208
208
|
return new AssocDirectEdgeLoader(this.edgeType, edgeCtr, options, context);
|
|
209
209
|
}
|
|
210
210
|
// we create a loader which can combine first X queries in the same fetch
|
|
211
|
-
const key = `${this.name}:limit:${options.limit}:orderby:${options.orderby}:disableTransformations:${options.disableTransformations}`;
|
|
211
|
+
const key = `${this.name}:limit:${options.limit}:orderby:${options.orderby?.map((orderBy) => JSON.stringify(orderBy))}:disableTransformations:${options.disableTransformations}`;
|
|
212
212
|
return (0, loader_1.getCustomLoader)(key, () => new AssocEdgeLoader(this.edgeType, ctr, options, context), context);
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, Viewer } from "../base";
|
|
2
2
|
import { AssocEdge } from "../ent";
|
|
3
3
|
import { AssocEdgeCountLoaderFactory } from "../loaders/assoc_count_loader";
|
|
4
4
|
import { AssocEdgeLoaderFactory } from "../loaders/assoc_edge_loader";
|
|
5
|
-
import {
|
|
5
|
+
import { BaseEdgeQuery, EdgeQuery, IDInfo } from "./query";
|
|
6
6
|
export type EdgeQuerySource<TSource extends Ent<TViewer>, TDest extends Ent<TViewer> = Ent<any>, TViewer extends Viewer = Viewer> = TSource | TSource[] | ID | ID[] | EdgeQuery<TDest, Ent, AssocEdge>;
|
|
7
7
|
type loaderOptionsFunc<TViewer extends Viewer> = (type: string) => LoadEntOptions<Ent, TViewer>;
|
|
8
8
|
export declare abstract class AssocEdgeQueryBase<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TEdge extends AssocEdge, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<TSource, TDest, TEdge> implements EdgeQuery<TSource, TDest, TEdge> {
|
|
@@ -24,16 +24,28 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.AssocEdgeQueryBase = void 0;
|
|
27
|
+
const clause = __importStar(require("../clause"));
|
|
27
28
|
const ent_1 = require("../ent");
|
|
28
29
|
const query_1 = require("./query");
|
|
29
|
-
const clause = __importStar(require("../clause"));
|
|
30
30
|
// would be nice to someday have an API for assoc asc
|
|
31
31
|
class AssocEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
32
32
|
constructor(viewer, src, countLoaderFactory, dataLoaderFactory,
|
|
33
33
|
// if function, it's a polymorphic edge and need to provide
|
|
34
34
|
// a function that goes from edgeType to LoadEntOptions
|
|
35
35
|
options) {
|
|
36
|
-
super(viewer,
|
|
36
|
+
super(viewer, {
|
|
37
|
+
cursorCol: "id2",
|
|
38
|
+
orderby: [
|
|
39
|
+
{
|
|
40
|
+
column: "time",
|
|
41
|
+
direction: "DESC",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
column: "id2",
|
|
45
|
+
direction: "DESC",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
});
|
|
37
49
|
this.viewer = viewer;
|
|
38
50
|
this.src = src;
|
|
39
51
|
this.countLoaderFactory = countLoaderFactory;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Data, EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, QueryDataOptions, Viewer } from "../base";
|
|
2
2
|
import { Clause } from "../clause";
|
|
3
3
|
import { OrderBy } from "../query_impl";
|
|
4
|
-
import { BaseEdgeQuery,
|
|
5
|
-
export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
4
|
+
import { BaseEdgeQuery, IDInfo } from "./query";
|
|
5
|
+
export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer, TSource extends Ent<TViewer> | undefined = undefined> {
|
|
6
6
|
loadEntOptions: LoadEntOptions<TDest, TViewer>;
|
|
7
|
+
source?: TSource;
|
|
7
8
|
clause: Clause;
|
|
8
9
|
name: string;
|
|
9
10
|
primarySortColIsUnique?: boolean;
|
|
@@ -14,19 +15,20 @@ export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer ex
|
|
|
14
15
|
disableTransformations?: boolean;
|
|
15
16
|
joinBETA?: QueryDataOptions["join"];
|
|
16
17
|
}
|
|
17
|
-
export declare class CustomClauseQuery<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<any, TDest, Data> {
|
|
18
|
+
export declare class CustomClauseQuery<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer, TSource extends Ent<TViewer> | undefined = undefined> extends BaseEdgeQuery<any, TDest, Data> {
|
|
18
19
|
viewer: TViewer;
|
|
19
20
|
private options;
|
|
20
21
|
private clause;
|
|
22
|
+
private source?;
|
|
21
23
|
constructor(viewer: TViewer, options: CustomClauseQueryOptions<TDest, TViewer>);
|
|
22
|
-
|
|
24
|
+
__maybeSetSource(src: TSource): void;
|
|
25
|
+
sourceEnt(_id: ID): Promise<NonNullable<TSource> | null>;
|
|
23
26
|
getTableName(): string;
|
|
24
|
-
protected includeSortColInCursor(options: EdgeQueryOptions): boolean;
|
|
25
27
|
queryRawCount(): Promise<number>;
|
|
26
28
|
queryAllRawCount(): Promise<Map<ID, number>>;
|
|
27
29
|
protected loadRawIDs(_addID: (src: ID) => void): Promise<void>;
|
|
28
30
|
protected loadRawData(_infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
|
|
29
31
|
dataToID(edge: Data): ID;
|
|
30
32
|
protected loadEntsFromEdges(id: ID, rows: Data[]): Promise<TDest[]>;
|
|
31
|
-
__getOptions(): CustomClauseQueryOptions<TDest, TViewer>;
|
|
33
|
+
__getOptions(): CustomClauseQueryOptions<TDest, TViewer, undefined>;
|
|
32
34
|
}
|
|
@@ -52,17 +52,27 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
|
52
52
|
this.viewer = viewer;
|
|
53
53
|
this.options = options;
|
|
54
54
|
this.clause = getClause(options);
|
|
55
|
+
this.source = options.source;
|
|
56
|
+
}
|
|
57
|
+
__maybeSetSource(src) {
|
|
58
|
+
if (this.source && this.source !== src) {
|
|
59
|
+
console.warn("source already set to something else");
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.source = src;
|
|
63
|
+
}
|
|
55
64
|
}
|
|
56
65
|
async sourceEnt(_id) {
|
|
57
|
-
|
|
66
|
+
// The sourceEnt is used for privacy checks and if we have the source we already know
|
|
67
|
+
// the privacy checks have been done or will be done
|
|
68
|
+
// This is being set for completeness but we don't really care about this.
|
|
69
|
+
// See https://github.com/lolopinto/ent/blob/15af0165f83458acc1d1c9f934f4534dca6154ff/ts/src/core/query/query.ts#L729-L739 for how sourceEnt is
|
|
70
|
+
// used in the codebase
|
|
71
|
+
return this.source ?? null;
|
|
58
72
|
}
|
|
59
73
|
getTableName() {
|
|
60
74
|
return this.options.loadEntOptions.tableName;
|
|
61
75
|
}
|
|
62
|
-
includeSortColInCursor(options) {
|
|
63
|
-
// TODO maybe we should just always do this?
|
|
64
|
-
return options.joinBETA !== undefined && this.sortCol !== this.cursorCol;
|
|
65
|
-
}
|
|
66
76
|
async queryRawCount() {
|
|
67
77
|
// sqlite needs as count otherwise it returns count(1)
|
|
68
78
|
let fields = ["count(1) as count"];
|
|
@@ -97,7 +107,7 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
|
97
107
|
if (!options.orderby) {
|
|
98
108
|
options.orderby = [
|
|
99
109
|
{
|
|
100
|
-
column: this.
|
|
110
|
+
column: this.getCursorCol(),
|
|
101
111
|
direction: this.options.orderByDirection ?? "DESC",
|
|
102
112
|
nullsPlacement: this.options.nullsPlacement,
|
|
103
113
|
},
|
|
@@ -116,7 +126,12 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
|
|
|
116
126
|
// if doing a join, select distinct rows
|
|
117
127
|
distinct: this.options.joinBETA !== undefined,
|
|
118
128
|
});
|
|
119
|
-
this.
|
|
129
|
+
if (this.source) {
|
|
130
|
+
this.edges.set(this.source.id, rows);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.edges.set(1, rows);
|
|
134
|
+
}
|
|
120
135
|
}
|
|
121
136
|
dataToID(edge) {
|
|
122
137
|
return edge.id;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Data, Ent, ID,
|
|
1
|
+
import { ConfigurableLoaderFactory, Data, EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, LoaderFactory, Viewer } from "../base";
|
|
2
2
|
import { Clause } from "../clause";
|
|
3
3
|
import { OrderBy } from "../query_impl";
|
|
4
|
-
import { BaseEdgeQuery,
|
|
4
|
+
import { BaseEdgeQuery, EdgeQuery, IDInfo } from "./query";
|
|
5
5
|
export interface CustomEdgeQueryOptionsDeprecated<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
6
6
|
src: TSource | ID;
|
|
7
7
|
countLoaderFactory: LoaderFactory<ID, number>;
|
package/core/query/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Data, EdgeQueryableDataOptions, EdgeQueryableDataOptionsConfigureLoader, Ent, ID, PrivacyPolicy, QueryableDataOptions, SelectBaseDataOptions, Viewer } from "../base";
|
|
2
2
|
import { OrderBy } from "../query_impl";
|
|
3
3
|
export interface EdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> {
|
|
4
4
|
queryEdges(): Promise<TEdge[]>;
|
|
@@ -31,6 +31,7 @@ export interface PaginationInfo {
|
|
|
31
31
|
startCursor: string;
|
|
32
32
|
endCursor: string;
|
|
33
33
|
}
|
|
34
|
+
export type CursorKeyValues = [key: string, value: string | number | null][];
|
|
34
35
|
export interface EdgeQueryOptions {
|
|
35
36
|
cursorCol: string;
|
|
36
37
|
cursorColIsDate?: boolean;
|
|
@@ -48,16 +49,12 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
48
49
|
protected genIDInfosToFetch: () => Promise<IDInfo[]>;
|
|
49
50
|
private idMap;
|
|
50
51
|
private idsToFetch;
|
|
51
|
-
protected sortCol: string;
|
|
52
52
|
protected cursorCol: string;
|
|
53
|
-
protected cursorColIsDate: boolean;
|
|
54
|
-
protected sortColIsDate: boolean;
|
|
55
53
|
private edgeQueryOptions;
|
|
56
54
|
private limitAdded;
|
|
57
55
|
private cursorKeys;
|
|
58
|
-
constructor(viewer: Viewer, sortCol: string, cursorCol: string);
|
|
59
56
|
constructor(viewer: Viewer, options: EdgeQueryOptions);
|
|
60
|
-
protected
|
|
57
|
+
protected getCursorCol(): string;
|
|
61
58
|
getPrivacyPolicy(): PrivacyPolicy<Ent<Viewer<Ent<any> | null, ID | null>>, Viewer<Ent<any> | null, ID | null>>;
|
|
62
59
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
63
60
|
first(n: number, after?: string): this;
|
|
@@ -89,7 +86,6 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
89
86
|
protected configureEdgeQueryableDataOptions(opts: EdgeQueryableDataOptionsConfigureLoader): void;
|
|
90
87
|
protected getDefaultEdgeQueryOptions(): Partial<Pick<QueryableDataOptions, "clause" | "limit" | "orderby" | "disableTransformations">> | undefined;
|
|
91
88
|
private loadEdges;
|
|
92
|
-
protected includeSortColInCursor(options: EdgeQueryOptions): boolean;
|
|
93
89
|
getCursor(row: TEdge): string;
|
|
94
90
|
}
|
|
95
91
|
export interface IDInfo {
|