diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 91dceaf5a6..f62bb0f7e3 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -529,7 +529,7 @@ class InternalBuilder { if (!matchesTableName) { updatedKey = filterKey.replace( new RegExp(`^${relationship.column}.`), - `${aliases![relationship.tableName]}.` + `${aliases?.[relationship.tableName] || relationship.tableName}.` ) } else { updatedKey = filterKey @@ -1579,7 +1579,7 @@ class InternalBuilder { query = this.addFilters(query, filters, { relationship: true }) // handle relationships with a CTE for all others - if (relationships?.length) { + if (relationships?.length && aggregations.length === 0) { const mainTable = this.query.tableAliases?.[this.query.endpoint.entityId] || this.query.endpoint.entityId @@ -1594,10 +1594,8 @@ class InternalBuilder { // add JSON aggregations attached to the CTE return this.addJsonRelationships(cte, tableName, relationships) } - // no relationships found - return query - else { - return query - } + + return query } update(opts: QueryOptions): Knex.QueryBuilder { diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 62aa4d8d2a..939ea399d9 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -682,7 +682,19 @@ export class ExternalRequest { filters = this.prepareFilters(id, filters || {}, table) const relationships = buildExternalRelationships(table, this.tables) + let aggregations: Aggregation[] = [] + if (sdk.views.isView(this.source)) { + const calculationFields = helpers.views.calculationFields(this.source) + for (const [key, field] of Object.entries(calculationFields)) { + aggregations.push({ + ...field, + name: key, + }) + } + } + const incRelationships = + aggregations.length === 0 && config.includeSqlRelationships === IncludeRelationship.INCLUDE // clean up row on ingress using schema @@ -709,17 +721,6 @@ export class ExternalRequest { throw "Deletion must be filtered" } - let aggregations: Aggregation[] = [] - if (sdk.views.isView(this.source)) { - const calculationFields = helpers.views.calculationFields(this.source) - for (const [key, field] of Object.entries(calculationFields)) { - aggregations.push({ - ...field, - name: key, - }) - } - } - let json: QueryJson = { endpoint: { datasourceId: this.datasource._id!, diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 15b9a29e11..1f2470f097 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -3659,6 +3659,108 @@ describe.each([ ) }) + it("should be able to filter on relationships", async () => { + const companies = await config.api.table.save( + saveTableRequest({ + schema: { + name: { + name: "name", + type: FieldType.STRING, + }, + }, + }) + ) + + const employees = await config.api.table.save( + saveTableRequest({ + schema: { + age: { + type: FieldType.NUMBER, + name: "age", + }, + name: { + type: FieldType.STRING, + name: "name", + }, + company: { + type: FieldType.LINK, + name: "company", + tableId: companies._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + fieldName: "company", + }, + }, + }) + ) + + const view = await config.api.viewV2.create({ + tableId: employees._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + queryUI: { + groups: [ + { + filters: [ + { + operator: BasicOperator.EQUAL, + field: "company.name", + value: "Aperture Science Laboratories", + }, + ], + }, + ], + }, + schema: { + sum: { + visible: true, + calculationType: CalculationType.SUM, + field: "age", + }, + }, + }) + + const apertureScience = await config.api.row.save( + companies._id!, + { + name: "Aperture Science Laboratories", + } + ) + + const blackMesa = await config.api.row.save(companies._id!, { + name: "Black Mesa", + }) + + await Promise.all([ + config.api.row.save(employees._id!, { + name: "Alice", + age: 25, + company: apertureScience._id, + }), + config.api.row.save(employees._id!, { + name: "Bob", + age: 30, + company: apertureScience._id, + }), + config.api.row.save(employees._id!, { + name: "Charly", + age: 27, + company: blackMesa._id, + }), + config.api.row.save(employees._id!, { + name: "Danny", + age: 15, + company: blackMesa._id, + }), + ]) + + const { rows } = await config.api.viewV2.search(view.id, { + query: {}, + }) + + expect(rows).toHaveLength(1) + expect(rows[0].sum).toEqual(55) + }) + it("should be able to filter rows on the view itself", async () => { const table = await config.api.table.save( saveTableRequest({