doc: example and readme fixes

This commit is contained in:
Michał Matczuk
2017-08-03 18:39:16 +02:00
parent 804cfad37c
commit bc289ada45
2 changed files with 140 additions and 214 deletions

View File

@@ -9,11 +9,17 @@ hood it uses `sqlx/reflectx` package so `sqlx` models will also work with `gocql
## Installation ## Installation
go get github.com/scylladb/gocqlx go get -u github.com/scylladb/gocqlx
## Features ## Features
Fast, boilerplate free and flexible `SELECTS`, `INSERTS`, `UPDATES` and `DELETES`. * Flexible `SELECT`, `INSERT`, `UPDATE` `DELETE` and `BATCH` query building using a DSL
* Support for named parameters (:identifier) in queries
* Binding parameters form struct or map
* Scanning results into structs
* Fast!
Example, see [full example here](https://github.com/scylladb/gocqlx/blob/master/example_test.go)
```go ```go
type Person struct { type Person struct {
@@ -38,57 +44,69 @@ p := &Person{
} }
} }
// Insert with TTL // Batch
{ {
stmt, names := qb.Insert("person").Columns("first_name", "last_name", "email").TTL().ToCql() i := qb.Insert("person").Columns("first_name", "last_name", "email")
stmt, names := qb.Batch().
Add("a.", i).
Add("b.", i).
ToCql()
q := gocqlx.Query(session.Query(stmt), names) q := gocqlx.Query(session.Query(stmt), names)
if err := q.BindStructMap(p, qb.M{"_ttl": qb.TTL(86400 * time.Second)}).Exec(); err != nil { b := struct {
log.Fatal(err) A Person
B Person
}{
A: Person{
"Igy",
"Citizen",
[]string{"ian.citzen@gocqlx_test.com"},
},
B: Person{
"Ian",
"Citizen",
[]string{"igy.citzen@gocqlx_test.com"},
},
}
if err := q.BindStruct(&b).Exec(); err != nil {
t.Fatal(err)
} }
} }
// Update // Get
{ {
p.Email = append(p.Email, "patricia1.citzen@gocqlx_test.com") var p Person
if err := gocqlx.Get(&p, session.Query("SELECT * FROM gocqlx_test.person WHERE first_name=?", "Patricia")); err != nil {
stmt, names := qb.Update("person").Set("email").Where(qb.Eq("first_name"), qb.Eq("last_name")).ToCql() t.Fatal("get:", err)
q := gocqlx.Query(session.Query(stmt), names)
if err := q.BindStruct(p).Exec(); err != nil {
log.Fatal(err)
} }
t.Log(p) // {Patricia Citizen [patricia.citzen@gocqlx_test.com patricia1.citzen@gocqlx_test.com]}
} }
// Select // Select
{ {
stmt, names := qb.Select("person").Where(qb.In("first_name")).ToCql() stmt, names := qb.Select("gocqlx_test.person").Where(qb.In("first_name")).ToCql()
q := gocqlx.Query(session.Query(stmt), names) q := gocqlx.Query(session.Query(stmt), names)
q.BindMap(qb.M{"first_name": []string{"Patricia", "John"}}) q.BindMap(qb.M{"first_name": []string{"Patricia", "Igy", "Ian"}})
if err := q.Err(); err != nil { if err := q.Err(); err != nil {
log.Fatal(err) t.Fatal(err)
} }
var people []Person var people []Person
if err := gocqlx.Select(&people, q.Query); err != nil { if err := gocqlx.Select(&people, q.Query); err != nil {
log.Fatal("select:", err) t.Fatal("select:", err)
} }
log.Println(people) t.Log(people) // [{Ian Citizen [igy.citzen@gocqlx_test.com]} {Igy Citizen [ian.citzen@gocqlx_test.com]} {Patricia Citizen [patricia.citzen@gocqlx_test.com patricia1.citzen@gocqlx_test.com]}]
// [{Patricia Citizen [patricia.citzen@com patricia1.citzen@com]} {John Doe [johndoeDNE@gmail.net]}]
} }
``` ```
For more details see [example test](https://github.com/scylladb/gocqlx/blob/master/example_test.go).
## Performance ## Performance
Gocqlx is fast, below is a benchmark result comparing `gocqlx` to raw `gocql` on Gocqlx is fast, this is a benchmark result comparing `gocqlx` to raw `gocql`
my machine, see the benchmark [here](https://github.com/scylladb/gocqlx/blob/master/benchmark_test.go). on a local machine. For query binding (insert) `gocqlx` is faster then `gocql`
thanks to smart caching, otherwise the performance is comparable.
For query binding gocqlx is faster as it does not require parameter rewriting
while binding. For get and insert the performance is comparable.
``` ```
BenchmarkE2EGocqlInsert-4 500000 258434 ns/op 2627 B/op 59 allocs/op BenchmarkE2EGocqlInsert-4 500000 258434 ns/op 2627 B/op 59 allocs/op
@@ -99,23 +117,4 @@ BenchmarkE2EGocqlSelect-4 30000 2588562 ns/op 34605
BenchmarkE2EGocqlxSelect-4 30000 2637187 ns/op 27718 B/op 951 allocs/op BenchmarkE2EGocqlxSelect-4 30000 2637187 ns/op 27718 B/op 951 allocs/op
``` ```
Gocqlx comes with automatic snake case support for field names and does not See the [benchmark here](https://github.com/scylladb/gocqlx/blob/master/benchmark_test.go).
require manual tagging. This is also fast, below is a comparison to
`strings.ToLower` function (`sqlx` default).
```
BenchmarkSnakeCase-4 10000000 124 ns/op 32 B/op 2 allocs/op
BenchmarkToLower-4 100000000 57.9 ns/op 0 B/op 0 allocs/op
```
Building queries is fast and low on allocations too.
```
BenchmarkCmp-4 3000000 464 ns/op 112 B/op 3 allocs/op
BenchmarkDeleteBuilder-4 10000000 214 ns/op 112 B/op 2 allocs/op
BenchmarkInsertBuilder-4 20000000 103 ns/op 64 B/op 1 allocs/op
BenchmarkSelectBuilder-4 10000000 214 ns/op 112 B/op 2 allocs/op
BenchmarkUpdateBuilder-4 10000000 212 ns/op 112 B/op 2 allocs/op
```
Enyoy!

View File

@@ -6,7 +6,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/gocql/gocql"
"github.com/scylladb/gocqlx" "github.com/scylladb/gocqlx"
"github.com/scylladb/gocqlx/qb" "github.com/scylladb/gocqlx/qb"
) )
@@ -19,14 +18,6 @@ CREATE TABLE IF NOT EXISTS gocqlx_test.person (
PRIMARY KEY(first_name, last_name) PRIMARY KEY(first_name, last_name)
)` )`
var placeSchema = `
CREATE TABLE IF NOT EXISTS gocqlx_test.place (
country text,
city text,
code int,
PRIMARY KEY(country, city)
)`
// Field names are converted to camel case by default, no need to add // Field names are converted to camel case by default, no need to add
// `db:"first_name"`, if you want to disable a filed add `db:"-"` tag. // `db:"first_name"`, if you want to disable a filed add `db:"-"` tag.
type Person struct { type Person struct {
@@ -35,88 +26,14 @@ type Person struct {
Email []string Email []string
} }
type Place struct {
Country string
City string
TelCode int `db:"code"`
}
func TestExample(t *testing.T) { func TestExample(t *testing.T) {
session := createSession(t) session := createSession(t)
defer session.Close() defer session.Close()
mustExec := func(q *gocql.Query) {
if err := q.Exec(); err != nil {
t.Fatal("query:", q, err)
}
}
// Fill person table.
{
if err := createTable(session, personSchema); err != nil { if err := createTable(session, personSchema); err != nil {
t.Fatal("create table:", err) t.Fatal("create table:", err)
} }
q := session.Query("INSERT INTO gocqlx_test.person (first_name, last_name, email) VALUES (?, ?, ?)")
mustExec(q.Bind("Jason", "Moiron", []string{"jmoiron@jmoiron.net"}))
mustExec(q.Bind("John", "Doe", []string{"johndoeDNE@gmail.net"}))
q.Release()
}
// Fill place table.
{
if err := createTable(session, placeSchema); err != nil {
t.Fatal("create table:", err)
}
q := session.Query("INSERT INTO gocqlx_test.place (country, city, code) VALUES (?, ?, ?)")
mustExec(q.Bind("United States", "New York", 1))
mustExec(q.Bind("Hong Kong", "", 852))
mustExec(q.Bind("Singapore", "", 65))
q.Release()
}
// Query the database, storing results in a []Person (wrapped in []interface{}).
{
var people []Person
if err := gocqlx.Select(&people, session.Query("SELECT * FROM gocqlx_test.person")); err != nil {
t.Fatal("select:", err)
}
t.Log(people)
// [{John Doe [johndoeDNE@gmail.net]} {Jason Moiron [jmoiron@jmoiron.net]}]
}
// Get a single result.
{
var jason Person
if err := gocqlx.Get(&jason, session.Query("SELECT * FROM gocqlx_test.person WHERE first_name=?", "Jason")); err != nil {
t.Fatal("get:", err)
}
t.Log(jason)
// Jason Moiron [jmoiron@jmoiron.net]}
}
// Loop through rows using only one struct.
{
var place Place
iter := gocqlx.Iter(session.Query("SELECT * FROM gocqlx_test.place"))
for iter.StructScan(&place) {
t.Log(place)
}
if err := iter.Close(); err != nil {
t.Fatal("iter:", err)
}
iter.ReleaseQuery()
// {Hong Kong 852}
// {United States New York 1}
// {Singapore 65}
}
// Query builder, using DSL to build queries, using `:name` as the bindvar.
{
p := &Person{ p := &Person{
"Patricia", "Patricia",
"Citizen", "Citizen",
@@ -170,12 +87,12 @@ func TestExample(t *testing.T) {
B Person B Person
}{ }{
A: Person{ A: Person{
"Ian", "Igy",
"Citizen", "Citizen",
[]string{"ian.citzen@gocqlx_test.com"}, []string{"ian.citzen@gocqlx_test.com"},
}, },
B: Person{ B: Person{
"Igy", "Ian",
"Citizen", "Citizen",
[]string{"igy.citzen@gocqlx_test.com"}, []string{"igy.citzen@gocqlx_test.com"},
}, },
@@ -186,12 +103,23 @@ func TestExample(t *testing.T) {
} }
} }
// Get
{
var p Person
if err := gocqlx.Get(&p, session.Query("SELECT * FROM gocqlx_test.person WHERE first_name=?", "Patricia")); err != nil {
t.Fatal("get:", err)
}
t.Log(p)
// {Patricia Citizen [patricia.citzen@gocqlx_test.com patricia1.citzen@gocqlx_test.com]}
}
// Select // Select
{ {
stmt, names := qb.Select("gocqlx_test.person").Where(qb.In("first_name")).ToCql() stmt, names := qb.Select("gocqlx_test.person").Where(qb.In("first_name")).ToCql()
q := gocqlx.Query(session.Query(stmt), names) q := gocqlx.Query(session.Query(stmt), names)
q.BindMap(qb.M{"first_name": []string{"Patricia", "John"}}) q.BindMap(qb.M{"first_name": []string{"Patricia", "Igy", "Ian"}})
if err := q.Err(); err != nil { if err := q.Err(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -202,8 +130,7 @@ func TestExample(t *testing.T) {
} }
t.Log(people) t.Log(people)
// [{Patricia Citizen [patricia.citzen@gocqlx_test.com patricia1.citzen@gocqlx_test.com]} {John Doe [johndoeDNE@gmail.net]}] // [{Ian Citizen [igy.citzen@gocqlx_test.com]} {Igy Citizen [ian.citzen@gocqlx_test.com]} {Patricia Citizen [patricia.citzen@gocqlx_test.com patricia1.citzen@gocqlx_test.com]}]
}
} }
// Named queries, using `:name` as the bindvar. // Named queries, using `:name` as the bindvar.