Skip to content

Commit

Permalink
Merge pull request #5 from FreeLeh/count-using-gviz
Browse files Browse the repository at this point in the history
Simplify COUNT() operation to use GVIZ API instead
  • Loading branch information
fatanugraha authored Aug 27, 2022
2 parents 757ce9b + 269edfd commit ae2965f
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 56 deletions.
9 changes: 5 additions & 4 deletions internal/google/sheets/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package sheets

import (
"fmt"
"strconv"
"strings"
)

Expand Down Expand Up @@ -134,12 +133,14 @@ func (r rawQueryRowsResult) convertRawValue(cellIdx int, cell rawQueryRowsResult
col := r.Table.Cols[cellIdx]
switch col.Type {
case "boolean":
return cell.Raw == "true", nil
return strings.ToLower(cell.Raw) == "true", nil
case "number":
if strings.Contains(cell.Raw, ".") {
return strconv.ParseFloat(cell.Raw, 64)
return cell.Value, nil
} else {
val := cell.Value.(float64)
return int64(val), nil
}
return strconv.ParseInt(cell.Raw, 10, 64)
case "string":
// `string` type does not have the raw value
return cell.Value, nil
Expand Down
19 changes: 15 additions & 4 deletions internal/google/sheets/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,28 @@ func TestRawQueryRowsResult_toQueryRowsResult(t *testing.T) {
Cols: []rawQueryRowsResultColumn{
{ID: "A", Type: "number"},
{ID: "B", Type: "string"},
{ID: "C", Type: "boolean"},
},
Rows: []rawQueryRowsResultRow{
{
[]rawQueryRowsResultCell{
{Value: 123, Raw: "123"},
{Value: 123.0, Raw: "123"},
{Value: "blah", Raw: "blah"},
{Value: true, Raw: "true"},
},
},
{
[]rawQueryRowsResultCell{
{Value: 456, Raw: "456"},
{Value: 456.0, Raw: "456"},
{Value: "blah2", Raw: "blah2"},
{Value: false, Raw: "FALSE"},
},
},
{
[]rawQueryRowsResultCell{
{Value: 123.1, Raw: "123.1"},
{Value: "blah", Raw: "blah"},
{Value: true, Raw: "TRUE"},
},
},
},
Expand All @@ -107,8 +117,9 @@ func TestRawQueryRowsResult_toQueryRowsResult(t *testing.T) {

expected := QueryRowsResult{
Rows: [][]interface{}{
{int64(123), "blah"},
{int64(456), "blah2"},
{int64(123), "blah", true},
{int64(456), "blah2", false},
{123.1, "blah", true},
},
}

Expand Down
6 changes: 6 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package freeleh
import (
"context"
"errors"
"github.com/FreeLeh/GoFreeLeh/google/auth"
"regexp"
"strconv"

Expand Down Expand Up @@ -51,6 +52,11 @@ var (
ErrKeyNotFound = errors.New("error key not found")
)

// FreeDBGoogleAuthScopes specifies the list of Google Auth scopes required to run FreeDB implementations properly.
var (
FreeDBGoogleAuthScopes = auth.GoogleSheetsReadWrite
)

var (
defaultRowHeaderRange = "A1:" + generateColumnName(maxColumn-1) + "1"
defaultRowFullTableRange = "A2:" + generateColumnName(maxColumn-1)
Expand Down
40 changes: 13 additions & 27 deletions row.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ func (c GoogleSheetRowStoreConfig) validate() error {

// GoogleSheetRowStore encapsulates row store functionality on top of a Google Sheet.
type GoogleSheetRowStore struct {
wrapper sheetsWrapper
spreadsheetID string
sheetName string
scratchpadSheetName string
scratchpadLocation sheets.A1Range
colsMapping colsMapping
config GoogleSheetRowStoreConfig
wrapper sheetsWrapper
spreadsheetID string
sheetName string
colsMapping colsMapping
config GoogleSheetRowStoreConfig
}

// Select specifies which columns to return from the Google Sheet when querying and the output variable
Expand Down Expand Up @@ -104,10 +102,9 @@ func (s *GoogleSheetRowStore) Count() *GoogleSheetCountStmt {
return newGoogleSheetCountStmt(s)
}

// Close cleans up all held resources like the scratchpad cell booked for this specific GoogleSheetRowStore instance.
func (s *GoogleSheetRowStore) Close(ctx context.Context) error {
_, err := s.wrapper.Clear(ctx, s.spreadsheetID, []string{s.scratchpadLocation.Original})
return err
// Close cleans up all held resources if any.
func (s *GoogleSheetRowStore) Close(_ context.Context) error {
return nil
}

func (s *GoogleSheetRowStore) ensureHeaders() error {
Expand Down Expand Up @@ -156,30 +153,19 @@ func NewGoogleSheetRowStore(
}

config = injectTimestampCol(config)
scratchpadSheetName := sheetName + scratchpadSheetNameSuffix

store := &GoogleSheetRowStore{
wrapper: wrapper,
spreadsheetID: spreadsheetID,
sheetName: sheetName,
scratchpadSheetName: scratchpadSheetName,
colsMapping: generateColumnMapping(config.Columns),
config: config,
wrapper: wrapper,
spreadsheetID: spreadsheetID,
sheetName: sheetName,
colsMapping: generateColumnMapping(config.Columns),
config: config,
}

_ = ensureSheets(store.wrapper, store.spreadsheetID, store.sheetName)
_ = ensureSheets(store.wrapper, store.spreadsheetID, store.scratchpadSheetName)

if err := store.ensureHeaders(); err != nil {
panic(fmt.Errorf("error checking headers: %w", err))
}

scratchpadLocation, err := findScratchpadLocation(store.wrapper, store.spreadsheetID, store.scratchpadSheetName)
if err != nil {
panic(fmt.Errorf("error finding a scratchpad location in sheet %s: %w", store.scratchpadSheetName, err))
}
store.scratchpadLocation = scratchpadLocation

return store
}

Expand Down
2 changes: 1 addition & 1 deletion row_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestGoogleSheetRowStore_Integration(t *testing.T) {
GoogleSheetRowStoreConfig{Columns: []string{"name", "age", "dob"}},
)
defer func() {
deleteSheet(t, db.wrapper, spreadsheetID, []string{db.sheetName, db.scratchpadSheetName})
deleteSheet(t, db.wrapper, spreadsheetID, []string{db.sheetName})
_ = db.Close(context.Background())
}()

Expand Down
28 changes: 8 additions & 20 deletions stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,36 +549,24 @@ func (s *GoogleSheetCountStmt) Exec(ctx context.Context) (uint64, error) {
return 0, err
}

formula := fmt.Sprintf(
rwoCountQueryTemplate,
getA1Range(s.store.sheetName, defaultRowFullTableRange),
selectStmt,
)

result, err := s.store.wrapper.UpdateRows(
ctx,
s.store.spreadsheetID,
s.store.scratchpadLocation.Original,
[][]interface{}{{formula}},
)
result, err := s.store.wrapper.QueryRows(ctx, s.store.spreadsheetID, s.store.sheetName, selectStmt, true)
if err != nil {
return 0, err
}
if len(result.UpdatedValues) == 0 || len(result.UpdatedValues[0]) == 0 {
return 0, fmt.Errorf("error retrieving row indices to delete: %+v", result)
}

raw := result.UpdatedValues[0][0].(string)
if raw == naValue || raw == errorValue || raw == "" {
return 0, fmt.Errorf("error retrieving row indices to delete: %s", raw)
if len(result.Rows) != 1 || len(result.Rows[0]) != 1 {
return 0, errors.New("")
}
return strconv.ParseUint(raw, 10, 64)

count := result.Rows[0][0].(int64)
return uint64(count), nil
}

func newGoogleSheetCountStmt(store *GoogleSheetRowStore) *GoogleSheetCountStmt {
countClause := fmt.Sprintf("COUNT(%s)", rowIdxCol)
return &GoogleSheetCountStmt{
store: store,
queryBuilder: newQueryBuilder(store.colsMapping.NameMap(), []string{rowIdxCol}),
queryBuilder: newQueryBuilder(store.colsMapping.NameMap(), []string{countClause}),
}
}

Expand Down

0 comments on commit ae2965f

Please sign in to comment.