Initial implementation - needs testing.

This commit is contained in:
mike12345567 2024-07-08 18:42:11 +01:00
parent 37d53cff20
commit c0b85c6379
3 changed files with 59 additions and 15 deletions

View File

@ -22,6 +22,18 @@ export function isManyToMany(
return !!(field as ManyToManyRelationshipFieldMetadata).through
}
function isCorrectRelationship(
relationship: RelationshipsJson,
row: Row
): boolean {
const junctionTableId = relationship.through!
const possibleColumns = [
`${junctionTableId}.doc1.fieldName`,
`${junctionTableId}.doc2.fieldName`,
]
return !!possibleColumns.find(col => row[col] === relationship.column)
}
/**
* This iterates through the returned rows and works out what elements of the rows
* actually match up to another row (based on primary keys) - this is pretty specific
@ -64,7 +76,9 @@ export async function updateRelationshipColumns(
if (!linked._id) {
continue
}
columns[relationship.column] = linked
if (opts?.sqs && isCorrectRelationship(relationship, row)) {
columns[relationship.column] = linked
}
}
for (let [column, related] of Object.entries(columns)) {
if (!row._id) {

View File

@ -5,6 +5,7 @@ import {
Operation,
QueryJson,
RelationshipFieldMetadata,
RelationshipsJson,
Row,
RowSearchParams,
SearchFilters,
@ -30,7 +31,10 @@ import {
SQLITE_DESIGN_DOC_ID,
SQS_DATASOURCE_INTERNAL,
} from "@budibase/backend-core"
import { CONSTANT_INTERNAL_ROW_COLS } from "../../../../db/utils"
import {
CONSTANT_INTERNAL_ROW_COLS,
generateJunctionTableID,
} from "../../../../db/utils"
import AliasTables from "../sqlAlias"
import { outputProcessing } from "../../../../utilities/rowProcessor"
import pick from "lodash/pick"
@ -52,7 +56,7 @@ const USER_COLUMN_PREFIX_REGEX = new RegExp(
function buildInternalFieldList(
table: Table,
tables: Table[],
opts: { relationships: boolean } = { relationships: true }
opts?: { relationships?: RelationshipsJson[] }
) {
let fieldList: string[] = []
fieldList = fieldList.concat(
@ -60,20 +64,31 @@ function buildInternalFieldList(
)
for (let col of Object.values(table.schema)) {
const isRelationship = col.type === FieldType.LINK
if (!opts.relationships && isRelationship) {
if (!opts?.relationships && isRelationship) {
continue
}
if (isRelationship) {
const linkCol = col as RelationshipFieldMetadata
const relatedTable = tables.find(table => table._id === linkCol.tableId)!
fieldList = fieldList.concat(
buildInternalFieldList(relatedTable, tables, { relationships: false })
// no relationships provided, don't go more than a layer deep
fieldList = fieldList.concat(buildInternalFieldList(relatedTable, tables))
fieldList.push(
`${generateJunctionTableID(
table._id!,
relatedTable._id!
)}.doc1.fieldName`
)
fieldList.push(
`${generateJunctionTableID(
table._id!,
relatedTable._id!
)}.doc2.fieldName`
)
} else {
fieldList.push(`${table._id}.${mapToUserColumn(col.name)}`)
}
}
return fieldList
return [...new Set(fieldList)]
}
function cleanupFilters(
@ -165,18 +180,27 @@ function reverseUserColumnMapping(rows: Row[]) {
})
}
function runSqlQuery(json: QueryJson, tables: Table[]): Promise<Row[]>
function runSqlQuery(
json: QueryJson,
tables: Table[],
relationships: RelationshipsJson[]
): Promise<Row[]>
function runSqlQuery(
json: QueryJson,
tables: Table[],
relationships: RelationshipsJson[],
opts: { countTotalRows: true }
): Promise<number>
async function runSqlQuery(
json: QueryJson,
tables: Table[],
relationships: RelationshipsJson[],
opts?: { countTotalRows?: boolean }
) {
const alias = new AliasTables(tables.map(table => table.name))
const relationshipJunctionTableIds = relationships.map(rel => rel.through!)
const alias = new AliasTables(
tables.map(table => table.name).concat(relationshipJunctionTableIds)
)
if (opts?.countTotalRows) {
json.endpoint.operation = Operation.COUNT
}
@ -193,8 +217,13 @@ async function runSqlQuery(
let bindings = query.bindings
// quick hack for docIds
sql = sql.replace(/`doc1`.`rowId`/g, "`doc1.rowId`")
sql = sql.replace(/`doc2`.`rowId`/g, "`doc2.rowId`")
const fixJunctionDocs = (field: string) =>
["doc1", "doc2"].forEach(doc => {
sql = sql.replaceAll(`\`${doc}\`.\`${field}\``, `\`${doc}.${field}\``)
})
fixJunctionDocs("rowId")
fixJunctionDocs("fieldName")
if (Array.isArray(query)) {
throw new Error("SQS cannot currently handle multiple queries")
@ -260,7 +289,7 @@ export async function search(
columnPrefix: USER_COLUMN_PREFIX,
},
resource: {
fields: buildInternalFieldList(table, allTables),
fields: buildInternalFieldList(table, allTables, { relationships }),
},
relationships,
}
@ -292,11 +321,11 @@ export async function search(
try {
const queries: Promise<Row[] | number>[] = []
queries.push(runSqlQuery(request, allTables))
queries.push(runSqlQuery(request, allTables, relationships))
if (options.countRows) {
// get the total count of rows
queries.push(
runSqlQuery(request, allTables, {
runSqlQuery(request, allTables, relationships, {
countTotalRows: true,
})
)

View File

@ -111,7 +111,8 @@ export default class AliasTables {
aliasField(field: string) {
const tableNames = this.tableNames
if (field.includes(".")) {
const [tableName, column] = field.split(".")
const [tableName, ...rest] = field.split(".")
const column = rest.join(".")
const foundTableName = tableNames.find(name => {
const idx = tableName.indexOf(name)
if (idx === -1 || idx > 1) {