Add support for aliasing fields (#7419)

* Don't double split csv values

* Still join them on create tho

* Add support for `alias` query param

* Support aliases in wildcards
This commit is contained in:
Rijk van Zanten
2021-08-14 20:33:55 +02:00
committed by GitHub
parent 7601178ea3
commit d302c6c263
8 changed files with 188 additions and 63 deletions

View File

@@ -2,7 +2,7 @@ import { Knex } from 'knex';
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 { AST, FieldNode, NestedCollectionNode, M2ONode } from '../types/ast';
import { applyFunctionToColumnName } from '../utils/apply-function-to-column-name';
import applyQuery from '../utils/apply-query';
import { getColumn } from '../utils/get-column';
@@ -60,14 +60,15 @@ export default async function runAST(
async function run(collection: string, children: (NestedCollectionNode | FieldNode)[], query: Query) {
// Retrieve the database columns to select in the current AST
const { columnsToSelect, primaryKeyField, nestedCollectionNodes } = await parseCurrentLevel(
const { fieldNodes, primaryKeyField, nestedCollectionNodes } = await parseCurrentLevel(
schema,
collection,
children
children,
query
);
// The actual knex query builder instance. This is a promise that resolves with the raw items from the db
const dbQuery = await getDBQuery(schema, knex, collection, columnsToSelect, query, options?.nested);
const dbQuery = await getDBQuery(schema, knex, collection, fieldNodes, query, options?.nested);
const rawItems: Item | Item[] = await dbQuery;
@@ -106,7 +107,8 @@ export default async function runAST(
async function parseCurrentLevel(
schema: SchemaOverview,
collection: string,
children: (NestedCollectionNode | FieldNode)[]
children: (NestedCollectionNode | FieldNode)[],
query: Query
) {
const primaryKeyField = schema.collections[collection].primary;
const columnsInCollection = Object.keys(schema.collections[collection].fields);
@@ -117,8 +119,17 @@ async function parseCurrentLevel(
for (const child of children) {
if (child.type === 'field') {
const fieldKey = stripFunction(child.name);
if (columnsInCollection.includes(fieldKey) || fieldKey === '*') {
columnsToSelectInternal.push(child.name); // maintain original name here (includes functions)
if (query.alias) {
columnsToSelectInternal.push(
...Object.entries(query.alias)
.filter(([_key, value]) => value === child.name)
.map(([key]) => key)
);
}
}
continue;
@@ -127,7 +138,7 @@ async function parseCurrentLevel(
if (!child.relation) continue;
if (child.type === 'm2o') {
columnsToSelectInternal.push(child.relation.field);
columnsToSelectInternal.push(child.fieldKey);
}
if (child.type === 'm2a') {
@@ -138,30 +149,49 @@ async function parseCurrentLevel(
nestedCollectionNodes.push(child);
}
/**
* Always fetch primary key in case there's a nested relation that needs it
const isAggregate = (query.aggregate && Object.keys(query.aggregate).length > 0) ?? false;
/** Always fetch primary key in case there's a nested relation that needs it. Aggregate payloads
* can't have nested relational fields
*/
const childrenContainRelational = children.some((child) => child.type !== 'field');
if (childrenContainRelational && columnsToSelectInternal.includes(primaryKeyField) === false) {
if (isAggregate === false && columnsToSelectInternal.includes(primaryKeyField) === false) {
columnsToSelectInternal.push(primaryKeyField);
}
/** Make sure select list has unique values */
const columnsToSelect = [...new Set(columnsToSelectInternal)];
return { columnsToSelect, nestedCollectionNodes, primaryKeyField };
const fieldNodes = columnsToSelect.map(
(column: string) =>
children.find((childNode) => childNode.fieldKey === column) ?? { type: 'field', name: column, fieldKey: column }
) as FieldNode[];
return { fieldNodes, nestedCollectionNodes, primaryKeyField };
}
function getColumnPreprocessor(knex: Knex, schema: SchemaOverview, table: string) {
const helper = getGeometryHelper();
return function (column: string): Knex.Raw<string> {
const field = schema.collections[table].fields[column];
if (isNativeGeometry(field)) {
return helper.asText(table, column);
return function (fieldNode: FieldNode | M2ONode): Knex.Raw<string> {
let field;
if (fieldNode.type === 'field') {
field = schema.collections[table].fields[fieldNode.name];
} else {
field = schema.collections[fieldNode.relation.collection].fields[fieldNode.relation.field];
}
return getColumn(knex, table, column);
let alias = undefined;
if (fieldNode.name !== fieldNode.fieldKey) {
alias = fieldNode.fieldKey;
}
if (isNativeGeometry(field)) {
return helper.asText(table, field.field);
}
return getColumn(knex, table, field.field, alias);
};
}
@@ -169,12 +199,12 @@ function getDBQuery(
schema: SchemaOverview,
knex: Knex,
table: string,
columns: string[],
fieldNodes: FieldNode[],
query: Query,
nested?: boolean
): Knex.QueryBuilder {
const preProcess = getColumnPreprocessor(knex, schema, table);
const dbQuery = knex.select(columns.map(preProcess)).from(table);
const dbQuery = knex.select(fieldNodes.map(preProcess)).from(table);
const queryCopy = clone(query);
queryCopy.limit = typeof queryCopy.limit === 'number' ? queryCopy.limit : 100;
@@ -217,11 +247,19 @@ function applyParentFilters(
});
if (relatedM2OisFetched === false) {
nestedNode.children.push({ type: 'field', name: nestedNode.relation.field });
nestedNode.children.push({
type: 'field',
name: nestedNode.relation.field,
fieldKey: nestedNode.relation.field,
});
}
if (nestedNode.relation.meta?.sort_field) {
nestedNode.children.push({ type: 'field', name: nestedNode.relation.meta.sort_field });
nestedNode.children.push({
type: 'field',
name: nestedNode.relation.meta.sort_field,
fieldKey: nestedNode.relation.meta.sort_field,
});
}
nestedNode.query = {
@@ -399,10 +437,9 @@ function removeTemporaryFields(
const nestedCollectionNodes: NestedCollectionNode[] = [];
for (const child of ast.children) {
if (child.type === 'field') {
fields.push(child.name);
} else {
fields.push(child.fieldKey);
fields.push(child.fieldKey);
if (child.type !== 'field') {
nestedCollectionNodes.push(child);
}
}
@@ -414,7 +451,7 @@ function removeTemporaryFields(
if (operation === 'count' && aggregateFields.includes('*')) fields.push('count');
fields.push(...aggregateFields.map((field) => `${field}_${operation}`));
fields.push(...aggregateFields.map((field) => `${operation}.${field}`));
}
}