@snowtop/ent 0.1.19-test2 → 0.1.20

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/context.js CHANGED
@@ -46,7 +46,7 @@ class ContextCache {
46
46
  parts.push((0, query_impl_1.getOrderByPhrase)(options.orderby));
47
47
  }
48
48
  if (options.join) {
49
- parts.push((0, query_impl_1.getJoinPhrase)(options.join));
49
+ parts.push((0, query_impl_1.getJoinInfo)(options.join).phrase);
50
50
  }
51
51
  return parts.join(",");
52
52
  }
package/core/ent.d.ts CHANGED
@@ -132,9 +132,8 @@ export declare class AssocEdge {
132
132
  }
133
133
  export interface cursorOptions {
134
134
  row: Data;
135
- col: string;
136
- cursorKey?: string;
137
- conv?: (any: any) => any;
135
+ keys: string[];
136
+ cursorKeys?: string[];
138
137
  }
139
138
  export declare function getCursor(opts: cursorOptions): string;
140
139
  export declare class AssocEdgeData {
package/core/ent.js CHANGED
@@ -848,27 +848,35 @@ class AssocEdge {
848
848
  getCursor() {
849
849
  return getCursor({
850
850
  row: this,
851
- col: "id2",
851
+ keys: ["id2"],
852
852
  });
853
853
  }
854
854
  }
855
855
  exports.AssocEdge = AssocEdge;
856
856
  // TODO eventually update this for sortCol time unique keys
857
857
  function getCursor(opts) {
858
- const { row, col, conv } = opts;
858
+ const { row, keys, cursorKeys } = opts;
859
859
  // row: Data, col: string, conv?: (any) => any) {
860
860
  if (!row) {
861
861
  throw new Error(`no row passed to getCursor`);
862
862
  }
863
- let datum = row[col];
864
- if (!datum) {
865
- return "";
863
+ if (cursorKeys?.length && cursorKeys.length !== keys.length) {
864
+ throw new Error("length of cursorKeys should match keys");
866
865
  }
867
- if (conv) {
868
- datum = conv(datum);
869
- }
870
- const cursorKey = opts.cursorKey || col;
871
- const str = `${cursorKey}:${datum}`;
866
+ const convert = (d) => {
867
+ if (d instanceof Date) {
868
+ return d.getTime();
869
+ }
870
+ return d;
871
+ };
872
+ const parts = [];
873
+ for (let i = 0; i < keys.length; i++) {
874
+ const key = keys[i];
875
+ const cursorKey = cursorKeys?.[i] || key;
876
+ parts.push(key);
877
+ parts.push(convert(row[cursorKey]));
878
+ }
879
+ const str = parts.join(":");
872
880
  return Buffer.from(str, "ascii").toString("base64");
873
881
  }
874
882
  exports.getCursor = getCursor;
@@ -1078,13 +1086,15 @@ async function loadTwoWayEdges(opts) {
1078
1086
  fields,
1079
1087
  clause: actualClause,
1080
1088
  context: opts.context,
1081
- join: {
1082
- tableName,
1083
- alias: "t2",
1084
- clause: clause.And(
1085
- // these are not values so need this to not think they're values...
1086
- clause.Expression("t1.id1 = t2.id2"), clause.Expression("t1.id2 = t2.id1")),
1087
- },
1089
+ join: [
1090
+ {
1091
+ tableName,
1092
+ alias: "t2",
1093
+ clause: clause.And(
1094
+ // these are not values so need this to not think they're values...
1095
+ clause.Expression("t1.id1 = t2.id2"), clause.Expression("t1.id2 = t2.id1")),
1096
+ },
1097
+ ],
1088
1098
  });
1089
1099
  return rows;
1090
1100
  }
@@ -1,7 +1,7 @@
1
- import { Data, EdgeQueryableDataOptions, Ent, ID, LoadEntOptions, Viewer } from "../base";
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, IDInfo } from "./query";
4
+ import { BaseEdgeQuery, EdgeQueryOptions, IDInfo } from "./query";
5
5
  export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
