ngorm

package module
v0.0.0-...-9ed695e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2017 License: MIT Imports: 21 Imported by: 0

README

IMPORTANT : THIS PROJECT HAS MOVED TO https://github.com/ngorm

NGORM

The fork of gorm,The fantastic ORM( Object Relational Mapping) library for Golang, that focus on

  • Performance
  • Maintainability
  • Modularity
  • Battle testing
  • Extensibility
  • Safety
  • Developer friendly for real

Build Status Coverage Status GoDoc Go Report Card

Overview

  • Full-Featured ORM (almost)
  • Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
  • Callbacks (Before/After Create/Save/Update/Delete/Find)
  • Preloading (eager loading)
  • Transactions
  • Composite Primary Key
  • SQL Builder
  • Auto Migrations
  • Logger
  • Extendible, write Plugins based on NGORM hooks
  • Every feature comes with tests
  • Developer Friendly

Documentation https://godoc.org/github.com/gernest/ngorm

Database support

  • ql
  • postgresql
  • mysql
  • mssql
  • sqlite

Motivation

I wanted to contribute to gorm by improving performance , I ended up being enlightened.

Installation

go get -u github.com/gernest/ngorm

Usage

See GUIDE for getting started, examples and so much more.

FAQ

Why ngorm?

Seriously? Why not?

Can I use my gorm models?

Yep

Why there is no support for database x?

There are check boxes on this README. If you find the database is unchecked then it might be in a queue you can come back in the future and hopefully it should be there!

Theres is a board tracking database support I am a sole maintainer, I'm good in ql so that is why I'm maintaining support for it. If you are good in any of the databases, I can help you getting started so you can add support and help maintaining.

What is the difference between gorm and ngorm?

As the name implies, ngorm is based on gorm. An attempt to modernise gorm.These are some of the things that differentiate the two.

Oh! Wait, there is no much difference from the user point of view. I restructured the gorm source files. Removed the global state, reworked callbacks/hooks to be more straight forward and added support for ql database. The API is almost the same.

The bonus here is, it is easy to measure performance, and hence improve. The code base can easily be groked hence easy to contribute , also this comes with an extensive test suite to make sure that regressions can not be introduced without being detected.

Documentation

Overview

Package ngorm i a Go Object relation mapper that focus on performance, maintainability, modularity, battle testing, extensibility , safety and developer frindliness.

To achieve all of the goals, the project is divided into many components. The components are desined in a functional style API, whereby objects are explicitly passed around as arguments to functions that operate on them.

This tries to avoid defining methods on structs. This comes at a cost of limiting chaining, this cost is intentional. I intend to work really hard on improving performance and thus avoiding spaghetti is not an option.

Installation

You can install with go get

go get -u github.com/gernest/ngorm

The package is divided into two phases, Query building and Query execution phase.

Query Building

The subpackage engine exposes a structure named Engine. This structure has everything necessary to build a query. Most of the functions defined in this package subpackages operate on this struct by accepting it as the first argument.

Having this as a separate layer helps fine tuning the generated querries and also it make easy to test and very that the ORM is doing the right thing. So, the generated query can be easily optimised without adding a lot of overhead.

Query execution

This is s the phase where the generated sql query is executed. This phase is as generic as possible in a way that you can easily implement adoptes for non SQL database and still reap all the benefits of this package.

Table of Ccntents

The following are links to packages under this project.

WARNING: You will never be touching most of these packages. They are the building block of the high level API.

[engine] https://godoc.org/github.com/gernest/ngorm/engine

This is what drives the whole project, helps with query building and provides conveinet structure to help with query execution.

[scope] https://godoc.org/github.com/gernest/ngorm/scope

Functions to help with model manipulations.

[search] https://godoc.org/github.com/gernest/ngorm/search

Functions to help with search querries building.

[hooks] https://godoc.org/github.com/gernest/ngorm/hooks

Callbacks executed by ngorm. You can easily overide and provide custom ones to suit your needs.

[logger] https://godoc.org/github.com/gernest/ngorm/logger

The logger used by ngorm for logging. It is an interface, and a reference implementation is provided.

[dialects] https://godoc.org/github.com/gernest/ngorm/dialects

Adopts to different SQL databases supported by ngorm. For now ngorm support ql .

Chaining

The API supports methof chaining for a specific set of method.Be warned if you intend to chain anything don't forget to start with Begin method. This will help clear any context left after previous calls.

db.Begin().Model(User{}).AddIndex()
Example (BelongsTo)
//One to many relationship
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()

