March 16, 2025
+ 12
Using a Couchbase SDK, you can run a simple or more complex vector search against a Vector Search index.

You cannot use Vector Search on Windows platforms. You can use Vector Search on Linux from Couchbase Server version 7.6.0 and MacOS from version 7.6.2.

You can still use other features of the Search Service.

For more information about how the Search Service scores documents in search results, see Scoring for Search Queries.

Not all available Couchbase SDK languages are covered by the examples on this page.

For additional Vector Search examples, see the SDK documentation:

Prerequisites

Choose your preferred programming language to view the applicable prerequisites for the examples on this page.

Example: Searching for a Similar Color Vector

The sample dataset inside rgb.json has small embedding vectors inside the colorvect_l2 field. These embedding vectors describe a color using RGB values. For example, the color red has an embedding vector of [255, 0, 0].

The following example code searches for the color navy ([0.0, 0.0, 128.0]) in the rgb.json dataset:

package main

import (
    "fmt"
    "log"
    "time"
    "os"
    "github.com/couchbase/gocb/v2"
    // Include this import if you want to run a Search query using regular Search index features.
    // "github.com/couchbase/gocb/v2/search"
    "github.com/couchbase/gocb/v2/vector"
)
// Make sure to change CB_USERNAME, CB_PASSWORD, and CB_HOSTNAME to the username, password, and hostname for your database.
func main() {
    connstr := "couchbases://" + os.Getenv("CB_HOSTNAME") 
    username := os.Getenv("CB_USERNAME")
    password := os.Getenv("CB_PASSWORD")
	// Make sure to change the bucket, and scope names to match where you stored the sample data in your database. 
    bucket_name := "vector-sample"
    scope_name := "color"

    cluster, err := gocb.Connect(connstr, gocb.ClusterOptions{
        Authenticator: gocb.PasswordAuthenticator{
            Username: username,
            Password: password,
        },
        SecurityConfig: gocb.SecurityConfig{
            TLSSkipVerify: true, // Disables TLS certificate verification
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    bucket := cluster.Bucket(bucket_name)
    err = bucket.WaitUntilReady(5*time.Second, nil)
    if err != nil {
        log.Fatal(err)
    }

    scope := bucket.Scope(scope_name)

    request := gocb.SearchRequest{
        VectorSearch: vector.NewSearch(
            []*vector.Query{
			//  You can change the RGB values {0.0, 0.0, 128.0} to search for a different color.
            vector.NewQuery("colorvect_l2", []float32{0.0, 0.0, 128.0}),
        },
        nil,
       ),
    }
	// Change the limit value to return more results. Change the fields array to return different fields from your Search index.
    opts := &gocb.SearchOptions{Limit: 3, Fields: []string{"color"}}

	// Make sure to change the index name to match your Search index. 
    matchResult, err := scope.Search("color-index", request, opts)
    if err != nil {
        log.Fatal(err)
    }

    for matchResult.Next() {
        row := matchResult.Row()
        docID := row.ID
        var fields interface{}
        err := row.Fields(&fields)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Document ID: %s, Fields: %v\n", docID, fields)
    }

    if err = matchResult.Err(); err != nil {
        log.Fatal(err)
    }
}

Example: Semantic Search with Color Descriptions

The following code sample requires you to have a paid subscription to the OpenAI API to generate an embedding vector from a sample text string. For more information about pricing for the OpenAI API, see OpenAI’s Pricing page for embedding models.

The rgb.json sample data contains ready-made embedding vectors for each color’s description text. For an example of how to use a ready-made vector with Vector Search, see Run a Vector Search with the REST API and curl/HTTP or Run A Vector Search with the Server Web Console.

If you use the sample dataset inside rgb.json, you can use the OpenAI API to generate an embedding from any text string.

The following code generates an embedding vector with the question What color hides everything like the night?:

package main

import (
    "fmt"
    "log"
    "time"
    "os"
    "github.com/couchbase/gocb/v2"
	// Include this import if you want to run a Search query using regular Search index features.
    // "github.com/couchbase/gocb/v2/search"
    "github.com/couchbase/gocb/v2/vector"
    "bytes"
    "encoding/json"
    "io/ioutil"
    "net/http"
)

type OpenAIResponse struct {
    Data []struct {
        Embedding []float32 `json:"embedding"`
    } `json:"data"`
}

// generateVector makes a request to OpenAI's API to get an embedding vector for the given input text.
// Make sure to replace OPENAI_API_KEY with your own API Key.
func generateVector(inputText string) ([]float32, error) {
    openaiAPIKey := os.Getenv("OPENAI_API_KEY")
    if openaiAPIKey == "" {
        return nil, fmt.Errorf("OPENAI_API_KEY environment variable is not set")
    }

    requestBody, err := json.Marshal(map[string]interface{}{
        "input":  inputText,
        "model": "text-embedding-ada-002",
    })
    if err != nil {
        return nil, fmt.Errorf("error marshaling request body: %w", err)
    }

    request, err := http.NewRequest("POST", "https://api.openai.com/v1/embeddings", bytes.NewBuffer(requestBody))
    if err != nil {
        return nil, fmt.Errorf("error creating request: %w", err)
    }

    request.Header.Set("Content-Type", "application/json")
    request.Header.Set("Authorization", "Bearer "+openaiAPIKey)

    client := &http.Client{}
    response, err := client.Do(request)
    if err != nil {
        return nil, fmt.Errorf("error making request: %w", err)
    }
    defer response.Body.Close()

    if response.StatusCode != http.StatusOK {
        bodyBytes, _ := ioutil.ReadAll(response.Body)
        return nil, fmt.Errorf("API request failed with status %d: %s", response.StatusCode, string(bodyBytes))
    }

    var openAIResponse OpenAIResponse
    if err := json.NewDecoder(response.Body).Decode(&openAIResponse); err != nil {
        return nil, fmt.Errorf("error decoding response: %w", err)
    }

    if len(openAIResponse.Data) == 0 || len(openAIResponse.Data[0].Embedding) == 0 {
        return nil, fmt.Errorf("no embedding vector found in response")
    }

    return openAIResponse.Data[0].Embedding, nil
}
// Make sure to change CB_USERNAME, CB_PASSWORD, and CB_HOSTNAME to the username, password, and hostname for your database.
func main() {
    connstr := "couchbases://" + os.Getenv("CB_HOSTNAME") 
    username := os.Getenv("CB_USERNAME")
    password := os.Getenv("CB_PASSWORD")
	// Make sure to change the bucket, and scope names to match where you stored the sample data in your database. 
    bucket_name := "vector-sample"
    scope_name := "color"

    cluster, err := gocb.Connect(connstr, gocb.ClusterOptions{
        Authenticator: gocb.PasswordAuthenticator{
            Username: username,
            Password: password,
        },
        SecurityConfig: gocb.SecurityConfig{
            TLSSkipVerify: true, // Disables TLS certificate verification
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    bucket := cluster.Bucket(bucket_name)
    err = bucket.WaitUntilReady(5*time.Second, nil)
    if err != nil {
        log.Fatal(err)
    }

    scope := bucket.Scope(scope_name)
	// Change the question to whatever you want to ask.
    question := "What color hides everything like the night?"
    vect, err := generateVector(question)
    if err != nil {
        log.Fatalf("Error generating vector: %v", err)
    }

    request := gocb.SearchRequest{
        VectorSearch: vector.NewSearch(
            []*vector.Query{
            vector.NewQuery("embedding_vector_dot", vect),
        },
        nil,
       ),
    }
	// Change the limit value to return more results. Change the fields array to return different fields from your Search index.
    opts := &gocb.SearchOptions{Limit: 2, Fields: []string{"color","description"}}
	
	// Make sure to change the index name to match your Search index. 
    matchResult, err := scope.Search("color-index", request, opts)
    if err != nil {
        log.Fatal(err)
    }

    for matchResult.Next() {
        row := matchResult.Row()
        docID := row.ID
        var fields interface{}
        err := row.Fields(&fields)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Document ID: %s, Fields: %v\n", docID, fields)
    }

    if err = matchResult.Err(); err != nil {
        log.Fatal(err)
    }
}

Next Steps

You can create a child field or use the Quick Index editor to update your Vector Search index to include the description field with your search results.

For example, you could use the following JSON Vector Search index payload to create your Search index. It includes two child field mappings, colorvect_l2 and embedding_vector_dot on two different vector fields in the keyspace’s documents. It also adds 3 normal Search index fields (brightness, color, and description) to add more usable data to the Vector Search index:

{
  "type": "fulltext-index",
  "name": "vector-sample.color.color-index",
  "sourceType": "gocbcore",
  "sourceName": "vector-sample",
  "planParams": {
    "maxPartitionsPerPIndex": 512,
    "indexPartitions": 1
  },
  "params": {
    "doc_config": {
      "docid_prefix_delim": "",
      "docid_regexp": "",
      "mode": "scope.collection.type_field",
      "type_field": "type"
    },
    "mapping": {
      "analysis": {},
      "default_analyzer": "standard",
      "default_datetime_parser": "dateTimeOptional",
      "default_field": "_all",
      "default_mapping": {
        "dynamic": false,
        "enabled": false
      },
      "default_type": "_default",
      "docvalues_dynamic": false,
      "index_dynamic": false,
      "store_dynamic": false,
      "type_field": "_type",
      "types": {
        "color.rgb": {
          "dynamic": false,
          "enabled": true,
          "properties": {
            "brightness": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "index": true,
                  "name": "brightness",
                  "store": true,
                  "type": "number"
                }
              ]
            },
            "color": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "analyzer": "en",
                  "index": true,
                  "name": "color",
                  "store": true,
                  "type": "text"
                }
              ]
            },
            "colorvect_dot": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "dims": 3,
                  "index": true,
                  "name": "colorvect_dot",
                  "similarity": "dot_product",
                  "type": "vector"
                }
              ]
            },
            "colorvect_l2": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "dims": 3,
                  "index": true,
                  "name": "colorvect_l2",
                  "similarity": "l2_norm",
                  "type": "vector"
                }
              ]
            },
            "description": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "analyzer": "en",
                  "index": true,
                  "name": "description",
                  "store": true,
                  "type": "text"
                }
              ]
            },
            "embedding_vector_dot": {
              "dynamic": false,
              "enabled": true,
              "fields": [
                {
                  "dims": 1536,
                  "index": true,
                  "name": "embedding_vector_dot",
                  "similarity": "dot_product",
                  "type": "vector"
                }
              ]
            }
          }
        }
      }
    },
    "store": {
      "indexType": "scorch",
      "segmentVersion": 16
    }
  },
  "sourceParams": {}
}

Run the example in Example: Semantic Search with Color Descriptions again to see the description paragraphs in your results.

If you did not get the search results you were expecting, you can try to recreate your Vector Search index with the REST API.

Vector Search indexes can use the same settings and features as regular Search indexes. If you want to add additional fields and features to your index, see Customize a Search Index with the Web Console.