6
6
  loadEntOptions: LoadEntOptions<TDest, TViewer>;
7
7
  clause: Clause;
@@ -12,6 +12,7 @@ export interface CustomClauseQueryOptions<TDest extends Ent<TViewer>, TViewer ex
12
12
  orderByDirection?: "ASC" | "DESC";
13
13
  nullsPlacement?: "first" | "last";
14
14
  disableTransformations?: boolean;
15
+ joinBETA?: QueryDataOptions["join"];
15
16
  }
16
17
  export declare class CustomClauseQuery<TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<any, TDest, Data> {
17
18
  viewer: TViewer;
@@ -20,10 +21,12 @@ export declare class CustomClauseQuery<TDest extends Ent<TViewer>, TViewer exten
20
21
  constructor(viewer: TViewer, options: CustomClauseQueryOptions<TDest, TViewer>);
21
22
  sourceEnt(_id: ID): Promise<null>;
22
23
  getTableName(): string;
24
+ protected includeSortColInCursor(options: EdgeQueryOptions): boolean;
23
25
  queryRawCount(): Promise<number>;
24
26
  queryAllRawCount(): Promise<Map<ID, number>>;
25
27
  protected loadRawIDs(_addID: (src: ID) => void): Promise<void>;
26
28
  protected loadRawData(_infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
27
29
  dataToID(edge: Data): ID;
28
30
  protected loadEntsFromEdges(id: ID, rows: Data[]): Promise<TDest[]>;
31
+ __getOptions(): CustomClauseQueryOptions<TDest, TViewer>;
29
32
  }
@@ -46,6 +46,8 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
46
46
  super(viewer, {
47
47
  orderby,
48
48
  cursorCol,
49
+ joinBETA: options.joinBETA,
50
+ fieldOptions: options.loadEntOptions,
49
51
  });
50
52
  this.viewer = viewer;
51
53
  this.options = options;
@@ -57,13 +59,32 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
57
59
  getTableName() {
58
60
  return this.options.loadEntOptions.tableName;
59
61
  }
62
+ includeSortColInCursor(options) {
63
+ // TODO maybe we should just always do this?
64
+ return options.joinBETA !== undefined && this.sortCol !== this.cursorCol;
65
+ }
60
66
  async queryRawCount() {
67
+ // sqlite needs as count otherwise it returns count(1)
68
+ let fields = ["count(1) as count"];
69
+ if (this.options.joinBETA) {
70
+ const requestedFields = this.options.loadEntOptions.fields;
71
+ const alias = this.options.loadEntOptions.fieldsAlias ??
72
+ this.options.loadEntOptions.alias;
73
+ if (alias) {
74
+ fields = [`count(distinct ${alias}.${requestedFields[0]}) as count`];
75
+ }
76
+ else {
77
+ fields = [`count(distinct ${requestedFields[0]}) as count`];
78
+ }
79
+ }
61
80
  const row = await (0, ent_1.loadRow)({
81
+ ...this.options.loadEntOptions,
62
82
  tableName: this.options.loadEntOptions.tableName,
63
- // sqlite needs as count otherwise it returns count(1)
64
- fields: ["count(1) as count"],
83
+ fields,
65
84
  clause: this.clause,
66
85
  context: this.viewer.context,
86
+ join: this.options.joinBETA,
87
+ disableFieldsAlias: true,
67
88
  });
68
89
  return parseInt(row?.count, 10) || 0;
69
90
  }
@@ -86,12 +107,14 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
86
107
  options.limit = (0, ent_1.getDefaultLimit)();
87
108
  }
88
109
  const rows = await (0, ent_1.loadRows)({
89
- tableName: this.options.loadEntOptions.tableName,
90
- fields: this.options.loadEntOptions.fields,
110
+ ...this.options.loadEntOptions,
91
111
  clause: (0, clause_1.AndOptional)(this.clause, options.clause),
92
112
  orderby: options.orderby,
93
113
  limit: options?.limit || (0, ent_1.getDefaultLimit)(),
94
114
  context: this.viewer.context,
115
+ join: this.options.joinBETA,
116
+ // if doing a join, select distinct rows
117
+ distinct: this.options.joinBETA !== undefined,
95
118
  });
96
119
  this.edges.set(1, rows);
97
120
  }
@@ -101,5 +124,8 @@ class CustomClauseQuery extends query_1.BaseEdgeQuery {
101
124
  async loadEntsFromEdges(id, rows) {
102
125
  return (0, ent_1.applyPrivacyPolicyForRows)(this.viewer, rows, this.options.loadEntOptions);
103
126
  }
127
+ __getOptions() {
128
+ return this.options;
129
+ }
104
130
  }
105
131
  exports.CustomClauseQuery = CustomClauseQuery;
@@ -80,6 +80,7 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
80
80
  super(viewer, {
81
81
  cursorCol: uniqueCol,
82
82
  orderby,
83
+ fieldOptions: opts.loaderFactory.options,
83
84
  });
84
85
  this.viewer = viewer;
85
86
  this.options = options;
@@ -1,4 +1,4 @@
1
- import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data, PrivacyPolicy, EdgeQueryableDataOptionsConfigureLoader } from "../base";
1
+ import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data, PrivacyPolicy, EdgeQueryableDataOptionsConfigureLoader, QueryableDataOptions, SelectBaseDataOptions } 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,9 +31,12 @@ export interface PaginationInfo {
31
31
  startCursor: string;
32
32
  endCursor: string;
33
33
  }
34
- interface EdgeQueryOptions {
34
+ export interface EdgeQueryOptions {
35
35
  cursorCol: string;
36
+ cursorColIsDate?: boolean;
36
37
  orderby: OrderBy;
38
+ joinBETA?: NonNullable<QueryableDataOptions["join"]>;
39
+ fieldOptions?: SelectBaseDataOptions;
37
40
  }
38
41
  export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> implements EdgeQuery<TSource, TDest, TEdge> {
39
42
  viewer: Viewer;
@@ -45,10 +48,13 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
45
48
  protected genIDInfosToFetch: () => Promise<IDInfo[]>;
46
49
  private idMap;
47
50
  private idsToFetch;
48
- private sortCol;
49
- private cursorCol;
51
+ protected sortCol: string;
52
+ protected cursorCol: string;
53
+ protected cursorColIsDate: boolean;
54
+ protected sortColIsDate: boolean;
50
55
  private edgeQueryOptions;
51
56
  private limitAdded;
57
+ private cursorKeys;
52
58
  constructor(viewer: Viewer, sortCol: string, cursorCol: string);
53
59
  constructor(viewer: Viewer, options: EdgeQueryOptions);
54
60
  protected getSortCol(): string;
@@ -81,12 +87,12 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
81
87
  protected genIDInfosToFetchImpl(): Promise<IDInfo[]>;
82
88
  private _defaultEdgeQueryableOptions;
83
89
  protected configureEdgeQueryableDataOptions(opts: EdgeQueryableDataOptionsConfigureLoader): void;
84
- protected getDefaultEdgeQueryOptions(): Partial<Pick<import("../base").QueryableDataOptions, "clause" | "limit" | "orderby" | "disableTransformations">> | undefined;
90
+ protected getDefaultEdgeQueryOptions(): Partial<Pick<QueryableDataOptions, "clause" | "limit" | "orderby" | "disableTransformations">> | undefined;
85
91
  private loadEdges;
92
+ protected includeSortColInCursor(options: EdgeQueryOptions): boolean;
86
93
  getCursor(row: TEdge): string;
87
94
  }
88
95
  export interface IDInfo {
89
96
  id: ID;
90
97
  invalidated?: boolean;
91
98
  }
92
- export {};
@@ -40,36 +40,61 @@ function assertPositive(n) {
40
40
  throw new Error("cannot use a negative number");
41
41
  }
42
42
  }
