How to refactor this code to retrieve custom (virtual) columns from DB with one query only using an ORM

How to refactor this code to retrieve custom (virtual) columns from DB with one query only using an ORM

I’m studying Golang trying to build an application.

I have written many models:

  • Player

  • Book

  • Score

  • Referee

  • Life

  • Team

  • Doc

  • Family

and so on…

A model is like this:


type Player struct {

  id   int

  name string

}

I’m using an ORM: Go-PG (https://github.com/go-pg/pg) and before I also tried GORM (https://github.com/jinzhu/gorm).

I’m using it like this:


db := database.New(config)

defer db.Close()

var players []Player

error := db.Model(&players).Select()

// handle error

and it works.

The SQL query is:


SELECT * FROM players

with these rows:

+--------+----------+
| id     | name     |
+--------+----------+
| 1      | John     |
+--------+----------+
| 2      | Mike     |
+--------+----------+
| 3      | Bob      |
+--------+----------+

Now I need to query from DB one or more “virtual” columns like this:


'mysqlcalculation' AS mycalculation

so I tried to use:


query.Column("*").ColumnExpr("'mysqlcalculation' AS mycalculation")

which generates a correct SQL query:


SELECT *, 'mysqlcalculation' AS mycalculation FROM players

with these rows:

+--------+----------+------------------+
| id     | name     | mycalculation    |
+--------+----------+------------------+
| 1      | John     | mysqlcalculation |
+--------+----------+------------------+
| 2      | Mike     | mysqlcalculation |
+--------+----------+------------------+
| 3      | Bob      | mysqlcalculation |
+--------+----------+------------------+

I’m doing this because I am interested in the result in the mycalculation column which is much more convenient to calculate in the database than in Go; it can be JSON or string. Simply data.

Now my ORM panic with:

PNC error="pg: can't find column=mycalculation in model=Player (try discard_unknown_columns)"

I can understand that because now go-pg doesn’t know how to bind data, so now I’m using:

func FindAllPlayers(ctx context.Context, pagination models.Pagination) ([]*models.Player, *string, error) {

  var cursor *string

  var playerWithCalculation []struct {

    Mycalculation  string

    models.Player

  }

  var players []Player

  // use pagination for changing query settings

  error := db.Model(&playerWithCalculation).Column("*").ColumnExpr("'mysqlcalculation' AS mycalculation").Select()

  // handle error

  cursor := playerWithCalculation[0].Mycalculation

  for i := range playerWithCalculation {

    players = append(players, &playerWithCalculation[i].Player)

  }

return players, cursor, nil

QUESTION

I think this is wasteful and expensive. Many allocations and loops.

And besides, I have several models and resolvers and methods!

What can I do?

How can I improve this code and my architecture?

I think a possible solution is to run two queries, perhaps in two different goroutines.

But I don’t want to run two queries for this.