It's more about the databases than Go, but the samples are in Go, so
The proper way to model, store and query data. The sql language is standardized by ISO 9075 and the language was based in a theory field called relational algebra and the structures can be better organized by the normal forms
SQL database implementations usually also offers ACID properties and excel offering reliable, flexible and correct information extracted from stored data.
The following sample creates a table in a relational database (sqlite):
func GetConnection() *sql.DB {
fmt.Println("get connection for relational database")
db, err := sql.Open("sqlite3", "./todo.db")
if err != nil {
log.Fatal(err)
}
init := `
create table if not exists todo(
id integer primary key autoincrement,
description text,
done boolean,
created timestamp,
updated timestamp
);
`
_, _ = db.Exec(init)
return db
}
It's possible to store data this way:
func Insert(db *sql.DB, todo *model.Todo) int64 {
result, _ := db.Exec(`
insert into todo (description,done,created,updated)
values(?,?,?,?)
`, todo.Description, todo.Done, todo.Created, todo.Updated)
id, _ := result.LastInsertId()
return id
}
Check the full source code here.
This kind of database takes a different approach and instead of tables and schemas we have just documents.
Schemaless documents are easier to evolve, assuming that data has a fluid nature it copes well with such changes.
I also do some other compromises, like the eventual consistency instead of ACID properties from a relational database.
Denormalization is not a key feature on document databases but it's pretty common practice.
This kind of database became quite popular in early 2000 decade, when internet applications started to suffer from database bottlenecks and everyone started to blame relational databases. The term NoSQL became a thing, although it goes re-branded as "Not Only SQL" nowadays.
The following sample code creates a connection with a document database and stores a value:
func GetConnection() *c.DB {
fmt.Println("get connection for document-based database")
db, err := c.Open("todo-doc")
if err != nil {
log.Panic(err)
}
_ = db.CreateCollection("todos")
return db
}
func Insert(db *c.DB, todo *model.Todo) string {
todo.Id = todo.Created.Unix() // It's an insert
doc := c.NewDocumentOf(todo.ToMap())
id, _ := db.InsertOne("todos", doc)
return id
}
Check the full source code here.
This kind of database is simpler and has a different approach on retrieving data stored: instead of query columns or properties, all you can query is the keys. Data itself is opaque and the keys are supposed to be rich enough to perform detailed queries of what is needed.
This limitation usually comes with performance gains.
Usually, key/value databases goes with document-databases regarding ACID properties, but it's not a rule. It's also common to get mistakenly a key/value database as a document database because both are schemaless and some implementations of key/value also offers ways to query documents inside values.
The following sample code creates a connection with a key/value database and stores a value:
func GetConnection() *leveldb.DB {
fmt.Println("get connection for key/value database")
db, err := leveldb.OpenFile("todo-kv", nil)
if err != nil {
log.Panic(err)
}
return db
}
func Insert(db *leveldb.DB, todo *model.Todo) int64 {
todo.Id = todo.Created.Unix()
_todo, _ := json.Marshal(todo)
_ = db.Put([]byte(todo.Created.Format(time.RFC3339)), _todo, nil)
return todo.Id
}
Check the full source code here.
It depends of your needs.
Key/Value ones are great for time-series or big throughput of data to be analyzed later.
Document, highly available, databases also are good for big data volumes and if it's ok some data loss either by data volume or eventual document evolution.
If you care about the data and it's not ok to suffer any data loss, or if you plan to extract some intelligence, some information from the data then you get a full-featured relational database, it will make the task easier.
When it comes to choose the right database keep in mind that it's not an exclusive choice.
Most real-world mission-critical systems often employs more than one of these technologies to perform distinct jobs.
So, make sure to choose wisely which database will solve which problem.
Happy hacking!