Skip to content
This repository has been archived by the owner on May 10, 2020. It is now read-only.

workshop-depot/dockage

Go Report Card GoDoc

dockage

This is an embedded document/json store based on badger key-value store.

word

  • Document id must be database-wide unique. There is no notion of grouping documents (tables, collections, buckets, etc).
  • id and rev are mandatory fields that must be present inside document json.

put document

First, make sure to close the db when it is no longer needed:

db := createDB()
defer db.Close()

Database operations are safe to be done concurrently. In fact it is a characteristic of badger that writes will get a performance boost when done concurrently.

Define a struct that has id and rev json tags. These two fields are not optional.

type post struct {
    ID   string    `json:"id"`
    Rev  string    `json:"rev"`
    By   string    `json:"by,omitempty"`
    Text string    `json:"text,omitempty"`
    At   time.Time `json:"at,omitempty"`
    Tags []string  `json:"tags,omitempty"`
}

Now to put a post document inside the database:

p := &post{
    ID:   "POST:001",
    By:   "Frodo Baggins",
    Text: "Awesome blog post!",
    At:   time.Now(),
    Tags: []string{"golang", "nosql"},
}

db.Put(p)

get document

Documents can be read from database, using their id.

var result []post
db.Get(&result, "POST:001")

delete document

Deleting documents is also done using their id.

db.Delete("POST:001")

Do not set the field with rev tag. It is used for optimistic concurrency and is filled automatically by Put() method.

query posts by day

Queries can be performed using Views, which are predefined queries. Queries must be defined right after opening the database.

This View emits the day of posts:

db.AddView(NewView("time-day",
	func(em Emitter, id string, doc interface{}) {
		c, ok := doc.(*post)
		if !ok {
			return
		}
		em.Emit([]byte(c.At.Format("2006-01-02")), nil)
		return
	}))

View functions will be executed after put operations, inside the same write transaction, right before commit. So the Views are also consistent. An Emitter, the id of the document and the document itself is passed to View function. time-day is a View that only indexes posts and ignores other type of documents.

Now we can query this view:

db.Query(Q{View: "time-day", Start: []byte("2018-05-20"), End: []byte("2018-05-30")})

Queries return the document id (view key) and other stuff generated by the view. For querying all documents, leave View field empty.

query posts by tag

Nice thing about views is that it is possible to index and query nested structs.

Let's define a view that allows us to query posts based on their tags:

db.AddView(NewView("tags",
	func(em Emitter, id string, doc interface{}) {
		c, ok := doc.(*post)
		if !ok {
			return
		}
		for _, v := range c.Tags {
			em.Emit([]byte(v), nil)
		}
		return
    }))

Here Emit() method is called multiple times, for each tag.

To query and find posts that are tagged with golang:

db.Query(Q{View: "tags", Start: []byte("golang"), Prefix: []byte("golang")})

Prefix is set because we do not need tags greater than golang - like gozoo!