From 34385a3f4c8727ff9d6736e398ea58976a4466c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net>
Date: Thu, 7 Aug 2014 11:53:25 -0500
Subject: [PATCH] QL: Adding support for db.Result.Group().

---
 ql/_dumps/structs.sql |  8 +++++++
 ql/database.go        |  1 +
 ql/database_test.go   | 56 +++++++++++++++++++++++++++++++++++++++++++
 ql/layout.go          |  8 +++++++
 ql/result.go          | 22 +++++++++++++++++
 5 files changed, 95 insertions(+)

diff --git a/ql/_dumps/structs.sql b/ql/_dumps/structs.sql
index 997b8165..21b48e86 100644
--- a/ql/_dumps/structs.sql
+++ b/ql/_dumps/structs.sql
@@ -43,4 +43,12 @@ CREATE TABLE data_types (
   _time time
 );
 
+DROP TABLE IF EXISTS stats_test;
+
+CREATE TABLE stats_test (
+	id uint,
+	numeric int64,
+	value int64
+);
+
 COMMIT;
diff --git a/ql/database.go b/ql/database.go
index 13af8cf1..bb7af134 100644
--- a/ql/database.go
+++ b/ql/database.go
@@ -108,6 +108,7 @@ func init() {
 		qlDropDatabaseLayout,
 		qlDropTableLayout,
 		qlSelectCountLayout,
+		qlGroupByLayout,
 	}
 
 	db.Register(Adapter, &source{})
diff --git a/ql/database_test.go b/ql/database_test.go
index 9e73cc7a..24fcb689 100644
--- a/ql/database_test.go
+++ b/ql/database_test.go
@@ -34,6 +34,7 @@ import (
 
 	"menteslibres.net/gosexy/to"
 	//"reflect"
+	"math/rand"
 	"strings"
 	"testing"
 	"time"
@@ -215,6 +216,61 @@ func TestAppend(t *testing.T) {
 
 }
 
+func TestGroup(t *testing.T) {
+
+	var err error
+	var sess db.Database
+	var stats db.Collection
+
+	if sess, err = db.Open(Adapter, settings); err != nil {
+		t.Fatal(err)
+	}
+
+	type stats_t struct {
+		Numeric int `db:"numeric"`
+		Value   int `db:"value"`
+	}
+
+	defer sess.Close()
+
+	if stats, err = sess.Collection("stats_test"); err != nil {
+		t.Fatal(err)
+	}
+
+	// Truncating table.
+	if err = stats.Truncate(); err != nil {
+		t.Fatal(err)
+	}
+
+	// Adding row append.
+	for i := 0; i < 1000; i++ {
+		numeric, value := rand.Intn(10), rand.Intn(100)
+		if _, err = stats.Append(stats_t{numeric, value}); err != nil {
+			t.Fatal(err)
+		}
+	}
+
+	// db.Func{"COUNT", 1},
+	// db.Func{"SUM", `value`},
+
+	// Testing GROUP BY
+	res := stats.Find().Select(
+		`numeric`,
+		db.Raw{`count(1) AS counter`},
+		db.Raw{`sum(value) AS total`},
+	).Group(`numeric`)
+
+	var results []map[string]interface{}
+
+	if err = res.All(&results); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(results) != 10 {
+		t.Fatalf(`Expecting exactly 10 results, this could fail, but it's very unlikely to happen.`)
+	}
+}
+
 // Attempts to count all rows in our newly defined set.
 func TestResultCount(t *testing.T) {
 	var err error
diff --git a/ql/layout.go b/ql/layout.go
index 15f9a44f..d60f32e2 100644
--- a/ql/layout.go
+++ b/ql/layout.go
@@ -65,6 +65,8 @@ const (
 
 			{{.Where}}
 
+			{{.GroupBy}}
+
 			{{.OrderBy}}
 
 			{{if .Limit}}
@@ -121,4 +123,10 @@ const (
 	qlDropTableLayout = `
 		DROP TABLE {{.Table}}
 	`
+
+	qlGroupByLayout = `
+		{{if .GroupColumns}}
+			GROUP BY {{.GroupColumns}}
+		{{end}}
+	`
 )
diff --git a/ql/result.go b/ql/result.go
index 4c9dc638..164574ca 100644
--- a/ql/result.go
+++ b/ql/result.go
@@ -43,6 +43,7 @@ type result struct {
 	columns   sqlgen.Columns
 	where     sqlgen.Where
 	orderBy   sqlgen.OrderBy
+	groupBy   sqlgen.GroupBy
 	arguments []interface{}
 }
 
@@ -59,6 +60,7 @@ func (self *result) setCursor() error {
 			Offset:  self.offset,
 			Where:   self.where,
 			OrderBy: self.orderBy,
+			GroupBy: self.groupBy,
 		}, self.arguments...)
 	}
 	return err
@@ -83,6 +85,26 @@ func (self *result) Skip(n uint) db.Result {
 	return self
 }
 
+// Used to group results that have the same value in the same column or
+// columns.
+func (self *result) Group(fields ...interface{}) db.Result {
+
+	groupByColumns := make(sqlgen.GroupBy, 0, len(fields))
+
+	l := len(fields)
+	for i := 0; i < l; i++ {
+		switch value := fields[i].(type) {
+		// Maybe other types?
+		default:
+			groupByColumns = append(groupByColumns, sqlgen.Column{value})
+		}
+	}
+
+	self.groupBy = groupByColumns
+
+	return self
+}
+
 // Determines sorting of results according to the provided names. Fields may be
 // prefixed by - (minus) which means descending order, ascending order would be
 // used otherwise.
-- 
GitLab