MapReduce Views Using the Go SDK with Couchbase Server

    +
    You can use MapReduce views to create queryable indexes in Couchbase Server.
    Although still maintained and supported for legacy use, Views date from the earliest days of Couchbase Server development, and as such are rarely the best choice over, say, our Query service if you are starting a fresh application. See our discussion document on the best service for you to use.

    MapReduce Views

    The normal CRUD methods allow you to look up a document by its ID. A MapReduce (view query) allows you to lookup one or more documents based on various criteria. MapReduce views are comprised of a map function that is executed once per document (this is done incrementally, so this is not run each time you query the view) and an optional reduce function that performs aggregation on the results of the map function. The map and reduce functions are stored on the server and written in JavaScript.

    MapReduce queries can be further customized during query time to allow only a subset (or range) of the data to be returned.

    See the Incremental MapReduce Views and Querying Data with Views sections of the general documentation to learn more about views and their architecture.

    Querying Views through the Go SDK

    Once you have a view defined, it can be queried from the Go SDK by using the ViewQuery method on a Bucket instance.

    The following example is the definition of a by_country view in a "landmarks-by-country" design document. This view checks whether a document is a landmark and has a country. If it does, it emits the landmark’s country into the index. This view allows landmarks to be queried for by country. For example, it’s now possible to ask the question "What countries start with U?"

    cluster, err := gocb.Connect("localhost", opts)
    if err != nil {
    	panic(err)
    }
    
    // get a bucket reference
    bucket := cluster.Bucket("travel-sample")
    
    // We wait until the bucket is definitely connected and setup.
    err = bucket.WaitUntilReady(5*time.Second, nil)
    if err != nil {
    	panic(err)
    }
    
    // creates the required view for the query
    viewMgr := bucket.ViewIndexes()
    createView(viewMgr)
    
    viewResult, err := bucket.ViewQuery("landmarks-by-country", "by_country", &gocb.ViewOptions{
    	StartKey:        "U",
    	Limit:           10,
    	Namespace:       gocb.DesignDocumentNamespaceDevelopment,
    	ScanConsistency: gocb.ViewScanConsistencyRequestPlus,
    })
    if err != nil {
    	panic(err)
    }

    The following example is the definition of a by_name view in a "landmarks-by-name" design document in the "travel-sample" sample dataset. This view checks whether a document is a landmark and has a name. If it does, it emits the landmark’s name into the index. This view allows landmarks to be queried for by its "name" field.

    landmarksResult, err := bucket.ViewQuery("landmarks-by-name", "by_name", &gocb.ViewOptions{
    	Key:             "Circle Bar",
    	Namespace:       gocb.DesignDocumentNamespaceDevelopment,
    	ScanConsistency: gocb.ViewScanConsistencyRequestPlus,
    })
    if err != nil {
    	panic(err)
    }

    Once a view result is obtained then it can be iterated over and the ID, keys and values extracted.

    for landmarksResult.Next() {
    	landmarkRow := landmarksResult.Row()
    	fmt.Printf("Document ID: %s\n", landmarkRow.ID)
    	var key string
    	err = landmarkRow.Key(&key)
    	if err != nil {
    		panic(err)
    	}
    
    	var landmark interface{}
    	err = landmarkRow.Value(&landmark)
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Printf("Landmark named %s has value %v\n", key, landmark)
    }
    
    // always check for errors after iterating
    err = landmarksResult.Err()
    if err != nil {
    	panic(err)
    }

    Take care to ensure you call Err after accessing rows to check for any errors returned.