43
- function assertValidCursor(cursor, col) {
44
- let decoded = Buffer.from(cursor, "base64").toString("ascii");
45
- let parts = decoded.split(":");
46
- // uuid, don't parse int since it tries to validate just first part
47
- if ((0, uuid_1.validate)(parts[1])) {
48
- return parts[1];
49
- }
50
- // invalid or unknown cursor. nothing to do here.
51
- if (parts.length !== 2 || parts[0] !== col) {
52
- throw new Error(`invalid cursor ${cursor} passed`);
53
- }
43
+ function convertToIntMaybe(val) {
54
44
  // TODO handle both cases... (time vs not) better
55
45
  // TODO change this to only do the parseInt part if time...
56
46
  // pass flag indicating if time?
57
- const time = parseInt(parts[1], 10);
47
+ // handle non-integers for which the first part is an int
48
+ // @ts-ignore
49
+ if (isNaN(val)) {
50
+ return val;
51
+ }
52
+ const time = parseInt(val, 10);
58
53
  if (isNaN(time)) {
59
- return parts[1];
54
+ return val;
60
55
  }
61
56
  return time;
62
57
  }
58
+ function assertValidCursor(cursor, opts) {
59
+ let decoded = Buffer.from(cursor, "base64").toString("ascii");
60
+ let parts = decoded.split(":");
61
+ const { keys } = opts;
62
+ // invalid or unknown cursor. nothing to do here.
63
+ // we should have the same number of parts as keys * 2
64
+ if (parts.length !== keys.length * 2) {
65
+ throw new Error(`invalid cursor ${cursor} passed`);
66
+ }
67
+ const values = [];
68
+ for (let i = 0; i < keys.length; i++) {
69
+ const key = keys[i];
70
+ const keyPart = parts[i * 2];
71
+ if (key !== keyPart) {
72
+ throw new Error(`invalid cursor ${cursor} passed. expected ${key}. got ${keyPart} as key of field`);
73
+ }
74
+ const val = parts[i * 2 + 1];
75
+ // uuid, don't parse int since it tries to validate just first part
76
+ if ((0, uuid_1.validate)(val)) {
77
+ values.push(val);
78
+ continue;
79
+ }
80
+ values.push(convertToIntMaybe(val));
81
+ }
82
+ return values;
83
+ }
63
84
  const orderbyRegex = new RegExp(/([0-9a-z_]+)[ ]?([0-9a-z_]+)?/i);
64
85
  class FirstFilter {
65
86
  constructor(options) {
66
87
  this.options = options;
67
88
  this.pageMap = new Map();
68
89
  this.usedQuery = false;
90
+ this.cursorValues = [];
69
91
  assertPositive(options.limit);
70
92
  this.sortCol = options.sortCol;
71
93
  if (options.after) {
72
- this.offset = assertValidCursor(options.after, options.cursorCol);
94
+ this.cursorValues = assertValidCursor(options.after, {
95
+ keys: options.cursorKeys,
96
+ });
97
+ this.offset = this.cursorValues[0];
73
98
  }
74
99
  this.edgeQuery = options.query;
75
100
  }
@@ -133,6 +158,18 @@ class FirstFilter {
133
158
  // so we'd need a way to indicate whether this is done in sql or not
134
159
  return edges;
135
160
  }
161
+ getOffsetForQuery() {
162
+ // cursorCol maps to offset which we get from the cursor in assertValidCursor
163
+ return this.options.cursorColIsDate
164
+ ? new Date(this.offset).toISOString()
165
+ : this.offset;
166
+ }
167
+ getSortValueForQuery() {
168
+ // sortCol maps to the value we're comparing against
169
+ return this.options.sortColIsDate
170
+ ? new Date(this.cursorValues[1]).toISOString()
171
+ : this.cursorValues[1];
172
+ }
136
173
  async query(options) {
137
174
  this.usedQuery = true;
138
175
  // we fetch an extra one to see if we're at the end
@@ -150,17 +187,25 @@ class FirstFilter {
150
187
  });
151
188
  if (this.offset) {
152
189
  const res = this.edgeQuery.getTableName();
153
- const tableName = (0, types_1.isPromise)(res) ? await res : res;
154
- // inner col time
155
- options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, less ? "<" : ">", tableName, this.options.cursorCol, this.offset));
190
+ let tableName = (0, types_1.isPromise)(res) ? await res : res;
191
+ // using a join, we already know sortCol and cursorCol are different
192
+ // we have encoded both values in the cursor
193
+ // includeSortColInCursor() is true in this case
194
+ if (this.cursorValues.length === 2 &&
195
+ this.options.cursorKeys.length === 2) {
196
+ options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsQuery(this.sortCol, this.options.cursorCol, less, this.getSortValueForQuery(), this.getOffsetForQuery(), this.options.fieldOptions?.fieldsAlias ??
197
+ this.options.fieldOptions?.alias));
198
+ }
199
+ else {
200
+ // inner col time
201
+ options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, less ? "<" : ">", tableName, this.options.cursorCol, this.getOffsetForQuery()));
202
+ }
156
203
  }
