Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Go code to retrieve multiple rows from BigQuery

I am writing some utils to retrieve multiple rows from BigQuery in a generic way using Go. e.g.

type User struct {name string, surname string}
type Car struct {model string, platenumber string}

query1:="SELECT name, surname FROM UserTable"
query2:="SELECT model, platenumber FROM CarTable"

cars, _ := query2.GetResults()
users, _ := query1.GetResults()
OR
cars := []Car{}
query2.GetResults(cars) // and it would append to the slice

I am unsure about the signature of GetResults. I need somehow to pass the type to BigQuery library so it can retrieve the data and map it to the struct correctly. But at the same time I need to make it generic so it can be used for different types.

At the moment my GetResults looks like this: it doesn't work, the error is:

bigquery: cannot convert *interface {} to ValueLoader (need pointer to []Value, map[string]Value, or struct)[]

But I cannot pass directly the struct as I want to make it generic.

func (s *Query) GetResults() ([]interface{}, error) {
    var result []interface{}

    job, err := s.Run()
    if err != nil {
        s.log.Error(err, "error in running the query")
        return nil, err
    }

    it, err := job.ReadData()
    if err != nil {
        s.log.Error(err, "error in reading the data")
        return nil, err
    }
    var row interface{}
    for {
        err := it.Next(&row)
        if err != nil {
            fmt.Print(err)
            break
        }
        result = append(result, row)
    }
    return result, nil
}

Is there another way to achieve that? Or is the good way not to create a method like that?

I've tried quite a lot of different things, with or without pointer, with or without array, by modifying the args, or returning a new list, nothing seem to work, and doing all of that feels a bit wrong regarding the nature "easy" of what I am trying to achieve.

I've also looked into doing the following

GetResults[T any]() ([]T, error)

But it's "excluded" as GetResults is part of an interface (and we can't define generic for a method of an interface). And I can't/don't want to define a type for all the interface, as it impacts other interfaces.

like image 563
Simon Avatar asked Sep 14 '26 14:09

Simon


1 Answers

Basically the bigquery.Value is underlying from interface{} so you can use that, replace interface{} with bigquery.Value in your code. Here's a sample from my code:

func (srv *SBigQuery) GBQReadQueryToStruct(ctx context.Context, query string) ([]map[string]bigquery.Value, error) {
var results []map[string]bigquery.Value
// Construct a query.
q := srv.Query(query)

// Execute the query.
it, err := q.Read(ctx)
if err != nil {
    return nil, fmt.Errorf("unable to read query: %v", err)
}

// Iterate through the results.
var row map[string]bigquery.Value
for {
    err := it.Next(&row)
    if err == iterator.Done {
        break
    }
    if err != nil {
        return nil, fmt.Errorf("unable to iterate through query results: %v", err)
    }
    results = append(results, row)
}

return results, nil

}

Reference:

  1. https://cloud.google.com/go/docs/reference/cloud.google.com/go/bigquery/1.16.0#cloud_google_com_go_bigquery_Value
like image 106
Catur Bagas Avatar answered Sep 17 '26 04:09

Catur Bagas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!