couch

package module
v0.0.0-...-426bf55 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2016 License: MIT Imports: 9 Imported by: 1

README

Couch

Build Status GoDoc

WARNING: This project is not maintained anymore.

This is a CouchDB client for Go, it focuses on basic operations, proper conflict management, error handling and replication.

Not yet part of this are attachment handling, general statistics and optimizations, change detection and creating views. Most of the features are accessible using the generic Do() function, though.

Suggestions and critique are welcome.

Documentation

Documentation and API Reference can be found on godoc.org

Installation

Install couch using the "go get" command:

go get github.com/patrickjuchli/couch

The Go distribution is the only dependency.

Documentation

Overview

Package couch implements a client for a CouchDB database.

Version 0.1 focuses on basic operations, proper conflict management, error handling and replication. Not part of this version are attachment handling, general statistics and optimizations, change detection and creating views. Most of the features are accessible using the generic Do() function, though.

Getting started:

cred := couch.NewCredentials("user_notsosafe", "password_withoutssl")
s := couch.NewServer("http://127.0.0.1:5984", cred)
db := s.Database("MyDatabase")

if !db.Exists() {
  db.Create()
}

Basics

Every document in CouchDB has to be identifiable by a document id and a revision id. Two types already implement this interface called Identifiable: Doc and DynamicDoc. Doc can be used as an anonymous field in your own struct. DynamicDoc is a type alias for map[string]interface{}, use it when your documents have no implicit schema at all. To make code examples easier to follow, there will be no explicit error handling in these examples even though it's fully supported throughout the API.

type Person struct {
  couch.Doc
  Name string
}

Insert() will create a new document if it doesn't have an id yet:

p := &Person{Name : "Peter"}
db.Insert(p)

After the operation the final id and revision id will be written back to p. That's why you can now just edit p and call Insert() again which will save the same document under a new revision.

p.Name = "Anna"
db.Insert(p)

After this edit, p will contain the latest revision id. Note that it is possible that this second edit fails because someone else edited and saved the same document in the meantime. You will be notified of this in form of an error and you should then first retrieve the latest document revision to see the changes of this lost update:

db.Retrieve(p.ID, p)

CouchDB doesn't edit documents in-place but adds a complete revision for each edit. That's why you will be correctly informed of any lost update.

Conflicts

Because CouchDB supports multi-master replication of databases, it is possible that conflicts like the one described above can't be avoided. CouchDB is not going to interrupt replication because of a lost update.

Let's say you have two instances running, maybe a central one and a mobile one and both are kept in sync by replication. Now let's assume you edit a document on your mobile DB and someone else edits the same document on the central DB. After you've come online again, you use bi-directional replication to sync the databases. CouchDB will now create a branch structure for your document, similar to version control systems. Your document has two conflicting revisions and in this case they can't necessarily be resolved automatically. This client helps you with a number of methods to resolve such an issue quickly. Read more about the conflict model http://docs.couchdb.org/en/latest/replication/conflicts.html

Continuing with above example, replicate the database:

anotherDB := s.Database("sharedBackup")
db.ReplicateTo(anotherDB, false)

Now, on the other database, edit the document (note that it has the same id there):

p.Name = "AnotherAnna"
anotherDB.Insert(p)

Now edit the document on the first database. Retrieve it first to make sure it has the correct revision id:

db.Retrieve(p.ID, p)
p.Name = "LatestAnna"
db.Insert(p)

Now replicate anotherDB back to our first database:

anotherDB.ReplicateTo(db, false)

Now we have two conflicting versions of a document. Only you as the editor can decide whether "LatestAnna" or "AnotherAnna" is correct. To detect this conflict there are a number of methods. First, you can just ask a document:

conflict, _ := db.ConflictFor(p.ID)

You probably want to have a look at the revisions in your preferred format, use Revisions() to unmarshal the revision data into a slice of a custom data type:

var revs []Person
conflict.Revisions(&revs)

Pick one of the revisions or create a new document to solve the conflict:

solution := &Person{Name:"Anna"}
conflict.SolveWith(solution)

That's it. You can detect conflicts like these throughout your database using:

num := db.ConflictsCount()
docIDs := db.Conflicts()

Error handling

Errors returned by CouchDB will be converted into a Go error. Its regular Error() method will then return a combination of the shortform (e.g. bad_request) as well as the longer and more specific description. To be able to identify a specific error within your application, use ErrorType() to get the shortform only.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Design document for conflicts view
	ConflictsDesignID = "conflicts"

	// Name of the view to query documents with conflicts
	ConflictsViewID = "all"
)

Functions

func Do

func Do(url, method string, cred *Credentials, body, response interface{}) (*http.Response, error)

Generic CouchDB request. If CouchDB returns an error description, it will not be unmarshaled into response but returned as a regular Go error.

func ErrorType

func ErrorType(err error) string

ErrorType returns the shortform of a CouchDB error, e.g. bad_request. If the error didn't originate from CouchDB, the function will return an empty string.

Types

type Bulk

type Bulk struct {
	Docs         []Identifiable `json:"docs"`
	AllOrNothing bool           `json:"all_or_nothing"`
}

Bulk is a document container for bulk operations.

func (*Bulk) Add

func (bulk *Bulk) Add(doc Identifiable)

Add a document to a bulk of documents

func (*Bulk) Find

func (bulk *Bulk) Find(id, rev string) Identifiable

Find a document in a bulk of documents

type Conflict

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

Describes a conflict between different document revisions. Opaque type, use associated methods.

func (*Conflict) Revisions

func (c *Conflict) Revisions(v interface{})

Get all conflicting document revisions in a preferred format. It supports the same types for v as json.Unmarshal.

Revisions in a slice of structs:

var revs []MyStruct
conflict.Revisions(&revs)

Other examples:

var revs interface{}
var revs []map[string]interface{}

Note that map[string]interface{} will not work.

func (*Conflict) SolveWith

func (c *Conflict) SolveWith(finalDoc Identifiable) error

Solves a conflict with a final document. It will set the revision id of the document to the final revision id that CouchDB will report once the operation is complete.

If the operation is successful, the conflict c will no longer hold any information about the formerly conflicting revisions.

Be aware that while you solve a conflict, another party might have done so right before you. In this case of a lost update you will receive an error. You should then ask about the state of the conflict again using db.ConflictFor(myDocID).

type Credentials

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

Credentials represents access credentials.

func NewCredentials

func NewCredentials(user, password string) *Credentials

NewCredentials returns new credentials you can use for server and/or database operations.

type Database

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

Database represents a database of a CouchDB instance.

func (*Database) ConflictFor

func (db *Database) ConflictFor(docID string) (*Conflict, error)

Get conflicting revisions for a document id. Returns nil if there are no conflicts.

func (*Database) Conflicts

func (db *Database) Conflicts(forceView bool) (docIDs []string, err error)

Returns all conflicts in a database. To do so, a dedicated view is necessary at [db-url]/_design/conflicts/_view/all. If it doesn't exist and forceView is enabled, it will be automatically set up.

Note, that if the database is already large at that point, this operation can take a very long time. It's recommended to call this method or ConflictsCount() right after creating a new database.

func (*Database) ConflictsCount

func (db *Database) ConflictsCount(forceView bool) (int, error)

Returns the number of conflicts, sets up view if forceView is enabled. See db.Conflicts() for possible issues around creating a view.

func (*Database) Create

func (db *Database) Create() error

Create a new database on the CouchDB instance.

func (*Database) Cred

func (db *Database) Cred() *Credentials

Cred returns the credentials associated with the database. If there aren't any it will return the ones associated with the server.

func (*Database) Delete

func (db *Database) Delete(docID, revID string) error

Delete removes a document from the database.

func (*Database) DropDatabase

func (db *Database) DropDatabase() error

DropDatabase deletes a database.

func (*Database) Exists

func (db *Database) Exists() bool

Exists returns true if a database really exists.

func (*Database) HasView

func (db *Database) HasView(designID, viewID string) bool

Checks if a view really exists

func (*Database) Insert

func (db *Database) Insert(doc Identifiable) error

Insert a document as follows: If doc has an ID, it will edit the existing document, if not, create a new one. In case of an edit, the doc will be assigned the new revision id.

func (*Database) InsertBulk

func (db *Database) InsertBulk(bulk *Bulk, allOrNothing bool) (*Bulk, error)

InsertBulk inserts a bulk of documents at once. This transaction can have two semantics, all-or-nothing or per-document. See http://docs.couchdb.org/en/latest/api/database/bulk-api.html#bulk-documents-transaction-semantics After the transaction the method may return a new bulk of documents that couldn't be inserted. If this is the case you will still get an error reporting the issue.

func (*Database) Name

func (db *Database) Name() string

Name of database

func (*Database) Query

func (db *Database) Query(designID, viewID string, options map[string]interface{}) (*ViewResult, error)

Query a view with options, see http://docs.couchdb.org/en/latest/api/ddoc/views.html#db-design-design-doc-view-view-name

func (*Database) ReplicateTo

func (db *Database) ReplicateTo(target *Database, continuously bool) (*Replication, error)

Replicates given database to a target database. If the target database does not exist it will be created. The target database may be on a different host.

func (*Database) Retrieve

func (db *Database) Retrieve(docID string, doc Identifiable) error

Retrieve gets the latest revision of a document, the result will be written into doc

func (*Database) RetrieveRevision

func (db *Database) RetrieveRevision(docID, revID string, doc Identifiable) error

RetrieveRevision gets a specific revision of a document, the result will be written into doc

func (*Database) Server

func (db *Database) Server() *Server

Server returns the CouchDB instance the database is located on.

func (*Database) SetCred

func (db *Database) SetCred(c *Credentials)

SetCred sets the credentials used for operations with the database.

func (*Database) SyncWith

func (db *Database) SyncWith(target *Database, continuously bool) (*Sync, error)

Synchronizes two databases by setting up two replications, one from given database to target and from target to given database. If the target database does not exist it will be created. The target database may be on a different host.

This method may be convenient but note that it is not atomic: Sync means that this method will first replicate db to target and then target to db. If the first one fails, both fail. If the first one works but the second doesn't, the first one will have executed nonetheless. If the sync has been set up to be continuous, the first continuous replication will be cancelled if the second one fails.

func (*Database) URL

func (db *Database) URL() string

Url returns the absolute url to a database

type Doc

type Doc struct {
	ID  string `json:"_id,omitempty"`
	Rev string `json:"_rev,omitempty"`
}

Doc defines a basic struct for CouchDB documents. Add it as an anonymous field to your custom struct.

func (*Doc) IDRev

func (ref *Doc) IDRev() (id string, rev string)

Implement Identifiable

func (*Doc) SetIDRev

func (ref *Doc) SetIDRev(id string, rev string)

Implement Identifiable

type DynamicDoc

type DynamicDoc map[string]interface{}

DynamicDoc can be used for CouchDB documents without any implicit schema.

func (DynamicDoc) IDRev

func (m DynamicDoc) IDRev() (id string, rev string)

Implement Identifiable

func (DynamicDoc) SetIDRev

func (m DynamicDoc) SetIDRev(id string, rev string)

Implement Identifiable

type Identifiable

type Identifiable interface {

	// SetIDRev sets the document id and revision id
	SetIDRev(id string, rev string)

	// IDRev returns the document id and revision id
	IDRev() (id string, rev string)
}

Identifiable is the only interface a data structure must satisfy to be used as a CouchDB document.

type Replication

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

A replication from a source to a target

func (*Replication) Cancel

func (repl *Replication) Cancel() error

Cancel a continuously running replication

func (*Replication) Continuous

func (repl *Replication) Continuous() bool

Returns whether replication is running continuously or not

func (*Replication) IsActive

func (repl *Replication) IsActive() (bool, error)

IsRunning returns whether a replication is currently active or not.

func (*Replication) SessionID

func (repl *Replication) SessionID() string

func (*Replication) Source

func (repl *Replication) Source() *Database

Returns replication source

func (*Replication) Target

func (repl *Replication) Target() *Database

Returns replication target

type Server

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

Server represents a CouchDB instance.

func NewServer

func NewServer(url string, cred *Credentials) *Server

NewServer returns a handle to a CouchDB instance.

func (*Server) ActiveTasks

func (s *Server) ActiveTasks() ([]Task, error)

ActiveTasks returns all currently active tasks of a CouchDB instance.

func (*Server) Cred

func (s *Server) Cred() *Credentials

Cred returns credentials associated with a CouchDB instance.

func (*Server) Database

func (s *Server) Database(name string) *Database

Database returns a reference to a database. This method will not check if the database really exists.

func (*Server) URL

func (s *Server) URL() string

URL returns the host (including its port) of a CouchDB instance.

type Sync

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

A bidirectional replication

func (*Sync) Cancel

func (sync *Sync) Cancel() error

Cancel a continuously running sync

func (*Sync) IsActive

func (sync *Sync) IsActive() (bool, error)

IsActive returns whether a sync is active or not. A sync process consists of two replications. If one is active and the other isn't, you get an error message.

type Task

type Task map[string]interface{}

Task describes an active task running on an instance, like a continuous replication or indexing.

func (Task) HasReplicationID

func (t Task) HasReplicationID(id string) bool

HasReplicationID returns true if a task has a given replication id.

func (Task) IsReplication

func (t Task) IsReplication() bool

IsReplication returns true if a task represents a replication.

type ViewResult

type ViewResult struct {
	Offset uint64
	Rows   []ViewResultRow
}

Container for ViewResultRows

type ViewResultRow

type ViewResultRow struct {
	ID    string
	Key   interface{}
	Value interface{}
}

A single view result

func (*ViewResultRow) ValueInt

func (r *ViewResultRow) ValueInt() int

Jump to

Keyboard shortcuts

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