Merge branch 'aggregation' into insights

This commit is contained in:
rijkvanzanten
2021-06-17 16:06:34 -04:00
54 changed files with 1507 additions and 934 deletions

View File

@@ -0,0 +1,42 @@
import { Knex } from 'knex';
import { HelperFn } from '../types';
export class HelperMSSQL implements HelperFn {
private knex: Knex;
constructor(knex: Knex) {
this.knex = knex;
}
year(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(year, ??.??)', [table, column]);
}
month(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(month, ??.??)', [table, column]);
}
week(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(week, ??.??)', [table, column]);
}
day(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(day, ??.??)', [table, column]);
}
weekday(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(weekday, ??.??)', [table, column]);
}
hour(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(hour, ??.??)', [table, column]);
}
minute(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(minute, ??.??)', [table, column]);
}
second(table: string, column: string): Knex.Raw {
return this.knex.raw('DATEPART(second, ??.??)', [table, column]);
}
}

View File

@@ -0,0 +1,42 @@
import { Knex } from 'knex';
import { HelperFn } from '../types';
export class HelperMySQL implements HelperFn {
private knex: Knex;
constructor(knex: Knex) {
this.knex = knex;
}
year(table: string, column: string): Knex.Raw {
return this.knex.raw('YEAR(??.??)', [table, column]);
}
month(table: string, column: string): Knex.Raw {
return this.knex.raw('MONTH(??.??)', [table, column]);
}
week(table: string, column: string): Knex.Raw {
return this.knex.raw('WEEK(??.??)', [table, column]);
}
day(table: string, column: string): Knex.Raw {
return this.knex.raw('DAYOFMONTH(??.??)', [table, column]);
}
weekday(table: string, column: string): Knex.Raw {
return this.knex.raw('DAYOFWEEK??.??)', [table, column]);
}
hour(table: string, column: string): Knex.Raw {
return this.knex.raw('HOUR(??.??)', [table, column]);
}
minute(table: string, column: string): Knex.Raw {
return this.knex.raw('MINUTE(??.??)', [table, column]);
}
second(table: string, column: string): Knex.Raw {
return this.knex.raw('SECOND(??.??)', [table, column]);
}
}

View File

@@ -0,0 +1,42 @@
import { Knex } from 'knex';
import { HelperFn } from '../types';
export class HelperOracle implements HelperFn {
private knex: Knex;
constructor(knex: Knex) {
this.knex = knex;
}
year(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'IYYY')", [table, column]);
}
month(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'MM')", [table, column]);
}
week(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'IW')", [table, column]);
}
day(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'DD')", [table, column]);
}
weekday(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'D')", [table, column]);
}
hour(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'HH24')", [table, column]);
}
minute(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'MI')", [table, column]);
}
second(table: string, column: string): Knex.Raw {
return this.knex.raw("TO_CHAR(??.??, 'SS')", [table, column]);
}
}

View File

@@ -0,0 +1,42 @@
import { Knex } from 'knex';
import { HelperFn } from '../types';
export class HelperPostgres implements HelperFn {
private knex: Knex;
constructor(knex: Knex) {
this.knex = knex;
}
year(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(YEAR FROM ??.??)', [table, column]);
}
month(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(MONTH FROM ??.??)', [table, column]);
}
week(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(WEEK FROM ??.??)', [table, column]);
}
day(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(DAY FROM ??.??)', [table, column]);
}
weekday(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(DOW FROM ??.??)', [table, column]);
}
hour(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(HOUR FROM ??.??)', [table, column]);
}
minute(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(MINUTE FROM ??.??)', [table, column]);
}
second(table: string, column: string): Knex.Raw {
return this.knex.raw('EXTRACT(SECOND FROM ??.??)', [table, column]);
}
}

View File

@@ -0,0 +1,42 @@
import { Knex } from 'knex';
import { HelperFn } from '../types';
export class HelperSQLite implements HelperFn {
private knex: Knex;
constructor(knex: Knex) {
this.knex = knex;
}
year(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%Y', ??.??)", [table, column]);
}
month(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%m', ??.??)", [table, column]);
}
week(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%W', ??.??)", [table, column]);
}
day(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%d', ??.??)", [table, column]);
}
weekday(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%w', ??.??)", [table, column]);
}
hour(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%H', ??.??)", [table, column]);
}
minute(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%M', ??.??)", [table, column]);
}
second(table: string, column: string): Knex.Raw {
return this.knex.raw("strftime('%S', ??.??)", [table, column]);
}
}

View File

@@ -0,0 +1,25 @@
import { Knex } from 'knex';
import { HelperPostgres } from './dialects/postgres';
import { HelperMySQL } from './dialects/mysql';
import { HelperMSSQL } from './dialects/mssql';
import { HelperSQLite } from './dialects/sqlite';
import { HelperOracle } from './dialects/oracle';
export function FunctionsHelper(knex: Knex) {
switch (knex.client.constructor.name) {
case 'Client_MySQL':
return new HelperMySQL(knex);
case 'Client_PG':
return new HelperPostgres(knex);
case 'Client_SQLite3':
return new HelperSQLite(knex);
case 'Client_Oracledb':
case 'Client_Oracle':
return new HelperOracle(knex);
case 'Client_MSSQL':
return new HelperMSSQL(knex);
default:
throw Error('Unsupported driver used: ' + knex.client.constructor.name);
}
}

View File

@@ -0,0 +1,12 @@
import { Knex } from 'knex';
export interface HelperFn {
year(table: string, column: string): Knex.Raw;
month(table: string, column: string): Knex.Raw;
week(table: string, column: string): Knex.Raw;
day(table: string, column: string): Knex.Raw;
weekday(table: string, column: string): Knex.Raw;
hour(table: string, column: string): Knex.Raw;
minute(table: string, column: string): Knex.Raw;
second(table: string, column: string): Knex.Raw;
}

View File

@@ -83,9 +83,6 @@ export async function up(knex: Knex): Promise<void> {
}
}
// Can't reliably have circular cascade
const action = constraint.many_collection === constraint.one_collection ? 'NO ACTION' : 'SET NULL';
// MySQL doesn't accept FKs from `int` to `int unsigned`. `knex` defaults `.increments()`
// to `unsigned`, but defaults `.integer()` to `int`. This means that created m2o fields
// have the wrong type. This step will force the m2o `int` field into `unsigned`, but only
@@ -104,12 +101,15 @@ export async function up(knex: Knex): Promise<void> {
}
const indexName = getDefaultIndexName('foreign', constraint.many_collection, constraint.many_field);
table
const builder = table
.foreign(constraint.many_field, indexName)
.references(relatedPrimaryKeyField)
.inTable(constraint.one_collection!)
.onDelete(action);
.inTable(constraint.one_collection!);
// Can't reliably have circular cascade
if (constraint.many_collection !== constraint.one_collection) {
builder.onDelete('SET NULL');
}
});
} catch (err) {
logger.warn(

View File

@@ -3,7 +3,10 @@ import { clone, cloneDeep, pick, uniq } from 'lodash';
import { PayloadService } from '../services/payload';
import { Item, Query, SchemaOverview } from '../types';
import { AST, FieldNode, NestedCollectionNode } from '../types/ast';
import { applyFunctionToColumnName } from '../utils/apply-function-to-column-name';
import applyQuery from '../utils/apply-query';
import { getColumn } from '../utils/get-column';
import { stripFunction } from '../utils/strip-function';
import { toArray } from '../utils/to-array';
import getDatabase from './index';
@@ -113,8 +116,9 @@ async function parseCurrentLevel(
for (const child of children) {
if (child.type === 'field') {
if (columnsInCollection.includes(child.name) || child.name === '*') {
columnsToSelectInternal.push(child.name);
const fieldKey = stripFunction(child.name);
if (columnsInCollection.includes(fieldKey) || fieldKey === '*') {
columnsToSelectInternal.push(child.name); // maintain original name here (includes functions)
}
continue;
@@ -157,7 +161,7 @@ function getDBQuery(
query: Query,
nested?: boolean
): Knex.QueryBuilder {
const dbQuery = knex.select(columns.map((column) => `${table}.${column}`)).from(table);
const dbQuery = knex.select(columns.map((column) => getColumn(knex, table, column))).from(table);
const queryCopy = clone(query);
@@ -170,7 +174,7 @@ function getDBQuery(
delete queryCopy.limit;
}
applyQuery(table, dbQuery, queryCopy, schema);
applyQuery(knex, table, dbQuery, queryCopy, schema);
return dbQuery;
}
@@ -391,14 +395,14 @@ function removeTemporaryFields(
}
}
// Make sure any new aliased aggregate fields are included
// Make sure any requested aggregate fields are included
if (ast.query?.aggregate) {
for (const [_operation, aliasMap] of Object.entries(ast.query.aggregate)) {
if (!aliasMap) continue;
for (const [operation, aggregateFields] of Object.entries(ast.query.aggregate)) {
if (!fields) continue;
for (const [_column, alias] of Object.entries(aliasMap)) {
fields.push(alias);
}
if (operation === 'count' && aggregateFields.includes('*')) fields.push('count');
fields.push(...aggregateFields.map((field) => `${field}_${operation}`));
}
}
@@ -419,7 +423,9 @@ function removeTemporaryFields(
);
}
item = fields.length > 0 ? pick(rawItem, fields) : rawItem[primaryKeyField];
const fieldsWithFunctionsApplied = fields.map((field) => applyFunctionToColumnName(field));
item = fields.length > 0 ? pick(rawItem, fieldsWithFunctionsApplied) : rawItem[primaryKeyField];
items.push(item);
}