Updating how aliasing is handled.
This commit is contained in:
parent
09a0d00aa7
commit
bb0b776684
|
@ -113,7 +113,7 @@ export default class AliasTables {
|
|||
}
|
||||
json.meta.tables = aliasedTables
|
||||
}
|
||||
json.endpoint.alias = this.getAlias(json.endpoint.entityId)
|
||||
json.tableAliases = this.tableAliases
|
||||
const response = await getDatasourceAndQuery(json)
|
||||
return this.reverse(response)
|
||||
}
|
||||
|
|
|
@ -129,8 +129,13 @@ class InternalBuilder {
|
|||
addFilters(
|
||||
query: KnexQuery,
|
||||
filters: SearchFilters | undefined,
|
||||
opts: { relationship?: boolean; tableName?: string }
|
||||
tableName: string,
|
||||
opts: { aliases?: Record<string, string>; relationship?: boolean }
|
||||
): KnexQuery {
|
||||
function getTableName(name: string) {
|
||||
const alias = opts.aliases?.[name]
|
||||
return alias || name
|
||||
}
|
||||
function iterate(
|
||||
structure: { [key: string]: any },
|
||||
fn: (key: string, value: any) => void
|
||||
|
@ -139,10 +144,11 @@ class InternalBuilder {
|
|||
const updatedKey = dbCore.removeKeyNumbering(key)
|
||||
const isRelationshipField = updatedKey.includes(".")
|
||||
if (!opts.relationship && !isRelationshipField) {
|
||||
fn(`${opts.tableName}.${updatedKey}`, value)
|
||||
fn(`${getTableName(tableName)}.${updatedKey}`, value)
|
||||
}
|
||||
if (opts.relationship && isRelationshipField) {
|
||||
fn(updatedKey, value)
|
||||
const [filterTableName, property] = updatedKey.split(".")
|
||||
fn(`${getTableName(filterTableName)}.${property}`, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,17 +351,15 @@ class InternalBuilder {
|
|||
query: KnexQuery,
|
||||
fromTable: string,
|
||||
relationships: RelationshipsJson[] | undefined,
|
||||
schema: string | undefined
|
||||
schema: string | undefined,
|
||||
aliases?: Record<string, string>
|
||||
): KnexQuery {
|
||||
if (!relationships) {
|
||||
return query
|
||||
}
|
||||
const tableSets: Record<string, [RelationshipsJson]> = {}
|
||||
// add up all aliases
|
||||
let aliases: Record<string, string> = {}
|
||||
// aggregate into table sets (all the same to tables)
|
||||
for (let relationship of relationships) {
|
||||
aliases = { ...aliases, ...relationship.aliases }
|
||||
const keyObj: { toTable: string; throughTable: string | undefined } = {
|
||||
toTable: relationship.tableName,
|
||||
throughTable: undefined,
|
||||
|
@ -372,9 +376,9 @@ class InternalBuilder {
|
|||
}
|
||||
for (let [key, relationships] of Object.entries(tableSets)) {
|
||||
const { toTable, throughTable } = JSON.parse(key)
|
||||
const toAlias = aliases[toTable] || toTable,
|
||||
throughAlias = aliases[throughTable] || throughTable,
|
||||
fromAlias = aliases[fromTable] || fromTable
|
||||
const toAlias = aliases?.[toTable] || toTable,
|
||||
throughAlias = aliases?.[throughTable] || throughTable,
|
||||
fromAlias = aliases?.[fromTable] || fromTable
|
||||
let toTableWithSchema = this.tableNameWithSchema(toTable, {
|
||||
alias: toAlias,
|
||||
schema,
|
||||
|
@ -423,22 +427,23 @@ class InternalBuilder {
|
|||
|
||||
knexWithAlias(
|
||||
knex: Knex,
|
||||
endpoint: { entityId: string; alias?: string; schema?: string }
|
||||
): { query: KnexQuery; aliased: string } {
|
||||
endpoint: QueryJson["endpoint"],
|
||||
aliases?: QueryJson["tableAliases"]
|
||||
): KnexQuery {
|
||||
const tableName = endpoint.entityId
|
||||
const alias = endpoint.alias
|
||||
const aliased = alias ? alias : tableName
|
||||
const tableAliased = alias ? `${tableName} as ${alias}` : tableName
|
||||
const tableAliased = aliases?.[tableName]
|
||||
? `${tableName} as ${aliases?.[tableName]}`
|
||||
: tableName
|
||||
let query = knex(tableAliased)
|
||||
if (endpoint.schema) {
|
||||
query = query.withSchema(endpoint.schema)
|
||||
}
|
||||
return { query, aliased }
|
||||
return query
|
||||
}
|
||||
|
||||
create(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery {
|
||||
const { endpoint, body } = json
|
||||
let { query } = this.knexWithAlias(knex, endpoint)
|
||||
let query = this.knexWithAlias(knex, endpoint)
|
||||
const parsedBody = parseBody(body)
|
||||
// make sure no null values in body for creation
|
||||
for (let [key, value] of Object.entries(parsedBody)) {
|
||||
|
@ -457,7 +462,7 @@ class InternalBuilder {
|
|||
|
||||
bulkCreate(knex: Knex, json: QueryJson): KnexQuery {
|
||||
const { endpoint, body } = json
|
||||
let { query } = this.knexWithAlias(knex, endpoint)
|
||||
let query = this.knexWithAlias(knex, endpoint)
|
||||
if (!Array.isArray(body)) {
|
||||
return query
|
||||
}
|
||||
|
@ -466,8 +471,10 @@ class InternalBuilder {
|
|||
}
|
||||
|
||||
read(knex: Knex, json: QueryJson, limit: number): KnexQuery {
|
||||
let { endpoint, resource, filters, paginate, relationships } = json
|
||||
let { endpoint, resource, filters, paginate, relationships, tableAliases } =
|
||||
json
|
||||
|
||||
const tableName = endpoint.entityId
|
||||
// select all if not specified
|
||||
if (!resource) {
|
||||
resource = { fields: [] }
|
||||
|
@ -493,19 +500,20 @@ class InternalBuilder {
|
|||
}
|
||||
// start building the query
|
||||
|
||||
let { query, aliased } = this.knexWithAlias(knex, endpoint)
|
||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||
query = query.limit(foundLimit)
|
||||
if (foundOffset) {
|
||||
query = query.offset(foundOffset)
|
||||
}
|
||||
query = this.addFilters(query, filters, { tableName: aliased })
|
||||
query = this.addFilters(query, filters, tableName, {
|
||||
aliases: tableAliases,
|
||||
})
|
||||
// add sorting to pre-query
|
||||
query = this.addSorting(query, json)
|
||||
// @ts-ignore
|
||||
let preQuery: KnexQuery = knex({
|
||||
// @ts-ignore
|
||||
[aliased]: query,
|
||||
}).select(selectStatement)
|
||||
const alias = tableAliases?.[tableName] || tableName
|
||||
let preQuery = knex({
|
||||
[alias]: query,
|
||||
} as any).select(selectStatement) as any
|
||||
// have to add after as well (this breaks MS-SQL)
|
||||
if (this.client !== SqlClient.MS_SQL) {
|
||||
preQuery = this.addSorting(preQuery, json)
|
||||
|
@ -513,18 +521,24 @@ class InternalBuilder {
|
|||
// handle joins
|
||||
query = this.addRelationships(
|
||||
preQuery,
|
||||
aliased,
|
||||
tableName,
|
||||
relationships,
|
||||
endpoint.schema
|
||||
endpoint.schema,
|
||||
tableAliases
|
||||
)
|
||||
return this.addFilters(query, filters, { relationship: true })
|
||||
return this.addFilters(query, filters, tableName, {
|
||||
relationship: true,
|
||||
aliases: tableAliases,
|
||||
})
|
||||
}
|
||||
|
||||
update(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery {
|
||||
const { endpoint, body, filters } = json
|
||||
let { query, aliased } = this.knexWithAlias(knex, endpoint)
|
||||
const { endpoint, body, filters, tableAliases } = json
|
||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||
const parsedBody = parseBody(body)
|
||||
query = this.addFilters(query, filters, { tableName: aliased })
|
||||
query = this.addFilters(query, filters, endpoint.entityId, {
|
||||
aliases: tableAliases,
|
||||
})
|
||||
// mysql can't use returning
|
||||
if (opts.disableReturning) {
|
||||
return query.update(parsedBody)
|
||||
|
@ -534,9 +548,11 @@ class InternalBuilder {
|
|||
}
|
||||
|
||||
delete(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery {
|
||||
const { endpoint, filters } = json
|
||||
let { query, aliased } = this.knexWithAlias(knex, endpoint)
|
||||
query = this.addFilters(query, filters, { tableName: aliased })
|
||||
const { endpoint, filters, tableAliases } = json
|
||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||
query = this.addFilters(query, filters, endpoint.entityId, {
|
||||
aliases: tableAliases,
|
||||
})
|
||||
// mysql can't use returning
|
||||
if (opts.disableReturning) {
|
||||
return query.delete()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const Sql = require("../base/sql").default
|
||||
const { SqlClient } = require("../utils")
|
||||
import { SqlClient } from "../utils"
|
||||
import Sql from "../base/sql"
|
||||
import { QueryJson } from "@budibase/types"
|
||||
import { join } from "path"
|
||||
|
||||
const TABLE_NAME = "test"
|
||||
|
||||
|
@ -17,7 +19,7 @@ function generateReadJson({
|
|||
filters,
|
||||
sort,
|
||||
paginate,
|
||||
}: any = {}) {
|
||||
}: any = {}): QueryJson {
|
||||
return {
|
||||
endpoint: endpoint(table || TABLE_NAME, "READ"),
|
||||
resource: {
|
||||
|
@ -30,7 +32,7 @@ function generateReadJson({
|
|||
table: {
|
||||
name: table || TABLE_NAME,
|
||||
primary: ["id"],
|
||||
},
|
||||
} as any,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -687,102 +689,12 @@ describe("SQL query builder", () => {
|
|||
describe("Captures of real examples", () => {
|
||||
const limit = 5000
|
||||
|
||||
function getJson(name: string): QueryJson {
|
||||
return require(join(__dirname, "sqlQueryJson", name)) as QueryJson
|
||||
}
|
||||
|
||||
it("should handle filtering by relationship", () => {
|
||||
const queryJson = {
|
||||
endpoint: {
|
||||
datasourceId: "datasource_plus_8066e56456784eb2a00129d31be5c3e7",
|
||||
entityId: "products",
|
||||
operation: "READ",
|
||||
alias: "a",
|
||||
},
|
||||
resource: {
|
||||
fields: [
|
||||
"a.productname",
|
||||
"a.productid",
|
||||
"b.executorid",
|
||||
"b.taskname",
|
||||
"b.taskid",
|
||||
"b.completed",
|
||||
"b.qaid",
|
||||
],
|
||||
},
|
||||
filters: {
|
||||
equal: {
|
||||
"1:tasks.taskname": "assembling",
|
||||
},
|
||||
onEmptyFilter: "all",
|
||||
},
|
||||
sort: {
|
||||
productname: {
|
||||
direction: "ASCENDING",
|
||||
},
|
||||
},
|
||||
paginate: {
|
||||
limit: 100,
|
||||
page: 1,
|
||||
},
|
||||
relationships: [
|
||||
{
|
||||
tableName: "tasks",
|
||||
column: "tasks",
|
||||
through: "products_tasks",
|
||||
from: "productid",
|
||||
to: "taskid",
|
||||
fromPrimary: "productid",
|
||||
toPrimary: "taskid",
|
||||
aliases: {
|
||||
products_tasks: "c",
|
||||
tasks: "b",
|
||||
products: "a",
|
||||
},
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
table: {
|
||||
type: "table",
|
||||
_id: "datasource_plus_8066e56456784eb2a00129d31be5c3e7__products",
|
||||
primary: ["productid"],
|
||||
name: "a",
|
||||
schema: {
|
||||
productname: {
|
||||
type: "string",
|
||||
externalType: "character varying",
|
||||
autocolumn: false,
|
||||
name: "productname",
|
||||
constraints: {
|
||||
presence: false,
|
||||
},
|
||||
},
|
||||
productid: {
|
||||
type: "number",
|
||||
externalType: "integer",
|
||||
autocolumn: true,
|
||||
name: "productid",
|
||||
constraints: {
|
||||
presence: false,
|
||||
},
|
||||
},
|
||||
tasks: {
|
||||
tableId:
|
||||
"datasource_plus_8066e56456784eb2a00129d31be5c3e7__tasks",
|
||||
name: "tasks",
|
||||
relationshipType: "many-to-many",
|
||||
fieldName: "taskid",
|
||||
through:
|
||||
"datasource_plus_8066e56456784eb2a00129d31be5c3e7__products_tasks",
|
||||
throughFrom: "taskid",
|
||||
throughTo: "productid",
|
||||
type: "link",
|
||||
main: true,
|
||||
_id: "ca6862d9ba09146dd8a68e3b5b7055a09",
|
||||
},
|
||||
},
|
||||
sourceId: "datasource_plus_8066e56456784eb2a00129d31be5c3e7",
|
||||
sourceType: "external",
|
||||
primaryDisplay: "productname",
|
||||
},
|
||||
},
|
||||
}
|
||||
const queryJson = getJson(`filterByRelationship.json`)
|
||||
let query = new Sql(SqlClient.POSTGRES, limit)._query(queryJson)
|
||||
expect(query).toEqual({
|
||||
bindings: [100, "assembling", limit],
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"endpoint": {
|
||||
"datasourceId": "datasource_plus_8066e56456784eb2a00129d31be5c3e7",
|
||||
"entityId": "products",
|
||||
"operation": "READ"
|
||||
},
|
||||
"resource": {
|
||||
"fields": [
|
||||
"a.productname",
|
||||
"a.productid",
|
||||
"b.executorid",
|
||||
"b.taskname",
|
||||
"b.taskid",
|
||||
"b.completed",
|
||||
"b.qaid"
|
||||
]
|
||||
},
|
||||
"filters": {
|
||||
"equal": {
|
||||
"1:tasks.taskname": "assembling"
|
||||
},
|
||||
"onEmptyFilter": "all"
|
||||
},
|
||||
"sort": {
|
||||
"productname": {
|
||||
"direction": "ASCENDING"
|
||||
}
|
||||
},
|
||||
"paginate": {
|
||||
"limit": 100,
|
||||
"page": 1
|
||||
},
|
||||
"relationships": [
|
||||
{
|
||||
"tableName": "tasks",
|
||||
"column": "tasks",
|
||||
"through": "products_tasks",
|
||||
"from": "productid",
|
||||
"to": "taskid",
|
||||
"fromPrimary": "productid",
|
||||
"toPrimary": "taskid"
|
||||
}
|
||||
],
|
||||
"tableAliases": {
|
||||
"products_tasks": "c",
|
||||
"tasks": "b",
|
||||
"products": "a"
|
||||
},
|
||||
"meta": {
|
||||
"table": {
|
||||
"type": "table",
|
||||
"_id": "datasource_plus_8066e56456784eb2a00129d31be5c3e7__products",
|
||||
"primary": [
|
||||
"productid"
|
||||
],
|
||||
"name": "a",
|
||||
"schema": {
|
||||
"productname": {
|
||||
"type": "string",
|
||||
"externalType": "character varying",
|
||||
"autocolumn": false,
|
||||
"name": "productname",
|
||||
"constraints": {
|
||||
"presence": false
|
||||
}
|
||||
},
|
||||
"productid": {
|
||||
"type": "number",
|
||||
"externalType": "integer",
|
||||
"autocolumn": true,
|
||||
"name": "productid",
|
||||
"constraints": {
|
||||
"presence": false
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"tableId": "datasource_plus_8066e56456784eb2a00129d31be5c3e7__tasks",
|
||||
"name": "tasks",
|
||||
"relationshipType": "many-to-many",
|
||||
"fieldName": "taskid",
|
||||
"through": "datasource_plus_8066e56456784eb2a00129d31be5c3e7__products_tasks",
|
||||
"throughFrom": "taskid",
|
||||
"throughTo": "productid",
|
||||
"type": "link",
|
||||
"main": true,
|
||||
"_id": "ca6862d9ba09146dd8a68e3b5b7055a09"
|
||||
}
|
||||
},
|
||||
"sourceId": "datasource_plus_8066e56456784eb2a00129d31be5c3e7",
|
||||
"sourceType": "external",
|
||||
"primaryDisplay": "productname"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,6 @@ export interface RelationshipsJson {
|
|||
fromPrimary?: string
|
||||
toPrimary?: string
|
||||
tableName: string
|
||||
aliases?: Record<string, string>
|
||||
column: string
|
||||
}
|
||||
|
||||
|
@ -75,7 +74,6 @@ export interface QueryJson {
|
|||
endpoint: {
|
||||
datasourceId: string
|
||||
entityId: string
|
||||
alias?: string
|
||||
operation: Operation
|
||||
schema?: string
|
||||
}
|
||||
|
@ -96,6 +94,7 @@ export interface QueryJson {
|
|||
idFilter?: SearchFilters
|
||||
}
|
||||
relationships?: RelationshipsJson[]
|
||||
tableAliases?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface SqlQuery {
|
||||
|
|
Loading…
Reference in New Issue