157
204
  }
158
205
  else {
159
206
  if (this.offset) {
160
- let clauseFn = less ? clause.Less : clause.Greater;
161
- let val = this.options.sortColTime
162
- ? new Date(this.offset).toISOString()
163
- : this.offset;
207
+ const clauseFn = less ? clause.Less : clause.Greater;
208
+ const val = this.getOffsetForQuery();
164
209
  options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
165
210
  }
166
211
  }
@@ -172,14 +217,20 @@ class FirstFilter {
172
217
  return this.pageMap.get(id);
173
218
  }
174
219
  }
220
+ // TODO LastFilter same behavior as FirstFilter
221
+ // TODO can we share so we don't keep needing to change in both
175
222
  class LastFilter {
176
223
  constructor(options) {
177
224
  this.options = options;
178
225
  this.pageMap = new Map();
226
+ this.cursorValues = [];
179
227
  assertPositive(options.limit);
180
228
  this.sortCol = options.sortCol;
181
229
  if (options.before) {
182
- this.offset = assertValidCursor(options.before, options.cursorCol);
230
+ this.cursorValues = assertValidCursor(options.before, {
231
+ keys: options.cursorKeys,
232
+ });
233
+ this.offset = this.cursorValues[0];
183
234
  }
184
235
  this.edgeQuery = options.query;
185
236
  }
@@ -210,6 +261,19 @@ class LastFilter {
210
261
  }
211
262
  return ret;
212
263
  }
264
+ // copied from FirstFilter
265
+ getOffsetForQuery() {
266
+ // cursorCol maps to offset which we get from the cursor in assertValidCursor
267
+ return this.options.cursorColIsDate
268
+ ? new Date(this.offset).toISOString()
269
+ : this.offset;
270
+ }
271
+ getSortValueForQuery() {
272
+ // sortCol maps to the value we're comparing against
273
+ return this.options.sortColIsDate
274
+ ? new Date(this.cursorValues[1]).toISOString()
275
+ : this.cursorValues[1];
276
+ }
213
277
  async query(options) {
214
278
  const orderby = (0, query_impl_1.reverseOrderBy)(this.options.orderby);
215
279
  const greater = orderby[0].direction === "ASC";
@@ -218,8 +282,20 @@ class LastFilter {
218
282
  const res = this.edgeQuery.getTableName();
219
283
  const tableName = (0, types_1.isPromise)(res) ? await res : res;
220
284
  if (this.offset) {
221
- // inner col time
222
- options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.offset));
285
+ // using a join, we already know sortCol and cursorCol are different
286
+ // we have encoded both values in the cursor
287
+ // includeSortColInCursor() is true in this case
288
+ if (this.cursorValues.length === 2 &&
289
+ this.options.cursorKeys.length === 2) {
290
+ options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsQuery(this.sortCol, this.options.cursorCol,
291
+ // flipped here since we're going in the opposite direction
292
+ !greater, this.getSortValueForQuery(), this.getOffsetForQuery(), this.options.fieldOptions?.fieldsAlias ??
293
+ this.options.fieldOptions?.alias));
294
+ }
295
+ else {
296
+ // inner col time
297
+ options.clause = clause.AndOptional(options.clause, clause.PaginationMultipleColsSubQuery(this.sortCol, greater ? ">" : "<", tableName, this.options.cursorCol, this.getOffsetForQuery()));
298
+ }
223
299
  }
