Created
October 24, 2017 22:01
-
-
Save nathanborror/478ce582b51d54eb56e7d98a8ce83664 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Package entity implements functions to store and manipulate entity records | |
in a schema-less database. The current implementation supports Postgres only. | |
Inspired by: https://backchannel.org/blog/friendfeed-schemaless-mysql | |
Example implementation: | |
type Account struct { | |
ID string | |
Name string | |
Email string | |
Password string | |
Created time.Time | |
Modified time.Time | |
} | |
func (a *Account) EntityID() string { return a.ID } | |
func (a *Account) EntityKind() string { return "app_account" } | |
func (a *Account) EntityPrepare(id string, created, modified time.Time) { | |
a.ID = AccountID(id) | |
a.Created = created | |
a.Modified = modified | |
} | |
Writing a new entity: | |
newAccount := Account{"Nathan", "nathan@example.com", "n"} | |
ent := entity.NewEntity(newAccount, db) | |
ent.Write() | |
var account state.Account | |
ent.Unmarshal(&account) | |
*/ | |
package entity | |
import ( | |
"encoding/json" | |
"time" | |
"github.com/jmoiron/sqlx" | |
"github.com/jmoiron/sqlx/types" | |
uuid "github.com/satori/go.uuid" | |
) | |
const Schema = ` | |
CREATE TABLE IF NOT EXISTS entity ( | |
added_id serial PRIMARY KEY, | |
id uuid NOT NULL UNIQUE, | |
kind varchar(32) NOT NULL, | |
data jsonb, | |
created timestamp NOT NULL DEFAULT current_timestamp, | |
modified timestamp NOT NULL DEFAULT current_timestamp | |
)` | |
type EntityKind interface { | |
EntityID() string | |
EntityKind() string | |
EntityPrepare(id string, created, modified time.Time) | |
} | |
type Entity struct { | |
db *sqlx.DB | |
err error | |
AddedID int `db:"added_id"` | |
ID string | |
Kind string | |
Data types.JSONText | |
Created time.Time | |
Modified time.Time | |
} | |
func NewEntity(in EntityKind, db *sqlx.DB) *Entity { | |
e := Entity{ | |
db: db, | |
ID: in.EntityID(), | |
Kind: in.EntityKind(), | |
} | |
if e.ID == "" { | |
e.ID = uuid.NewV4().String() | |
} | |
if e.Created.IsZero() { | |
e.Created = time.Now().UTC() | |
} | |
if e.Modified.IsZero() { | |
e.Modified = time.Now().UTC() | |
} | |
in.EntityPrepare(e.ID, e.Created, e.Modified) | |
e.Data, e.err = json.Marshal(in) | |
return &e | |
} | |
func GetEntity(id string, db *sqlx.DB) *Entity { | |
var ent Entity | |
ent.err = db.Get(&ent, "SELECT * FROM entity WHERE id = $1", id) | |
return &ent | |
} | |
func (e *Entity) Write() { | |
e.Modified = time.Now().UTC() | |
tx := e.db.MustBegin() | |
tx.NamedExec(` | |
INSERT INTO entity | |
(id, kind, data) VALUES (:id, :kind, :data) | |
ON CONFLICT (id) DO UPDATE SET | |
(data, modified) = (EXCLUDED.data, EXCLUDED.modified)`, &e) | |
tx.Get(&e, "SELECT * FROM entity WHERE id = $1", e.ID) | |
e.err = tx.Commit() | |
} | |
func (e *Entity) Delete() { | |
tx := e.db.MustBegin() | |
tx.NamedExec("DELETE from entity WHERE id = :id", &e) | |
e.err = tx.Commit() | |
} | |
func (e *Entity) Unmarshal(dest EntityKind) { | |
e.err = e.Data.Unmarshal(dest) | |
dest.EntityPrepare(e.ID, e.Created, e.Modified) | |
} | |
func (e *Entity) Err() error { | |
return e.err | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment