Skip to content

feat: Add Avg, Min, and Max aggregate functions to DB Query#1240

Merged
hwbrzzl merged 10 commits intogoravel:masterfrom
oguzhankrcb:feat/db-aggregates-max-min-avg
Oct 21, 2025
Merged

feat: Add Avg, Min, and Max aggregate functions to DB Query#1240
hwbrzzl merged 10 commits intogoravel:masterfrom
oguzhankrcb:feat/db-aggregates-max-min-avg

Conversation

@oguzhankrcb
Copy link
Contributor

@oguzhankrcb oguzhankrcb commented Oct 19, 2025

Database Query Methods Refactoring

Summary

This PR refactors the database query aggregation methods to use a pointer pattern instead of returning values directly. This change provides better type safety and allows for more flexible error handling.

Key Changes

1. Pointer Pattern Implementation

We've refactored the aggregation methods to use a pointer pattern where the result is populated into a destination variable passed by reference:

  • Avg(column string, dest any) error
  • Sum(column string, dest any) error
  • Min(column string, dest any) error
  • Max(column string, dest any) error

2. New Sum Method

We've changed the Sum method to use pointer implementation:

// Sum calculates the sum of a column's values and populates the destination object.
func (r *Query) Sum(column string, dest any) error

3. Updated Method Signatures

All aggregation methods now follow the same pattern:

  • Accept a column name as a string
  • Accept a destination pointer (any type)
  • Return an error if the operation fails

4. Type Safety Improvements

The implementation includes validation to ensure the destination parameter is a pointer:

destValue := reflect.ValueOf(dest)
if destValue.Kind() != reflect.Ptr {
    return errors.DatabaseUnsupportedType.Args(destValue.Kind(), "pointer")
}

Usage Examples

Before (hypothetical example)

sum, err := query.Sum("price")
if err != nil {
    return err
}
fmt.Printf("Total price: %d\n", sum)

After (current implementation)

// Calculate the sum of a column
var totalSum int64
err := query.Table("products").Sum("price", &totalSum)
if err != nil {
    return err
}
fmt.Printf("Total price: %d\n", totalSum)

// Calculate the average of a column
var avgPrice float64
err := query.Table("products").Avg("price", &avgPrice)
if err != nil {
    return err
}
fmt.Printf("Average price: %.2f\n", avgPrice)

// Calculate the minimum value of a column
var minPrice int64
err := query.Table("products").Min("price", &minPrice)
if err != nil {
    return err
}
fmt.Printf("Minimum price: %d\n", minPrice)

// Calculate the maximum value of a column
var maxPrice int64
err := query.Table("products").Max("price", &maxPrice)
if err != nil {
    return err
}
fmt.Printf("Maximum price: %d\n", maxPrice)

Return Types

All aggregation methods now return:

  • error - indicating success or failure of the operation
  • The actual result is populated into the destination pointer passed as a parameter

Implementation Details

The implementation uses the following approach:

  1. Validate that the destination parameter is a non-nil pointer
  2. Build a SQL query with the appropriate aggregation function (SUM, AVG, MIN, MAX)
  3. Execute the query using the existing query infrastructure
  4. Populate the result into the destination pointer
  5. Return any errors that occurred during execution

Benefits

  1. Type Safety: The pointer pattern ensures type compatibility at compile time
  2. Consistency: All aggregation methods now follow the same pattern
  3. Flexibility: Users can specify the exact type they want the result in
  4. Error Handling: Clear error reporting when operations fail

Testing

All tests pass successfully with the new implementation.

@oguzhankrcb oguzhankrcb requested a review from a team as a code owner October 19, 2025 16:38
@codecov
Copy link

codecov bot commented Oct 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.73%. Comparing base (cfae10b) to head (558ae7f).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1240      +/-   ##
==========================================
- Coverage   66.09%   65.73%   -0.36%     
==========================================
  Files         248      248              
  Lines       17069    17185     +116     
==========================================
+ Hits        11282    11297      +15     
- Misses       5410     5511     +101     
  Partials      377      377              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, pretty good.

@@ -148,6 +148,12 @@ type Query interface {
SharedLock() Query
// Sum calculates the sum of a column's values and populates the destination object.
Sum(column string) (int64, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We consider changing the Sum function to Sum(column string, dest any) error to adapt the int* and float* situation. Hence, Avag, Min, Max can do that as well. If it makes sense, could you optimize it in this PR simultaneously?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, I have changed the Sum methods signature and added tests and same for the other methods.


avg, err := query.Query().Table("users").Avg("id")
s.Nil(err)
s.True(avg > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you precisely assert the value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


min, err := query.Query().Table("users").Min("id")
s.Nil(err)
s.True(min > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


max, err := query.Query().Table("users").Max("id")
s.Nil(err)
s.True(max > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@oguzhankrcb oguzhankrcb requested a review from hwbrzzl October 20, 2025 17:59
@hwbrzzl hwbrzzl requested a review from Copilot October 21, 2025 02:56
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors database aggregation methods (Sum, Avg, Min, Max) to use a pointer-based pattern for improved type safety and error handling. The new implementation requires callers to pass a destination pointer that will be populated with the result, rather than returning the value directly.

Key changes:

  • Refactored Sum method to accept a destination pointer parameter and return only an error
  • Added three new aggregation methods: Avg, Min, and Max, all following the same pointer-based pattern
  • Updated all contract interfaces, implementations, mocks, and tests to use the new signatures

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
contracts/database/orm/orm.go Updated Query interface with new signatures for Sum and added Avg, Min, Max methods
contracts/database/db/db.go Updated Query interface with new signatures for Sum and added Avg, Min, Max methods
database/gorm/query.go Implemented pointer-based Sum, Avg, Min, Max with pointer validation
database/db/query.go Implemented pointer-based Sum, Avg, Min, Max with pointer validation
mocks/database/orm/Query.go Generated mock implementations for updated method signatures
mocks/database/db/Query.go Generated mock implementations for updated method signatures
tests/models.go Added Ratio field to User model for testing aggregation functions
tests/table.go Added ratio column to user table schemas
tests/query_test.go Updated Sum tests and added comprehensive tests for Avg, Min, Max
tests/db_test.go Updated Sum tests and added comprehensive tests for Avg, Min, Max
tests/to_sql_test.go Updated SQL assertions to include new ratio column
database/db/query_test.go Updated unit tests for pointer-based aggregation methods

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@hwbrzzl hwbrzzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty good, thanks!

@hwbrzzl hwbrzzl merged commit 54334ac into goravel:master Oct 21, 2025
13 of 14 checks passed
@hwbrzzl
Copy link
Contributor

hwbrzzl commented Oct 26, 2025

Hey @oguzhankrcb FYI, added you to the contributor list, looking forward to more amazing PRs, thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants