objectdb

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Feb 20, 2024 License: MIT Imports: 8 Imported by: 0

README

ObjectDB

ObjectDB is a document-oriented NoSQL database for Go.

Features

  • Embedded: No database server required
  • Document-oriented: Store and query structs as JSON documents
  • Tiny: Simple and lightweight

Implementation

Internally, ObjectDB uses the Pebble LSM key-value store as its storage engine.

Installation

go get github.com/boonsuen/objectdb

Database and Collection

ObjectDB stores documents in collections. A collection is a set of documents. A database can have multiple collections.

Open a Database
db, err := objectdb.Open("db")
if err != nil {
  log.Fatal(err)
  return
}
defer db.Close()
Insert Documents

Collections are created implicitly when a document is inserted into a collection. Each document is identified by a unique UUID, which is added to the document as the _id field.

Insert a document into a collection:

type Employee struct {
  Name string      `json:"name"`
  Age  json.Number `json:"age"`
}

employee := Employee{
  Name: "John",
  Age:  "30",
}

id, err := db.InsertOne("employees", employee)
if err != nil {
  log.Fatal(err)
}

Insert multiple documents into a collection:

type Address struct {
  Postcode string `json:"postcode"`
}

type Restaurant struct {
  Name    string  `json:"name"`
  Cuisine string  `json:"cuisine"`
  Address Address `json:"address"`
}

newRestaurants := []Restaurant{
  {"Restaurant A", "Italian", Address{"80000"}},
  {"Restaurant B", "Fast Food", Address{"10000"}},
  {"Restaurant C", "Fast Food", Address{"10000"}},
  {"Restaurant D", "Fast Food", Address{"10000"}},
}

ids, err := db.InsertMany("restaurants", newRestaurants)
if err != nil {
  log.Fatalf("error inserting restaurants: %v", err)
  return
} else {
  log.Printf("Inserted restaurants' IDs: %v", ids)
}

Queries

Find a Document

A single document in a collection can be retrieved by using the FindOne or FindOneByID method. Use the Unmarshal method to convert the document to a struct.

doc, err := db.FindOneById("employees", id)
if err != nil {
  log.Fatal(err)
}

employee := Restaurant{}
err = objectdb.Unmarshal(doc, &employee)
if err != nil {
  log.Fatalf("error unmarshalling restaurant: %v", err)
  return
}

FindOne returns the first matching document. It's similar to using FindMany with a limit of 1.

// Find one employee with the age of 30
employee, err := db.FindOne("employees", objectdb.Query{
  {"AND", []objectdb.Condition{
    {Path: "age", Operator: "=", Value: 30},
  }},
})
Find Multiple Documents

To find multiple matching documents in a collection, use the FindMany method.

With empty query and options, it returns all documents in the collection.

employees, err := db.FindMany("employees", objectdb.Query{}, objectdb.Options{})
Limiting

The Options struct specifies the limit of the number of matching documents to return.

objectdb.Options{Limit: 2}
Filtering

The Query struct specifies the conditions to filter the documents.

The following example finds 2 Fast Food restaurants with the postcode of 10000.

resQuery := objectdb.Query{
  {"AND", []objectdb.Condition{
    {Path: "cuisine", Operator: "=", Value: "Fast Food"},
    {Path: "address.postcode", Operator: "=", Value: "10000"},
  }},
}

ffRestaurants, err := db.FindMany("restaurants", resQuery, objectdb.Options{Limit: 2})

The query accepts multiple conditions. The AND and OR operators can be used to combine the conditions. Top-level conditions (each element in the Query slice) are implicitly combined with the AND operator. Only two levels of nesting are supported.

query := objectdb.Query{
  {"AND", []objectdb.Condition{
    {Path: "name", Operator: "=", Value: "John"},
    {Path: "age", Operator: ">=", Value: "27"},
  }},
  {"OR", []objectdb.Condition{
    {Path: "address.city", Operator: "=", Value: "NY"},
    {Path: "address.postcode", Operator: "=", Value: "10000"},
  }},
}

Query above is equivalent to the following SQL where clause:

WHERE (name = 'John' AND age >= 27) AND (address.city = 'NY' OR address.postcode = '10000')

Delete Documents

Delete a Document

To delete a document, use the DeleteOneById method.

err = db.DeleteOneById("collectionName", id)

Indexing

ObjectDB keep tracks of the path-value pairs of the documents in a index. This allows for efficient querying of documents for certain queries. A search will fall back to a full collection scan when it is not possible to solely rely on the index to satisfy the query.

Aside from querying using the Find methods, ObjectDB also supports full-text search that scales well with large collections.

To allow full-text search on a field, annotate the field with the textIndex tag. It will be indexed and its text content can be searched in a full-text search query. Note that the field must be of string type.

type Restaurant struct {
  Name    string  `json:"name" objectdb:"textIndex"`
  Cuisine string  `json:"cuisine" objectdb:"textIndex"`
}

To perform a full-text search, use the Search method.

documents, err := db.Search("collectionName", "search query")

Documentation

Index

Constants

View Source
const (
	EQ  = "="
	NE  = "!="
	GT  = ">"
	GTE = ">="
	LT  = "<"
	LTE = "<="
)

Comparison operators

Variables

View Source
var (
	ErrDuplicateKey      = errors.New("duplicate key")           // A document with the same key already exists
	ErrNoDocuments       = errors.New("no documents found")      // No documents are found for a filter/query
	ErrDocumentNotExists = errors.New("document does not exist") // A document does not exist given an ID
)

Functions

func Unmarshal

func Unmarshal(doc Document, v interface{}) error

Unmarshal a document into a struct

Types

type Condition

type Condition struct {
	Path     string
	Operator string
	Value    interface{}
}

type DB

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

func Open

func Open(path string) (*DB, error)

Open opens the underlying storage engine

func (*DB) Clear

func (db *DB) Clear() error

Clear all data in the store and index

func (*DB) Close

func (db *DB) Close() error

Close closes the underlying storage engine

func (*DB) DeleteOneById

func (db *DB) DeleteOneById(collectionName, id string) error

func (*DB) FindMany

func (db *DB) FindMany(collectionName string, query Query, options Options) ([]Document, error)

func (*DB) FindOne

func (db *DB) FindOne(collectionName string, query Query) (Document, error)

func (*DB) FindOneById

func (db *DB) FindOneById(collectionName, id string) (Document, error)

func (*DB) InsertMany

func (db *DB) InsertMany(collectionName string, documents []interface{}) ([]string, error)

func (*DB) InsertOne

func (db *DB) InsertOne(collectionName string, document interface{}) (string, error)

func (*DB) PrintIndex

func (db *DB) PrintIndex() error

Pretty print all the key value pairs in the index

func (*DB) Search

func (db *DB) Search(collectionName, text string) ([]Document, error)

type Document

type Document map[string]interface{}

type Options

type Options struct {
	Limit int
}

type Query

type Query []struct {
	Operator string // AND or OR
	Operands []Condition
}

Directories

Path Synopsis
fts

Jump to

Keyboard shortcuts

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