upper.io/db.v3
Purpose
upper-db
provides a common interface to work with different data sources
using adapters that wrap mature SQL and NoSQL database drivers.
Its main purpose is to enable Go 1.8+ developers to perform database tasks (CRUD) in MySQL, PostgreSQL, SQLite, MSSQL, QL, or MongoDB.
> Coming from db.v2? we have a migration > guide that may come in handy.
Key concepts
A session is a database context created with the Open()
function featured in the adapter package.
A collection is a set of similar data type items identified with the name ‘table’ in SQL or ‘collection’ in NoSQL.
> The term ‘collection’ is used indistinctively by methods that work on both SQL and NoSQL. Such is the case of Collection()
, which creates a reference to the structures of any of these two database types.
A result set is a subset of items that match specific conditions. It is retrieved with Find()
and can be delimited or modified through different methods, like Update()
, Delete()
, Insert()
, All()
, or One()
.
The figure below ilustrates the session, collection, and result set concepts:
SQL/NoSQL Considerations
In order to use upper-db
efficiently, it is advisable that you:
- Understand the database you’re working with (object-relational or document-oriented)
- Use Go structs to describe data models. One struct per table is a good practice.
- Try to use methods applicable to both SQL and NoSQL first.
- Use SQL builder or raw SQL only when needed.
Installation
The upper.io/db.v3
package depends on the Go compiler and tools and is
compatible with Go 1.4 and above.
go get -v -u upper.io/db.v3
In the event this command does not work, you can always pull the data source directly from GitHub:
export UPPERIO_V3=$GOPATH/src/upper.io/db.v3
rm -rf $UPPERIO_V3
mkdir -p $UPPERIO_V3
git clone https://github.com/upper/db.git $UPPERIO_V3
cd $UPPERIO_V3
go build && go install
Supported databases
To see the complete list of supported adapters, click here.
Setup
Database Session
Import the adapter package into your application:
import (
"upper.io/db.v3/postgresql" // PostgreSQL package
)
Use the ConnectionURL
struct included in the adapter to create a DSN:
var settings = postgresql.ConnectionURL{
User: "john",
Password: "p4ss",
Address: "10.0.0.99",
Database: "myprojectdb",
}
fmt.Printf("DSN: %s", settings) // settings.String() is a DSN
Start a database session by passing the settings
value to the Open()
function of your adapter:
sess, err = postgresql.Open(settings) // sess is a db.Database type
...
> Once you finish to work with the database session, use Close()
to free all associated resources. Note that Go Servers are long-lived processes, you may never need to manually Close()
a session unless you don`t need it at all anymore.
err = sess.Close()
...
Collection Reference
Set a given database structure (table or collection):
users = sess.Collection("users") // Reference to a table named "users"
...
Mapping
Map exported fields to structs by adding a db
tag next to them:
type Person struct {
ID uint64 `db:"id,omitempty"` // Use `omitempty` for zero-valued
// fields that are not to be sent
// by the adapter.
Name string `db:"name"`
LastName string `db:"last_name"`
}
You can mix different db
struct tags, including those used to map JSON:
type Person struct {
ID uint64 `db:"id,omitempty" json:"id"`
Name string `db:"name" json:"name"`
...
Password string `db:"password,omitempty" json:"-"`
}
You can also set the adapter to ignore specific fields by means of a hyphen (-
):
type Person struct {
...
Token string `db:"-"` // Field to be skipped
}
> If mapping is not explicit, the adapter will perform a case-sensitive lookup of field names.
CRUD Functions
Retrieval
Get specific pieces of information (result sets) using Find()
:
// All the items in the collection are requested.
res = sess.Collection("people").Find()
...
// String-like syntax is accepted.
res = sess.Collection("people").Find("id", 25)
// Equality is the default operator but a different one can be used.
res = sess.Collection("people").Find("id >", 29)
// The `?` placeholder maps arguments by order.
res = sess.Collection("people").Find("id > ? AND id < ?", 20, 39)
// The And/Or methods can serve the same purpose.
res = sess.Collection("people").Find("id >", 20).And("id <", 39)
res = sess.Collection("people").Find("id", 20).Or("id", 21)
// Primary keys can also be passed as arguments.
res = sess.Collection("people").Find(20)
Constraints
You can narrow down result sets with db.Cond{}`:
cond = db.Cond{ // Equality is the default operator
"id": 36, // id = 36
}
cond = db.Cond{ // ...but a different one can be used.
"id >=": 36, // id >= 36
}
> Note that db.Cond
is a map[interface{}]interface{}
type and accepts multiple keys.
// John Smi% is to be located.
cond = db.Cond{
"name": "John",
"last_name LIKE": "Smi%",
}
Constraints can also be composed using db.Or()
/db.And()
:
db.Or(
db.Cond{"name": "John"}, // The name to be retrieved can be John or Jhon.
db.Cond{"name": "Jhon"},
)
db.And(
db.Cond{"age >": 21}, // The ages to be retrieved can range from 22 to 27.
db.Cond{"age <": 28},
)
Nesting values is another option:
```go
db.And(
db.And( // The result set will cover ages from 22 to 27
db.Cond{"age >": 21},
db.Cond{"age <": 28},
),
db.Or( // along with the names Joanna, John, or Jhon.
db.Cond{"name": "Joanna"},
db.Cond{"name": "John"},
db.Cond{"name": "Jhon"},
),
)
Results Limit and Order
You can determine the number of items you want to go through using Offset()
and
Limit()
:
res = col.Find(...)
...
err = res.Offset(2).Limit(8).All(&accounts) // The result set will consist of 8
// items and skip the first 2 rows.
...
Results can also be sorted according to a given value with OrderBy()
:
res = col.Find(...)
...
err = res.OrderBy("-last_name").All(&accounts) // Descending order by last name
...
> Remember that the total number of items in a result set can be calculated with Count()
:
res = col.Find(...)
...
c, err = res.Count()
...
fmt.Printf("There are %d items", c)
> Limit()
, Offset()
, and OrderBy()
affect exclusively the All()
and One()
methods.
Creation, Update, and Deletion
Insert, modify, and remove items in the result set.
To get the full picture on how to perform all CRUD tasks (starting right from the installation and setup steps), take the upper-db tour.
> The methods related to sessions, collections, and result sets are exemplified using the approaches ‘SQL/NoSQL’ and ‘SQL only’. For further reference about what applies in each case, click here.
Tips and tricks
Logging
The adapter can be set to print SQL statements and errors to standard output through the UPPERIO_DB_DEBUG
environment variable:
UPPERIO_DB_DEBUG=1 ./go-program
Underlying driver
In case you require methods that are only available from the underlying driver, you can use the db.Database.Driver()
. For instance, if you need the mgo.Session.Ping method, you can retrieve the underlying *mgo.Session
as an interface{}
, cast it into the appropriate type, and use Ping()
, as shown below:
drv = sess.Driver().(*mgo.Session) // The driver is cast into the
// the appropriate type.
err = drv.Ping()
You can do the same when working with an SQL adapter by changing the casting:
drv = sess.Driver().(*sql.DB)
rows, err = drv.Query("SELECT name FROM users WHERE age = ?", age)
License (MIT)
Copyright © 2013-2016 The upper.io/db authors.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.