Skip to content

Commit

Permalink
Test all aggregation types.
Browse files Browse the repository at this point in the history
  • Loading branch information
samwho committed Oct 1, 2024
1 parent 7cee150 commit 4165c6c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
55 changes: 55 additions & 0 deletions packages/server/src/api/routes/tests/viewV2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2490,6 +2490,61 @@ describe.each([
expect(row["Total Price"]).toEqual(priceByQuantity[row.quantity])
}
})

it.each([
CalculationType.COUNT,
CalculationType.SUM,
CalculationType.AVG,
CalculationType.MIN,
CalculationType.MAX,
])("should be able to calculate $type", async type => {
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
schema: {
aggregate: {
visible: true,
calculationType: type,
field: "price",
},
},
})

const response = await config.api.viewV2.search(view.id, {
query: {},
})

function calculate(
type: CalculationType,
numbers: number[]
): number {
switch (type) {
case CalculationType.COUNT:
return numbers.length
case CalculationType.SUM:
return numbers.reduce((a, b) => a + b, 0)
case CalculationType.AVG:
return numbers.reduce((a, b) => a + b, 0) / numbers.length
case CalculationType.MIN:
return Math.min(...numbers)
case CalculationType.MAX:
return Math.max(...numbers)
}
}

const prices = rows.map(row => row.price)
const expected = calculate(type, prices)
const actual = response.rows[0].aggregate

if (type === CalculationType.AVG) {
// The average calculation can introduce floating point rounding
// errors, so we need to compare to within a small margin of
// error.
expect(actual).toBeCloseTo(expected)
} else {
expect(actual).toEqual(expected)
}
})
})
})

Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/integrations/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
try {
const bindings = query.bindings || []
this.log(query.sql, bindings)
return await client.query(query.sql, bindings)
const result = await client.query(query.sql, bindings)
return result
} catch (err: any) {
await this.closeConnection()
let readableMessage = getReadableErrorMessage(
Expand Down
19 changes: 19 additions & 0 deletions packages/server/src/utilities/rowProcessor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,25 @@ export async function coreOutputProcessing(
}
}
}

if (sdk.views.isView(source)) {
const calculationFields = Object.keys(
helpers.views.calculationFields(source)
)

// We ensure all calculation fields are returned as numbers. During the
// testing of this feature it was discovered that the COUNT operation
// returns a string for MySQL, MariaDB, and Postgres. But given that all
// calculation fields should be numbers, we blanket make sure of that
// here.
for (const key of calculationFields) {
for (const row of rows) {
if (typeof row[key] === "string") {
row[key] = parseFloat(row[key])
}
}
}
}
}

if (!isUserMetadataTable(table._id!)) {
Expand Down

0 comments on commit 4165c6c

Please sign in to comment.