Initial work towards aliasing queries for SQL.

This commit is contained in:
mike12345567 2023-11-24 18:11:53 +00:00
parent df65303645
commit 478e297e9e
2 changed files with 102 additions and 0 deletions

View File

@ -0,0 +1,101 @@
import { QueryJson, SearchFilters, Table, Row } from "@budibase/types"
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
import { cloneDeep } from "lodash"
class AliasTables {
character: string
aliases: Record<string, string>
tableAliases: Record<string, string>
constructor() {
this.character = "a"
this.aliases = {}
this.tableAliases = {}
}
getAlias(tableName: string) {
if (this.aliases[tableName]) {
return this.aliases[tableName]
}
this.character = String.fromCharCode(this.character.charCodeAt(0) + 1)
this.aliases[tableName] = this.character
this.tableAliases[this.character] = tableName
return this.character
}
aliasField(tableNames: string[], field: string) {
if (field.includes(".")) {
const [tableName, column] = field.split(".")
if (tableNames.includes(tableName)) {
return `${this.getAlias(tableName)}.${column}`
}
}
return field
}
reverse<T extends Row | Row[]>(tableNames: string[], rows: T): T {
const process = (row: Row) => {
const final: Row = {}
for (let [key, value] of Object.entries(row)) {
if (!key.includes(".")) {
final[key] = value
} else {
const [alias, column] = key.split(".")
const tableName = this.tableAliases[alias] || alias
final[`${tableName}.${column}`] = value
}
}
return final
}
if (Array.isArray(rows)) {
return rows.map(row => process(row)) as T
} else {
return process(rows) as T
}
}
async queryWithAliasing(tableNames: string[], json: QueryJson) {
json = cloneDeep(json)
const aliasField = (field: string) => this.aliasField(tableNames, field)
const aliasTable = (table: Table) => ({
...table,
name: this.getAlias(table.name),
})
// run through the query json to update anywhere a table may be used
if (json.resource?.fields) {
json.resource.fields = json.resource.fields.map(field =>
aliasField(field)
)
}
if (json.filters) {
for (let [filterKey, filter] of Object.entries(json.filters)) {
if (typeof filter !== "object") {
continue
}
const aliasedFilters: typeof filter = {}
for (let key of Object.keys(filter)) {
aliasedFilters[aliasField(key)] = filter
}
json.filters[filterKey as keyof SearchFilters] = aliasedFilters
}
}
if (json.relationships) {
json.relationships = json.relationships.map(relationship => ({
...relationship,
tableName: this.getAlias(relationship.tableName),
}))
}
if (json.meta?.table) {
json.meta.table = aliasTable(json.meta.table)
}
if (json.meta?.tables) {
const aliasedTables: Record<string, Table> = {}
for (let [tableName, table] of Object.entries(json.meta.tables)) {
aliasedTables[this.getAlias(tableName)] = aliasTable(table)
}
json.meta.tables = aliasedTables
}
const response = await getDatasourceAndQuery(json)
return this.reverse(tableNames, response)
}
}

View File

@ -437,6 +437,7 @@ class InternalBuilder {
read(knex: Knex, json: QueryJson, limit: number): KnexQuery { read(knex: Knex, json: QueryJson, limit: number): KnexQuery {
let { endpoint, resource, filters, paginate, relationships } = json let { endpoint, resource, filters, paginate, relationships } = json
const tableName = endpoint.entityId const tableName = endpoint.entityId
// select all if not specified // select all if not specified
if (!resource) { if (!resource) {