// Here one user can only have one profile. But one profile can belong
// to multiple users.
type Profile struct {
	model.Model
	Name string
}
type User struct {
	model.Model
	Profile   Profile
	ProfileID int
}
_, err = db.Automigrate(&User{}, &Profile{})
if err != nil {
	log.Fatal(err)
}
u := User{
	Profile: Profile{Name: "gernest"},
}

// Creating the user with the Profile. The relation will
// automatically be created and the user.ProfileID will be set to
// the ID of hte created profile.
err = db.Create(&u)
if err != nil {
	log.Fatal(err)
}
fmt.Println(u.ProfileID == int(u.Profile.ID) && u.ProfileID != 0)
Output:

true

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DB

type DB struct {
	// contains filtered or unexported fields
}

DB provide an API for interacting with SQL databases using Go data structures.

func Open

func Open(dialect string, args ...interface{}) (*DB, error)

Open opens a database connection and returns *DB instance., dialect is the name of the driver that you want to use. The underlying connections are handled by database/sql package. Arguments that are accepted by database/sql Open function are valid here.

Not all databases are supported. There is still an ongoing efforts to add more databases but for now the following are the databases supported by this library,

The drivers for the libraries must be imported inside your application in the same package as you invoke this function.

Example

import _ "github.com/cznic/ql/driver"  // imports ql driver
Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()
fmt.Println(db.Dialect().GetName())
Output:

ql-mem

func OpenWithOpener

func OpenWithOpener(opener Opener, dialect string, args ...interface{}) (*DB, error)

OpenWithOpener uses the opener to initialize the dialects and establish database connection. In fact Open does not do anything by itself, it just calls this function with the default Opener.

Please see Open function for details. The only difference is here you need to pass an Opener. See the Opener interface for details about what the Opener is and what it is used for.

func (*DB) AddIndex

func (db *DB) AddIndex(indexName string, columns ...string) (sql.Result, error)

AddIndex add index for columns with given name

func (*DB) AddIndexSQL

func (db *DB) AddIndexSQL(indexName string, columns ...string) (*model.Expr, error)

AddIndexSQL generates SQL to add index for columns with given name

func (*DB) AddUniqueIndex

func (db *DB) AddUniqueIndex(indexName string, columns ...string) (sql.Result, error)

AddUniqueIndex add unique index for columns with given name

func (*DB) Assign

func (db *DB) Assign(attrs ...interface{}) *DB

Assign assign result with argument regardless it is found or not

func (*DB) Attrs

func (db *DB) Attrs(attrs ...interface{}) *DB

Attrs initialize struct with argument if record not found

func (*DB) Automigrate

func (db *DB) Automigrate(models ...interface{}) (sql.Result, error)

Automigrate creates tables that map to models if the tables don't exist yet in the database. This also takes care of situation where the models's fields have been updated(changed)

Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()

type Bar struct {
	ID  int64
	Say string
}

type Bun struct {
	ID   int64
	Dead bool
}

_, err = db.Automigrate(&Bar{}, &Bun{})
if err != nil {
	log.Fatal(err)
}
var names []string
qdb := db.SQLCommon()
rows, err := qdb.Query("select Name  from __Table ")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = rows.Close() }()
for rows.Next() {
	var n string
	err = rows.Scan(&n)
	if err != nil {
		log.Fatal(err)
		fmt.Println(err)
	}
	names = append(names, n)
}
if err := rows.Err(); err != nil {
	log.Fatal(err)
}
sort.Strings(names)
for _, v := range names {
	fmt.Println(v)
}
Output:

bars
buns

func (*DB) AutomigrateSQL

func (db *DB) AutomigrateSQL(models ...interface{}) (*model.Expr, error)

AutomigrateSQL generates sql query for running migrations on models.

Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()

type Bar struct {
	ID  int64
	Say string
}

type Bun struct {
	ID   int64
	Dead bool
}

sql, err := db.AutomigrateSQL(&Bar{}, &Bun{})
if err != nil {
	log.Fatal(err)
}
fmt.Println(sql.Q)
Output:

BEGIN TRANSACTION;
	CREATE TABLE bars (id int64,say string ) ;
	CREATE TABLE buns (id int64,dead bool ) ;
COMMIT;

func (*DB) Begin

func (db *DB) Begin() *DB

Begin gives back a fresh copy of DB ready for chaining methods that operates on the same model..

func (*DB) Close

func (db *DB) Close() error

Close closes the database connection and sends Done signal across all goroutines that subscribed to this instance context.

func (*DB) Count

func (db *DB) Count(value interface{}) error

Count get how many records for a model

func (*DB) Create

func (db *DB) Create(value interface{}) error

Create creates a new record.

You can hijack the execution of the generated SQL by overiding model.HookCreateExec hook.

func (*DB) CreateSQL

func (db *DB) CreateSQL(value interface{}) (*model.Expr, error)

CreateSQL generates SQl query for creating a new record/records for value. This uses Hooks to allow more flexibility.

There is no error propagation. Each step/hook execution must pass. Any error indicate the end of the execution.

The hooks that are used here are all defined in model package as constants. You can easily overide them by using DB.SetCreateHook method.

model.BeforeCreate

If set, this is the first hook to be executed. The default hook that is used is defined in hooks.BeforeCreate. If by any chance the hook returns an error then execution is halted and the error is returned.

model.HookSaveBeforeAss

If the model value has association and this is set then it will be executed. This is useful if you also want to save associations.

model.HookUpdateTimestamp

New record needs to have CreatedAt and UpdatedAt properly set. This is excuted to update the record timestamps( The default hook for this assumes you used model.Model convention for naming the timestamp fields).

model.Create

The last hook to be executed.

NOTE: All the hooks must be tailored towards generating SQL not executing anything that might change the state of the table.

All the other hooks apart from model.Create should write SQQL gerries in e.Scope.Epxrs only model.Create hook should write to e.Scope.SQL.

The end query is wrapped under TRANSACTION block.

Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()
type Bar struct {
	ID  int64
	Say string
}

b := Bar{Say: "hello"}
sql, err := db.CreateSQL(&b)
if err != nil {
	log.Fatal(err)
}
fmt.Println(sql.Q)
fmt.Printf("$1=%v", sql.Args[0])
Output:

BEGIN TRANSACTION;
	INSERT INTO bars (say) VALUES ($1);
COMMIT;
$1=hello
Example (ExtraOptions)
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()
type Bar struct {
	ID  int64
	Say string
}

b := Bar{Say: "hello"}
sql, err := db.Set(model.InsertOptions, "ON CONFLICT").
	CreateSQL(&b)
if err != nil {
	log.Fatal(err)
}
fmt.Println(sql.Q)
fmt.Printf("$1=%v", sql.Args[0])
Output:

BEGIN TRANSACTION;
	INSERT INTO bars (say) VALUES ($1) ON CONFLICT;
COMMIT;
$1=hello

func (*DB) CreateTable

func (db *DB) CreateTable(models ...interface{}) (sql.Result, error)

CreateTable creates new database tables that maps to the models.

func (*DB) CreateTableSQL

func (db *DB) CreateTableSQL(models ...interface{}) (*model.Expr, error)

CreateTableSQL return the sql query for creating tables for all the given models. The queries are wrapped in a TRANSACTION block.

func (*DB) Delete

func (db *DB) Delete(value interface{}, where ...interface{}) error

Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition

func (*DB) DeleteSQL

func (db *DB) DeleteSQL(value interface{}, where ...interface{}) (*model.Expr, error)

DeleteSQL generates SQL to delete value match given conditions, if the value has primary key, then will including the primary key as condition

func (*DB) Dialect

func (db *DB) Dialect() dialects.Dialect

Dialect return the dialect that is used by DB

func (*DB) DropColumn

func (db *DB) DropColumn(column string) (sql.Result, error)

DropColumn drop a column

func (*DB) DropTable

func (db *DB) DropTable(models ...interface{}) (sql.Result, error)

DropTable drops tables that are mapped to models. You can also pass the name of the table as astring and it will be handled.

func (*DB) DropTableIfExists

func (db *DB) DropTableIfExists(values ...interface{}) error

DropTableIfExists drop table if it is exist

func (*DB) DropTableSQL

func (db *DB) DropTableSQL(models ...interface{}) (*model.Expr, error)

DropTableSQL generates sql query for DROP TABLE. The generated query is wrapped under TRANSACTION block.

func (*DB) ExecTx

func (db *DB) ExecTx(query string, args ...interface{}) (sql.Result, error)

ExecTx wraps the query execution in a Transaction. This ensure all operations are Rolled back in case the execution fials.

func (*DB) Find

func (db *DB) Find(out interface{}, where ...interface{}) error

Find find records that match given conditions

func (*DB) FindSQL

func (db *DB) FindSQL(out interface{}, where ...interface{}) (*model.Expr, error)

FindSQL generates SQL query for finding records that match given conditions

func (*DB) First

func (db *DB) First(out interface{}, where ...interface{}) error

First fets the first record and order by primary key.

BUG: For some reason the ql database doesnt order the keys in ascending order. So this uses DESC to get the real record instead of ASC , I will need to dig more and see.

func (*DB) FirstOrCreate

func (db *DB) FirstOrCreate(out interface{}, where ...interface{}) error

FirstOrCreate find first matched record or create a new one with given conditions (only works with struct, map conditions)

func (*DB) FirstOrInit

func (db *DB) FirstOrInit(out interface{}, where ...interface{}) error

FirstOrInit find first matched record or initialize a new one with given conditions (only works with struct, map conditions) https://jinzhu.github.io/gorm/curd.html#firstorinit

func (*DB) FirstSQL

func (db *DB) FirstSQL(out interface{}, where ...interface{}) (*model.Expr, error)

FirstSQL returns SQL query for retrieving the first record ordering by primary key.

func (*DB) Group

func (db *DB) Group(query string) *DB

Group specify the group method on the find

func (*DB) HasTable

func (db *DB) HasTable(value interface{}) bool

HasTable returns true if there is a table for the given value, the value can either be a string representing a table name or a ngorm model.

func (*DB) Having

func (db *DB) Having(query string, values ...interface{}) *DB

Having specify HAVING conditions for GROUP BY

func (*DB) Hooks

func (db *DB) Hooks() *hooks.Book

Hooks returns the hook book for this db instance.

func (*DB) Joins

func (db *DB) Joins(query string, args ...interface{}) *DB

Joins specify Joins conditions

func (*DB) Last

func (db *DB) Last(out interface{}, where ...interface{}) error

Last finds the last record and order by primary key.

BUG: For some reason the ql database doesnt order the keys in descending order despite the use of DESC, so this uses ASC but the keys are in descending order. order. So this uses DESC to get the real record instead of ASC , I will need

func (*DB) LastSQL

func (db *DB) LastSQL(out interface{}, where ...interface{}) (*model.Expr, error)

LastSQL returns SQL query for retrieving the last record ordering by primary key.

func (*DB) Limit

func (db *DB) Limit(limit interface{}) *DB

Limit specify the number of records to be retrieved

func (*DB) Model

func (db *DB) Model(value interface{}) *DB

Model sets value as the database model. This model will be used for future calls on the erturned DB e.g

db.Model(&user).Update("name","hero")

func (*DB) ModifyColumn

func (db *DB) ModifyColumn(column string, typ string) (sql.Result, error)

ModifyColumn modify column to type

func (*DB) NewEngine

func (db *DB) NewEngine() *engine.Engine

NewEngine returns an initialized engine ready to kick some ass.

func (*DB) Not

func (db *DB) Not(query interface{}, args ...interface{}) *DB

Not filter records that don't match current conditions, similar to `Where`

func (*DB) Offset

func (db *DB) Offset(offset interface{}) *DB

Offset specify the number of records to skip before starting to return the records

func (*DB) Omit

func (db *DB) Omit(columns ...string) *DB

Omit specify fields that you want to ignore when saving to database for creating, updating

func (*DB) Or

func (db *DB) Or(query interface{}, args ...interface{}) *DB

Or filter records that match before conditions or this one, similar to `Where`

func (*DB) Order

func (db *DB) Order(value interface{}, reorder ...bool) *DB

Order specify order when retrieve records from database, set reorder to `true` to overwrite defined conditions

db.Order("name DESC")
db.Order("name DESC", true) // reorder
db.Order(gorm.Expr("name = ? DESC", "first")) // sql expression

func (*DB) Pluck

func (db *DB) Pluck(column string, value interface{}) error

Pluck used to query single column from a model as a map

var ages []int64
db.Find(&users).Pluck("age", &ages)

func (*DB) Preload

func (db *DB) Preload(column string, conditions ...interface{}) *DB

Preload preload associations with given conditions

db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)

func (*DB) RemoveIndex

func (db *DB) RemoveIndex(indexName string) error

RemoveIndex remove index with name

func (*DB) SQLCommon

func (db *DB) SQLCommon() model.SQLCommon

SQLCommon return SQLCommon used by the DB

func (*DB) Save

func (db *DB) Save(value interface{}) error

Save update value in database, if the value doesn't have primary key, will insert it

func (*DB) SaveSQL

func (db *DB) SaveSQL(value interface{}) (*model.Expr, error)

SaveSQL generates SQL query for saving/updating database record for value.

Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()
type Foo struct {
	ID    int64
	Stuff string
}

f := Foo{ID: 10, Stuff: "twenty"}
sql, err := db.SaveSQL(&f)
if err != nil {
	log.Fatal(err)
}
fmt.Println(sql.Q)
fmt.Printf("$1=%v\n", sql.Args[0])
fmt.Printf("$2=%v", sql.Args[1])
Output:

BEGIN TRANSACTION;
	UPDATE foos SET stuff = $1  WHERE id = $2;
COMMIT;
$1=twenty
$2=10

func (*DB) Select

func (db *DB) Select(query interface{}, args ...interface{}) *DB

Select specify fields that you want to retrieve from database when querying, by default, will select all fields; When creating/updating, specify fields that you want to save to database

func (*DB) Set

func (db *DB) Set(key string, value interface{}) *DB

Set sets scope key to value.

func (*DB) SingularTable

func (db *DB) SingularTable(enable bool)

SingularTable enables or diables singular tables name. By default this is diables, meaning table names are in plurar.

Model	| Plural table name
----------------------------
Session	| sessions
User	| users

Model	| Singular table name
----------------------------
Session	| session
User	| user

func (*DB) Table

func (db *DB) Table(name string) *DB

Table specify the table you would like to run db operations

func (*DB) Update

func (db *DB) Update(attrs ...interface{}) error

Update runs UPDATE queries.

func (*DB) UpdateColumn

func (db *DB) UpdateColumn(attrs ...interface{}) error

UpdateColumn update attributes without callbacks

func (*DB) UpdateColumns

func (db *DB) UpdateColumns(values interface{}) error

UpdateColumns update attributes without

func (*DB) UpdateSQL

func (db *DB) UpdateSQL(attrs ...interface{}) (*model.Expr, error)

UpdateSQL generates SQL that will be executed when you use db.Update

Example
db, err := Open("ql-mem", "test.db")
if err != nil {
	log.Fatal(err)
}
defer func() { _ = db.Close() }()
type Foo struct {
	ID    int64
	Stuff string
}

f := Foo{ID: 10, Stuff: "twenty"}
sql, err := db.Model(&f).UpdateSQL("stuff", "hello")
if err != nil {
	log.Fatal(err)
}
fmt.Println(sql.Q)
fmt.Printf("$1=%v\n", sql.Args[0])
fmt.Printf("$2=%v", sql.Args[1])
Output:

BEGIN TRANSACTION;
	UPDATE foos SET stuff = $1  WHERE id = $2;
COMMIT;
$1=hello
$2=10

func (*DB) Updates

func (db *DB) Updates(values interface{}, ignoreProtectedAttrs ...bool) error

Updates runs UPDATE query

func (*DB) UpdatesSQL

func (db *DB) UpdatesSQL(values interface{}, ignoreProtectedAttrs ...bool) (*model.Expr, error)

UpdatesSQL generates sql that will be used when you run db.UpdatesSQL

func (*DB) Where

func (db *DB) Where(query interface{}, args ...interface{}) *DB

Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions

type DefaultOpener

type DefaultOpener struct {
}

DefaultOpener implements Opener interface.

func (*DefaultOpener) Open

func (d *DefaultOpener) Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error)

Open opens up database connection using the database/sql package.

type Opener

type Opener interface {
	Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error)
}

Opener is an interface that is used to open up connection to SQL databases.

Directories

Path Synopsis
Package builder contains functions for SQL building.
Package builder contains functions for SQL building.
Package dialects defines a uniform interface for creating custom support for different SQL databases.
Package dialects defines a uniform interface for creating custom support for different SQL databases.
ql
Package ql exposes implementations and functions that enables ngorm to work with ql database.
Package ql exposes implementations and functions that enables ngorm to work with ql database.
Package engine defines the core structure that drives the ngorm API.
Package engine defines the core structure that drives the ngorm API.
Package fixture contains all stuctures neesessary for consinstent testing on a wide range of SQL database dialects.
Package fixture contains all stuctures neesessary for consinstent testing on a wide range of SQL database dialects.
Package hooks contains callbacks/hooks used by ngorm.
Package hooks contains callbacks/hooks used by ngorm.
Package logger defines interface for logging and provide a reference implementation.
Package logger defines interface for logging and provide a reference implementation.
Package model defines structures that are shared through out ngorm.
Package model defines structures that are shared through out ngorm.
Package regexes exposes pre compiled reqular expressions that are used by ngorm.
Package regexes exposes pre compiled reqular expressions that are used by ngorm.
Package scope defines functions that operates on engine.Engine and enables operating on model values easily.
Package scope defines functions that operates on engine.Engine and enables operating on model values easily.
Package search contains search functions for ngorm.
Package search contains search functions for ngorm.
Package util contains utility functions that are used in dirrent places of the codebase.
Package util contains utility functions that are used in dirrent places of the codebase.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL