Sample Code

The Go SDK supports creation and handling of extended attributes.

Subdocument Operations and Extended Attributes

A high-level summary of extended attributes can be found in Extended Attributes. Extended attributes are handled by means of extensions to the Subdocument API.

Go Extended Attributes Example

The following code demonstrates how extended attributes can be used. It assumes that Couchbase Server is established on localhost; that the Full Administrator username and password are Administrator and password respectively; and that the travel-sample bucket is installed. For information on installing the travel-sample bucket, see Sample Buckets.

package main

import (
    "github.com/couchbase/gocb"
    "fmt"
)

func main() {
    cluster, _ := gocb.Connect("couchbase://localhost")
    cluster.Authenticate(gocb.PasswordAuthenticator{
        Username: "Administrator",
        Password: "password",
    })

    bucket, _ := cluster.OpenBucket("travel-sample", "")

    // Add key-value pairs to hotel_10138, representing traveller-Ids and associated discount percentages
    bucket.MutateIn("hotel_10138", 0, 0).
        UpsertEx("discounts.jsmith123", "20", gocb.SubdocFlagXattr|gocb.SubdocFlagCreatePath).
        UpsertEx("discounts.pjones356", "30", gocb.SubdocFlagXattr|gocb.SubdocFlagCreatePath).
    // The following lines, "insert" and "remove", simply demonstrate insertion and
    // removal of the same path and value
        InsertEx("discounts.jbrown789", "25", gocb.SubdocFlagXattr|gocb.SubdocFlagCreatePath).
        RemoveEx("discounts.jbrown789",  gocb.SubdocFlagXattr).
        Execute()

    // Add key - value pairs to hotel_10142, again representing traveller - Ids and associated discount percentages
    bucket.MutateIn("hotel_10142", 0, 0).
        UpsertEx("discounts.jsmith123", "15", gocb.SubdocFlagXattr|gocb.SubdocFlagCreatePath).
        UpsertEx("discounts.pjones356", "10", gocb.SubdocFlagXattr|gocb.SubdocFlagCreatePath).
        Execute()

    q := gocb.NewN1qlQuery("SELECT id, META().id AS docID FROM `travel-sample`")
    rows, _ := bucket.ExecuteN1qlQuery(q, nil)

    var row struct {
        DocID string `json:"docID"`
    }
    for rows.Next(&row) {
        res, err := bucket.LookupIn(row.DocID).GetEx("discounts.jsmith123", gocb.SubdocFlagXattr).Execute()
        if err == nil {
            var discount string
            res.ContentByIndex(0, &discount)
            fmt.Printf("%s - %s\n", discount, row.DocID)
        }
    }
}

Virtual Extended Attributes Example

Using the Sub-Document API, Virtual XATTR can be used to fetch metadata about a document, via the $document virtual XATTR. A common use case is discovering documentation expiration metadata, or TTL:

package main

import (
	"fmt"
	"time"

	"github.com/couchbase/gocb"
)

func main() {
	cluster, err := gocb.Connect("couchbase://localhost")
	if err != nil {
		panic("Error connecting to cluster:" + err.Error())
	}
	err = cluster.Authenticate(gocb.PasswordAuthenticator{
		Username: "test",
		Password: "password",
	})
	if err != nil {
		panic("Error creating authenticator:" + err.Error())
	}

	bucket, err := cluster.OpenBucket("travel-sample", "")
	if err != nil {
		panic("Error opening bucket:" + err.Error())
	}

	key := "airline_17628"

	// Store the document
	_, err = bucket.Touch(key, 0, 300)
	if err != nil {
		panic("Error performing touch:" + err.Error())
	}

	lookup := bucket.LookupIn(key).GetEx("$document.exptime", gocb.SubdocFlagXattr)
	frag, err := lookup.Execute()
	if err != nil {
		panic(err)
	}

	var ttl uint32
	err = frag.Content("$document.exptime", &ttl)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Expiry value is %s\n", time.Unix(int64(ttl), 0).String())

	// Reset the expiry so that the document does not expire.
	_, err = bucket.Touch(key, 0, 0)
	if err != nil {
		panic("Error performing touch:" + err.Error())
	}

	// Multiple paths can be access at once via subdoc. It's limited to 16 paths and xattrs have to be first.
	multiLookup := bucket.LookupIn(key).
		GetEx("$document.exptime", gocb.SubdocFlagXattr).
		GetEx("$document.value_bytes", gocb.SubdocFlagXattr).
		Get("callsign")
	multiFrag, err := multiLookup.Execute()
	if err != nil {
		panic(err)
	}

	// Errors ignored for brevity.
	var size int
	var callsign string
	multiFrag.Content("$document.exptime", &ttl)
	multiFrag.Content("$document.value_bytes", &size)
	multiFrag.Content("callsign", &callsign)

	fmt.Printf("Expiry value is %s\n", time.Unix(int64(ttl), 0).String())
	fmt.Printf("Size is %d\n", size)
	fmt.Printf("Callsign is %s\n", callsign)
}