A Lightweight document-oriented NoSQL Database for the GO language

CloverDB is the work of a great friend and fellow Computer Scientist, Stefano Scafiti. It is a lightweight NoSQL database designed for being simple and easily maintainable, thanks to its small codebase. The project was inspired by tinyDB.

I contribute where I can to this great project. If you know the Go language, and love to support free open source software like myself, you are more than welcome to join the project on its GitHub page.

Features

  • Document oriented
  • Written in pure Golang
  • Simple and intuitive API
  • Easily maintainable

Why CloverDB?

CloverDB has been written for being easily maintainable. It trades performance with simplicity and is not intended to be an alternative to more performant databases such as MongoDB or MySQL. However, there are projects where running a separate database server may result in overkill, and, for simple queries, network delay may be the major performance bottleneck. For there scenario, CloverDB may be a more suitable alternative.

Database layout

CloverDB abstracts the way collections are stored on disk through the StorageEngine interface. The default implementation stores each collection in a separate text file, with each line corresponding to a different document. Each insert, update or delete operation rewrites from scratch the file corresponding to a given collection. Thus, the cost of such operations increases as the amount of data grows. In return, the absence of dead records in each file speed-ups iteration at query time. Also, to allow for fast document retrieval by id, each document’s size and location in the corresponding file are stored in an in-memory table.

If you are really concerned about performance, you could write your own implementation.

API usage

import (
	"log"
	c "github.com/ostafen/clover"
)

...

Create a new collection

db, _ := c.Open("clover-db")
db.CreateCollection("myCollection")

doc := c.NewDocument()
doc.Set("hello", "clover!")

docId, _ := db.InsertOne("myCollection", doc)

doc, _ = db.Query("myCollection").FindById(docId)
log.Println(doc.Get("hello"))

Query an existing database

db, _ := c.Open("../test-data/todos")

// find all completed todos belonging to users with id 5 and 8
docs, _ := db.Query("todos").Where(c.Field("completed").Eq(true).And(c.Field("userId").In(5, 8))).FindAll()

todo := &struct {
    Completed bool   `json:"completed"`
    Title     string `json:"title"`
    UserId    int    `json:"userId"`
}{}

for _, doc := range docs {
    doc.Unmarshal(todo)
    log.Println(todo)
}

Update and delete documents

db, _ := c.Open("../test-data/todos")

// mark all todos belonging to user with id 1 as completed
updates := make(map[string]interface{})
updates["completed"] = true

db.Query("todos").Where(c.Field("userId").Eq(1)).Update(updates)

// delete all todos belonging to users with id 5 and 8
db.Query("todos").Where(c.Field("userId").In(5,8)).Delete()