224
300
  // we also sort cursor col in same direction. (direction doesn't matter)
225
301
  orderby.push({
@@ -229,10 +305,8 @@ class LastFilter {
229
305
  }
230
306
  else {
231
307
  if (this.offset) {
232
- let clauseFn = greater ? clause.Greater : clause.Less;
233
- let val = this.options.sortColTime
234
- ? new Date(this.offset).toISOString()
235
- : this.offset;
308
+ const clauseFn = greater ? clause.Greater : clause.Less;
309
+ const val = this.getOffsetForQuery();
236
310
  options.clause = clause.AndOptional(options.clause, clauseFn(this.sortCol, val));
237
311
  }
238
312
  }
@@ -252,6 +326,7 @@ class BaseEdgeQuery {
252
326
  this.idMap = new Map();
253
327
  this.idsToFetch = [];
254
328
  this.limitAdded = false;
329
+ this.cursorKeys = [];
255
330
  // this is basically just raw rows
256
331
  this.queryEdges = async () => {
257
332
  return this.querySingleEdge("queryEdges");
@@ -304,6 +379,8 @@ class BaseEdgeQuery {
304
379
  };
305
380
  let sortCol;
306
381
  let cursorCol;
382
+ let sortColIsDate = false;
383
+ let cursorColIsDate = false;
307
384
  if (typeof sortColOrOptions === "string") {
308
385
  sortCol = sortColOrOptions;
309
386
  cursorCol = cursorColMaybe;
@@ -324,11 +401,15 @@ class BaseEdgeQuery {
324
401
  else {
325
402
  // TODO this orderby isn't consistent and this logic needs to be changed anywhere that's using this and this.getSortCol()
326
403
  sortCol = sortColOrOptions.orderby[0].column;
404
+ sortColIsDate = sortColOrOptions.orderby[0].dateColumn ?? false;
327
405
  }
328
406
  cursorCol = sortColOrOptions.cursorCol;
407
+ cursorColIsDate = sortColOrOptions.cursorColIsDate ?? false;
329
408
  this.edgeQueryOptions = sortColOrOptions;
330
409
  }
331
410
  this.sortCol = sortCol;
411
+ this.sortColIsDate = sortColIsDate;
412
+ this.cursorColIsDate = cursorColIsDate;
332
413
  let m = orderbyRegex.exec(sortCol);
333
414
  if (!m) {
334
415
  throw new Error(`invalid sort column ${sortCol}`);
@@ -340,6 +421,10 @@ class BaseEdgeQuery {
340
421
  this.cursorCol = cursorCol;
341
422
  this.memoizedloadEdges = (0, memoizee_1.default)(this.loadEdges.bind(this));
342
423
  this.genIDInfosToFetch = (0, memoizee_1.default)(this.genIDInfosToFetchImpl.bind(this));
424
+ this.cursorKeys.push(this.cursorCol);
425
+ if (this.includeSortColInCursor(this.edgeQueryOptions)) {
426
+ this.cursorKeys.push(this.sortCol);
427
+ }
343
428
  }
344
429
  getSortCol() {
345
430
  return this.sortCol;
@@ -356,8 +441,12 @@ class BaseEdgeQuery {
356
441
  after,
357
442
  sortCol: this.sortCol,
358
443
  cursorCol: this.cursorCol,
444
+ cursorKeys: this.cursorKeys,
445
+ cursorColIsDate: this.cursorColIsDate,
446
+ sortColIsDate: this.sortColIsDate,
359
447
  orderby: this.edgeQueryOptions.orderby,
360
448
  query: this,
449
+ fieldOptions: this.edgeQueryOptions.fieldOptions,
361
450
  }));
362
451
  return this;
363
452
  }
@@ -379,8 +468,12 @@ class BaseEdgeQuery {
379
468
  before,
380
469
  sortCol: this.sortCol,
381
470
  cursorCol: this.cursorCol,
471
+ cursorKeys: this.cursorKeys,
472
+ cursorColIsDate: this.cursorColIsDate,
473
+ sortColIsDate: this.sortColIsDate,
382
474
  orderby: this.edgeQueryOptions.orderby,
383
475
  query: this,
476
+ fieldOptions: this.edgeQueryOptions.fieldOptions,
384
477
  }));
385
478
  return this;
386
479
  }
@@ -490,10 +583,13 @@ class BaseEdgeQuery {
490
583
  this.queryDispatched = true;
491
584
  return this.edges;
492
585
  }
586
+ includeSortColInCursor(options) {
587
+ return false;
588
+ }
493
589
  getCursor(row) {
494
590
  return (0, ent_1.getCursor)({
495
591
  row,
496
- col: this.cursorCol,
592
+ keys: this.cursorKeys,
497
593
  });
498
594
  }
499
595
  }
@@ -205,11 +205,24 @@ const commonTests = (opts) => {
205
205
  function getViewer() {
206
206
  return new viewer_1.LoggedOutViewer();
207
207
  }
208
- function getCursorFrom(contacts, idx) {
209
- return (0, ent_1.getCursor)({
210
- row: contacts[idx],
211
- col: "id",
212
- });
208
+ function getCursorFrom(q, contacts, idx) {
209
+ let opts;
210
+ if (isCustomQuery(q)) {
211
+ opts = {
212
+ row: contacts[idx],
213
+ keys: ["id"],
214
+ };
215
+ }
216
+ else {
217
+ // for assoc queries, we're getting the value from 'id' field but the edge
218
+ // is from assoc_edge table id2 field and so cursor takes it from there
219
+ opts = {
220
+ row: contacts[idx],
221
+ keys: ["id2"],
222
+ cursorKeys: ["id"],
223
+ };
224
+ }
225
+ return (0, ent_1.getCursor)(opts);
213
226
  }
214
227
  function getVerifyAfterEachCursor(edges, pageLength, user) {
215
228
  return (0, query_1.getVerifyAfterEachCursorGeneric)(edges, pageLength, user,
@@ -550,7 +563,7 @@ const commonTests = (opts) => {
550
563
  const idx = 2;
551
564
  const N = 3;
552
565
  const filter = new TestQueryFilter((q, user, contacts) => {
553
- return q.first(N, getCursorFrom(contacts, idx));
566
+ return q.first(N, getCursorFrom(q, contacts, idx));
554
567
  }, opts.newQuery, (contacts) => {
555
568
  if (opts.orderby[0].direction === "DESC") {
556
569
  // < check so we shouldn't get that index
@@ -611,7 +624,7 @@ const commonTests = (opts) => {
611
624
  const idx = 2;
612
625
  const N = 3;
613
626
  const filter = new TestQueryFilter((q, user, contacts) => {
614
- return q.last(N, getCursorFrom(contacts, idx));
627
+ return q.last(N, getCursorFrom(q, contacts, idx));
615
628
  }, opts.newQuery, (contacts) => {
616
629
  // > check so we don't want that index
617
630
  if (opts.orderby[0].direction === "DESC") {
@@ -3,9 +3,15 @@ export interface OrderByOption {
3
3
  column: string;
4
4
  direction: "ASC" | "DESC";
5
5
  nullsPlacement?: "first" | "last";
6
+ dateColumn?: boolean;
6
7
  }
7
8
  export type OrderBy = OrderByOption[];
8
9
  export declare function getOrderByPhrase(orderby: OrderBy, alias?: string): string;
9
10
  export declare function reverseOrderBy(orderby: OrderBy): OrderBy;
10
- export declare function getJoinPhrase(join: NonNullable<QueryableDataOptions["join"]>, clauseIdx?: number): string;
11
+ interface JoinInfo {
12
+ phrase: string;
13
+ valuesUsed: number;
14
+ }
15
+ export declare function getJoinInfo(join: NonNullable<QueryableDataOptions["join"]>, clauseIdx?: number): JoinInfo;
11
16
  export declare function buildQuery(options: QueryableDataOptions): string;
17
+ export {};