Docs Menu
Docs Home
/
Atlas
/

Vector Quantization

Atlas Vector Search supports automatic quantization of your float vector embeddings (both 32-bit and 64-bit). It also supports ingesting and indexing your pre-quantized scalar and binary vectors from certain embedding models.

Quantization is the process of shrinking full-fidelity vectors into fewer bits. It reduces the amount of main memory required to store each vector in an Atlas Vector Search index by indexing the reduced representation vectors instead. This allows for storage of more vectors or vectors with higher dimensions. Therefore, quantization reduces resource consumption and improves speed. We recommend quantization for applications with a large number of vectors, such as over 100,000.

Scalar quantization involves first identifying the minimum and maximum values for each dimension of the indexed vectors to establish a range of values for a dimension. Then, the range is divided into equally sized intervals or bins. Finally, each float value is mapped to a bin to convert the continuous float values into discrete integers. In Atlas Vector Search, this quantization reduces the vector embedding's RAM cost to about one fourth (1/3.75) of the pre-quantization cost.

Binary quantization involves assuming a midpoint of 0 for each dimension, which is typically appropriate for embeddings normalized to length 1 such as OpenAI's text-embedding-3-large. Then, each value in the vector is compared to the midpoint and assigned a binary value of 1 if it's greater than the midpoint and a binary value of 0 if it's less than or equal to the midpoint. In Atlas Vector Search, this quantization reduces the vector embedding's RAM cost to one twenty-fourth (1/24) of the pre-quantization cost. The reason it's not 1/32 is because the data structure containing the Hierarchical Navigable Small Worlds graph itself, separate from the vector values, isn't compressed.

When you run a query, Atlas Vector Search converts the float value in the query vector into a binary vector using the same midpoint for efficient comparison between the query vector and indexed binary vectors. It then rescores by reevaluating the identified candidates in the binary comparison using the original float values associated with those results from the binary index to further refine the results. The full fidelity vectors are stored in their own data structure on disk, and are only referenced during rescoring when you configure binary quantization or when you perform exact search against either binary or scalar quantized vectors.

Tip

The following table shows the requirements for automatically quantizing and ingesting quantized vectors.

Note

Atlas stores all floating-point values as the double data type internally; therefore, both 32-bit and 64-bit embeddings are compatible with automatic quantization without conversion.

Requirement
For int1 Ingestion
For int8 Ingestion
For Automatic Scalar Quantization
For Automatic Binary Quantization

Requires index definition settings

No

No

Yes

Yes

Requires BSON binData format

Yes

Yes

No

No

Storage on mongod

binData(int1)

binData(int8)

binData(float32)
array(double)
binData(float32)
array(double)

Supported Similarity method

euclidean

cosine
euclidean
dotProduct
cosine
euclidean
dotProduct
cosine
euclidean
dotProduct

Supported Number of Dimensions

Multiple of 8

1 to 8192

1 to 8192

1 to 8192

Supports ANN and ENN Search

Yes

Yes

Yes

Yes

You can configure Atlas Vector Search to automatically quantize float vector embeddings in your collection to reduced representation types, such as int8 (scalar) and binary in your vector indexes.

To set or change the quantization type, specify a quantization field value of either scalar or binary in your index definition. This triggers an index rebuild similar to any other index definition change. The specified quantization type applies to all indexed vectors and query vectors at query-time. You don't need to change your query as your query vectors are automatically quantized.

For most embedding models, we recommend binary quantization with rescoring. If you want to use lower dimension models that are not QAT, use scalar quantization because it has less representational loss and therefore, incurs less representational capacity loss.

Atlas Vector Search provides native capabilities for scalar quantization as well as binary quantization with rescoring. Automatic quantization increases scalability and cost savings for your applications by reducing the computational resources for efficient processing of your vectors. Automatic quantization reduces the RAM for mongot by 3.75x for scalar and by 24x for binary; the vector values shrink by 4x and 32x respectively, but Hierarchical Navigable Small Worlds graph itself does not shrink. This improves performance, even at the highest volume and scale.

We recommend automatic quantization if you have large number of full fidelity vectors, typically over 100,000 vectors. After quantization, you index reduced representation vectors without compromising the accuracy when retrieving vectors.

To enable automatic quantization:

1

In a new or existing Atlas Vector Search index, specify one of the following quantization types in the fields.quantization field for your index definition:

  • scalar: to produce byte vectors from float input vectors.

  • binary: to produce bit vectors from float input vectors.

If you specify automatic quantization on data that is not an array of float values, Atlas Vector Search silently ignores that vector instead of indexing it, and those vectors will be skipped. Since Atlas stores float values (both 32-bit and 64-bit) as the double type internally, embeddings from models that output either precision will work with automatic quantization.

2

The index should take about one minute to build. While it builds, the index is in an initial sync state. When it finishes building, you can start querying the data in your collection.

The specified quantization type applies to all indexed vectors and query vectors at query-time.

When you view your quantized index in the Atlas UI, the index size might appear larger than an index without quantization. This is because the Size metric represents the total data stored, which includes the Hierarchical Navigable Small Worlds graph (in memory), the quantized vectors (in memory), and the full-fidelity vectors (on disk). To estimate the amount of memory used by the index at query-time, refer to the Required Memory metric.

Atlas Vector Search also supports ingestion and indexing of scalar and binary quantized vectors from certain embedding models. If you don't already have quantized vectors, you can convert your embeddings to BSON BinData vectors with float32, int1, or int8 subtype.

We recommend ingesting quantized BSON binData vectors for the following use cases:

  • You need to index quantized vector output from embedding models.

  • You have a large number of float vectors and want to reduce the storage and WiredTiger footprint (such as disk and memory usage) in mongod.

BinData is a BSON data type that stores binary data. It compresses your vector embeddings and requires about three times less disk space in your cluster compared to embeddings that use a standard float32 array. To learn more, see Vector Compression.

This subtype also allows you to index your vectors with alternate types such as int1 or int8 vectors, reducing the memory needed to build the Atlas Vector Search index for your collection. It reduces the RAM for mongot by 3.75x for scalar and by 24x for binary; the vector values shrink by 4x and 32x respectively, but the Hierarchical Navigable Small Worlds graph itself doesn't shrink.

If you don't already have binData vectors, you can convert your embeddings to this format by using any supported driver before writing your data to a collection. The following procedure walks you through the steps for converting your embeddings to the BinData vectors with float32, int8, and int1 subtypes.

BSON BinData vectors with float32, int1, and int8 subtypes is supported by the following drivers:


➤ Use the Select your language drop-down menu to set the language of the procedure on this page.


To quantize your BSON binData vectors, you must have the following:

  • An Atlas cluster running MongoDB version 6.0.11, 7.0.2, or later.

    Ensure that your IP address is included in your Atlas project's access list.

  • Access to an embedding model that supports byte vector output.

    The outputs from the following embedding models can be used to generate BSON binData vectors with a supported MongoDB driver:

    Embedding Model Provider
    Embedding Model

    voyage-3-large

    embed-english-v3.0

    nomic-embed-text-v1.5

    jina-embeddings-v2-base-en

    mxbai-embed-large-v1

    Scalar quantization preserves recall for these models because these models are all trained to be quantization aware. Therefore, recall degradation for scalar quantized embeddings produced by these models is minimal even at lower dimensions like 384.

  • A terminal and code editor to run your Go project.

  • Go installed.

  • Java Development Kit (JDK) version 8 or later.

  • An environment to set up and run a Java application. We recommend that you use an integrated development environment (IDE) such as IntelliJ IDEA or Eclipse IDE to configure Maven or Gradle to build and run your project.

  • A terminal and code editor to run your Node.js project.

  • npm and Node.js installed.

  • An environment to run interactive Python notebooks such as VS Code or Colab.

The examples in this procedure use either new data or existing data and embeddings generated by using Voyage AI's voyage-3-large model. The example for new data uses sample text strings, which you can replace with your own data. The example for existing data uses a subset of documents without any embeddings from the listingsAndReviews collection in the sample_airbnb database, which you can replace with your own database and collection (with or without any embeddings).

Select the tab based on whether you want to quantize binData vectors for new data or for data you already have in your Atlas cluster.

1

In a terminal window, run the following commands to create a new directory named ingest-binary-vectors and initialize your project:

mkdir ingest-binary-vectors-project
cd ingest-binary-vectors-project
go mod init ingest-binary-vectors-project
2

Run the following command to install the MongoDB Go Driver. This operation might take a few minutes to complete.

go get go.mongodb.org/mongo-driver/v2/mongo

You must install Go v2.1 or later driver. If necessary, you can also install libraries from your embedding model provider. For examples in this tutorial, we will use the Voyage AI REST API to generate embeddings. Therefore, you don't need to install any additional libraries.

3
  1. To access the embedding model provider for generating and converting embeddings, set the environment variable for the embedding model provider's API key, if necessary.

    For using embeddings from Voyage AI, set up the VOYAGE_API_KEY environment variable.

    export VOYAGE_API_KEY="<VOYAGE-API-KEY>"
  2. To access Atlas cluster, set the MONGODB_URI environment variable.

    export MONGODB_URI="<CONNECTION-STRING>"

    Your connection string should be in the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
4

You can use an embedding model provider to generate float32, int8, and int1 embeddings for your data and then use the MongoDB Go driver to convert your native vector embedding to BSON vectors. The following sample code uses Cohere's embed API to generate full-precision vectors.

  1. Create a new file named GenerateAndConvertEmbeddings.go in your Go project.

    touch GenerateAndConvertEmbeddings.go
  2. Copy and paste the following code in the GenerateAndConvertEmbeddings.go file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Cohere's embed API.

    • Converts the embeddings to BSON binData vectors by using MongoDB Go driver.

    • Creates a file named embeddings.json and saves the data with embeddings in the file.

    GenerateAndConvertEmbeddings.go
    1package main
    2
    3import (
    4"bytes"
    5"context"
    6"encoding/json"
    7"fmt"
    8"io/ioutil"
    9"log"
    10"net/http"
    11"os"
    12
    13"go.mongodb.org/mongo-driver/v2/bson"
    14)
    15
    16// Sample data for embedding
    17var data = []string{
    18"The Great Wall of China is visible from space.",
    19"The Eiffel Tower was completed in Paris in 1889.",
    20"Mount Everest is the highest peak on Earth at 8,848m.",
    21"Shakespeare wrote 37 plays and 154 sonnets during his lifetime.",
    22"The Mona Lisa was painted by Leonardo da Vinci.",
    23}
    24
    25func main() {
    26apiKey := os.Getenv("VOYAGE_API_KEY")
    27if apiKey == "" {
    28log.Fatal("Ensure VOYAGE_API_KEY is set.")
    29}
    30
    31model := "voyage-3-large"
    32
    33// Generate embeddings for float, int8, and ubinary
    34floatEmbeddings, err := fetchEmbeddingsFromVoyage(data, apiKey, model, "float")
    35if err != nil {
    36log.Fatalf("Error fetching float embeddings: %v", err)
    37}
    38
    39int8Embeddings, err := fetchEmbeddingsFromVoyage(data, apiKey, model, "int8")
    40if err != nil {
    41log.Fatalf("Error fetching int8 embeddings: %v", err)
    42}
    43
    44ubinaryEmbeddings, err := fetchEmbeddingsFromVoyage(data, apiKey, model, "ubinary")
    45if err != nil {
    46log.Fatalf("Error fetching ubinary embeddings: %v", err)
    47}
    48
    49// Convert to BSON and store in JSON file
    50documents := convertEmbeddingsToBSON(data, floatEmbeddings, int8Embeddings, ubinaryEmbeddings)
    51
    52err = writeJSONToFile("embeddings.json", documents)
    53if err != nil {
    54log.Fatalf("Error writing embeddings to file: %v", err)
    55}
    56
    57fmt.Println("Embeddings successfully stored in embeddings.json")
    58}
    59
    60// Fetch embeddings using Voyage AI REST API
    61func fetchEmbeddingsFromVoyage(texts []string, apiKey string, model string, outputDType string) ([]map[string]interface{}, error) {
    62url := "https://api.voyageai.com/v1/embeddings"
    63
    64// Prepare request body
    65requestBody := map[string]interface{}{
    66"input": texts,
    67"model": model,
    68"output_dtype": outputDType,
    69"output_dimension": 1024,
    70"input_type": "document",
    71}
    72
    73requestBytes, err := json.Marshal(requestBody)
    74if err != nil {
    75return nil, fmt.Errorf("failed to marshal request body: %w", err)
    76}
    77
    78req, err := http.NewRequestWithContext(context.TODO(), "POST", url, bytes.NewBuffer(requestBytes))
    79if err != nil {
    80return nil, fmt.Errorf("failed to create HTTP request: %w", err)
    81}
    82
    83req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
    84req.Header.Set("Content-Type", "application/json")
    85
    86client := &http.Client{}
    87resp, err := client.Do(req)
    88if err != nil {
    89return nil, fmt.Errorf("failed to make API request: %w", err)
    90}
    91defer resp.Body.Close()
    92
    93if resp.StatusCode != http.StatusOK {
    94body, _ := ioutil.ReadAll(resp.Body)
    95return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
    96}
    97
    98var response struct {
    99Data []map[string]interface{} `json:"data"`
    100}
    101if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
    102return nil, fmt.Errorf("failed to parse API response: %w", err)
    103}
    104
    105return response.Data, nil
    106}
    107
    108// Convert embeddings to BSON binary vectors
    109func convertEmbeddingsToBSON(sentences []string, floatEmbeddings []map[string]interface{}, int8Embeddings []map[string]interface{}, ubinaryEmbeddings []map[string]interface{}) []bson.M {
    110var documents []bson.M
    111
    112for i, sentence := range sentences {
    113floatEmbedding := convertInterfaceToFloat32(floatEmbeddings[i]["embedding"].([]interface{}))
    114int8Embedding := convertInterfaceToInt8(int8Embeddings[i]["embedding"].([]interface{}))
    115ubinaryEmbedding := convertInterfaceToBytes(ubinaryEmbeddings[i]["embedding"].([]interface{}))
    116
    117floatVector := bson.NewVector(floatEmbedding)
    118int8Vector := bson.NewVector(int8Embedding)
    119ubinaryVector, err := bson.NewPackedBitVector(ubinaryEmbedding, 0)
    120if err != nil {
    121log.Fatalf("Error creating PackedBitVector: %v", err)
    122}
    123
    124document := bson.M{
    125"text": sentence,
    126"embeddings_float32": floatVector.Binary(),
    127"embeddings_int8": int8Vector.Binary(),
    128"embeddings_int1": ubinaryVector.Binary(),
    129}
    130documents = append(documents, document)
    131}
    132
    133return documents
    134}
    135
    136// Write JSON file from in-memory BSON documents
    137func writeJSONToFile(filename string, documents []bson.M) error {
    138file, err := os.Create(filename)
    139if err != nil {
    140return fmt.Errorf("failed to create file: %w", err)
    141}
    142defer file.Close()
    143
    144var jsonData []json.RawMessage
    145for _, document := range documents {
    146jsonBytes, err := bson.MarshalExtJSON(document, false, false)
    147if err != nil {
    148return fmt.Errorf("error marshaling BSON to JSON: %w", err)
    149}
    150jsonData = append(jsonData, jsonBytes)
    151}
    152
    153marshaledData, err := json.MarshalIndent(jsonData, "", " ")
    154if err != nil {
    155return fmt.Errorf("failed to marshal JSON: %w", err)
    156}
    157
    158_, err = file.Write(marshaledData)
    159if err != nil {
    160return fmt.Errorf("failed to write JSON to file: %w", err)
    161}
    162
    163return nil
    164}
    165
    166// Convert a slice of interfaces to a slice of float32
    167func convertInterfaceToFloat32(data []interface{}) []float32 {
    168f32s := make([]float32, len(data))
    169for i, v := range data {
    170f32s[i] = float32(v.(float64))
    171}
    172return f32s
    173}
    174
    175// Convert a slice of interfaces to a slice of int8
    176func convertInterfaceToInt8(data []interface{}) []int8 {
    177ints8 := make([]int8, len(data))
    178for i, v := range data {
    179switch val := v.(type) {
    180case int:
    181ints8[i] = int8(val)
    182case float64:
    183ints8[i] = int8(val)
    184default:
    185log.Fatalf("Unexpected type %T in int8 embedding at index %d", v, i)
    186}
    187}
    188return ints8
    189}
    190
    191// Convert a slice of interfaces to a slice of bytes
    192func convertInterfaceToBytes(data []interface{}) []byte {
    193bytes := make([]byte, len(data))
    194for i, v := range data {
    195switch val := v.(type) {
    196case int:
    197bytes[i] = byte(val)
    198case float64:
    199bytes[i] = byte(val)
    200default:
    201log.Fatalf("Unexpected type %T in ubinary embedding at index %d", v, i)
    202}
    203}
    204return bytes
    205}
  3. Replace the following placeholder value in the code and save the file.

    VOYAGE_API_KEY

    Your Voyage AI API key only if you didn't set the environment variable.

  4. Run the program using the following command.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run GenerateAndConvertEmbeddings.go
    Embeddings successfully stored in embeddings.json
  5. Verify the embeddings in the embeddings.json file.

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

5

You must upload your data and embeddings to a collection in your Atlas cluster and create an Atlas Vector Search index on the data to run $vectorSearch queries against the data.

  1. Create a new file named UploadDataAndCreateIndex.go in your Go project.

    touch UploadDataAndCreateIndex.go
  2. Copy and paste the following code in the UploadDataAndCreateIndex.go file.

    This code does the following:

    • Uploads the float32, int8, and int1 embeddings in the embeddings.json file to your Atlas cluster.

    • Creates an Atlas Vector Search index on the embeddings.float32, embeddings.int8, and embeddings.int1 fields.

    UploadDataAndCreateIndex.go
    1package main
    2
    3import (
    4"context"
    5"fmt"
    6"io/ioutil"
    7"log"
    8"time"
    9"os"
    10
    11"go.mongodb.org/mongo-driver/v2/bson"
    12"go.mongodb.org/mongo-driver/v2/mongo"
    13"go.mongodb.org/mongo-driver/v2/mongo/options"
    14)
    15
    16var (
    17mongodbURI = os.Getenv("MONGODB_URI")
    18dbName = "<DATABASE-NAME>"
    19collectionName = "<COLLECTION-NAME>"
    20indexName = "<INDEX-NAME>"
    21numberOfDimensions = 1024
    22embeddingFields = []string{"embeddings_float32", "embeddings_int8", "embeddings_int1"}
    23embeddingSimilarity = []string{"dotProduct", "dotProduct", "euclidean"}
    24)
    25
    26func main() {
    27clientOpts := options.Client().ApplyURI(mongodbURI)
    28client, err := mongo.Connect(clientOpts)
    29if err != nil {
    30log.Fatalf("Failed to connect to MongoDB: %v", err)
    31}
    32
    33defer func() {
    34if err := client.Disconnect(context.TODO()); err != nil {
    35log.Fatalf("Failed to disconnect MongoDB client: %v", err)
    36}
    37}()
    38
    39storeEmbeddings(client)
    40setupVectorSearchIndex(client)
    41}
    42
    43// Reads JSON data, stores it in MongoDB
    44func storeEmbeddings(client *mongo.Client) {
    45database := client.Database(dbName)
    46collection := database.Collection(collectionName)
    47
    48data, err := ioutil.ReadFile("embeddings.json")
    49if err != nil {
    50log.Fatalf("Failed to read file: %v", err)
    51}
    52
    53var documents []bson.M
    54if err := bson.UnmarshalExtJSON(data, false, &documents); err != nil {
    55log.Fatalf("Failed to unmarshal JSON data: %v", err)
    56}
    57
    58if _, err := collection.InsertMany(context.TODO(), documents); err != nil {
    59log.Fatalf("Failed to insert documents: %v", err)
    60}
    61
    62fmt.Println("Inserted documents into MongoDB")
    63}
    64
    65// Sets up vector search index in MongoDB
    66func setupVectorSearchIndex(client *mongo.Client) {
    67database := client.Database(dbName)
    68collection := database.Collection(collectionName)
    69
    70ctx := context.TODO()
    71
    72type vectorDefinitionField struct {
    73Type string `bson:"type"`
    74Path string `bson:"path"`
    75NumDimensions int `bson:"numDimensions"`
    76Similarity string `bson:"similarity"`
    77}
    78
    79type vectorDefinition struct {
    80Fields []vectorDefinitionField `bson:"fields"`
    81}
    82
    83fields := make([]vectorDefinitionField, len(embeddingFields))
    84for i, field := range embeddingFields {
    85fields[i] = vectorDefinitionField{
    86Type: "vector",
    87Path: field,
    88NumDimensions: numberOfDimensions,
    89Similarity: embeddingSimilarity[i],
    90}
    91}
    92fmt.Println(fields)
    93
    94opts := options.SearchIndexes().SetName(indexName).SetType("vectorSearch")
    95
    96indexModel := mongo.SearchIndexModel{
    97Definition: vectorDefinition{
    98Fields: fields,
    99},
    100Options: opts,
    101}
    102
    103// Create the index
    104log.Println("Creating the index.")
    105searchIndexName, err := collection.SearchIndexes().CreateOne(ctx, indexModel)
    106if err != nil {
    107log.Fatalf("Failed to create the search index: %v", err)
    108}
    109
    110// Polling to confirm successful index creation
    111log.Println("Polling to confirm successful index creation.")
    112log.Println("NOTE: This may take up to a minute.")
    113searchIndexes := collection.SearchIndexes()
    114var doc bson.Raw
    115
    116for doc == nil {
    117cursor, err := searchIndexes.List(ctx, options.SearchIndexes().SetName(searchIndexName))
    118if err != nil {
    119log.Fatalf("failed to list search indexes: %v", err)
    120}
    121
    122if !cursor.Next(ctx) {
    123break
    124}
    125
    126name := cursor.Current.Lookup("name").StringValue()
    127queryable := cursor.Current.Lookup("queryable").Boolean()
    128if name == searchIndexName && queryable {
    129doc = cursor.Current
    130} else {
    131time.Sleep(5 * time.Second)
    132}
    133}
    134
    135log.Println("Name of Index Created: " + searchIndexName)
    136}
  3. Replace the following with valid values in the code and save the file.

    MONGODB_URI

    Your Atlas cluster connection string if you didn't set the environment variable.

    <DATABASE-NAME>

    Name of the Atlas Vector Search index for the collection.

    <COLLECTION-NAME>

    Name of the Atlas Vector Search index for the collection.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

  4. Run the program using the following command.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run UploadDataAndCreateIndex.go
    Inserted documents into MongoDB
    Creating the index.
    Polling to confirm successful index creation.
    NOTE: This may take up to a minute.
    Name of Index Created: <INDEX-NAME>
  5. Log in to your Atlas cluster and verify the following:

    • Data in the namespace.

    • Atlas Vector Search index for the collection.

6

To test your embeddings, you can run a query against your collection. Use an embedding model provider to generate float32, int8, and int1 embeddings for your query text. The following sample code uses Cohere's embed API to generate full-precision vectors. After generating the embeddings, use the MongoDB Go driver to convert your native vector embedding to BSON binary vectors and run $vectorSearch query against the collection.

  1. Create a new file named CreateEmbeddingsAndRunQuery.go in your Go project.

    touch CreateEmbeddingsAndRunQuery.go
  2. Copy and paste the following code in the CreateEmbeddingsAndRunQuery.go file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Cohere's embed API.

    • Converts the embeddings to BSON binData vectors by using MongoDB Go driver.

    • Runs the query against your collection and returns the results.

    CreateEmbeddingsAndRunQuery.go
    1package main
    2
    3import (
    4"bytes"
    5"context"
    6"encoding/json"
    7"fmt"
    8"io"
    9"log"
    10"net/http"
    11"os"
    12
    13"go.mongodb.org/mongo-driver/v2/bson"
    14"go.mongodb.org/mongo-driver/v2/mongo"
    15"go.mongodb.org/mongo-driver/v2/mongo/options"
    16)
    17
    18const (
    19dbName = "<DATABASE-NAME>"
    20collectionName = "<COLLECTION-NAME>"
    21vectorIndexName = "<INDEX-NAME>"
    22dataFieldName = "<TEXT-FIELD-NAME>"
    23queryText = "<QUERY-TEXT>"
    24model = "voyage-3-large"
    25outputDimension = 1024
    26candidates = <NUMBER-OF-CANDIDATES-TO-CONSIDER>
    27numDocs = <NUMBER-OF-DOCUMENTS-TO-RETURN>
    28)
    29
    30func main() {
    31apiKey := os.Getenv("VOYAGE_API_KEY")
    32mongodbURI := os.Getenv("MONGODB_URI")
    33
    34if apiKey == "" {
    35log.Fatal("API key not found. Set VOYAGE_API_KEY in your environment.")
    36}
    37if mongodbURI == "" {
    38log.Fatal("MongoDB URI not found. Set MONGODB_URI in your environment.")
    39}
    40
    41embeddingsData, err := generateAndConvertEmbeddings(apiKey, queryText)
    42if err != nil {
    43log.Fatalf("Error generating embeddings: %v", err)
    44}
    45
    46err = runVectorSearchQuery(mongodbURI, embeddingsData)
    47if err != nil {
    48log.Fatalf("Error running vector search query: %v", err)
    49}
    50}
    51
    52// Generate embeddings using Voyage AI's embedding API from the query text
    53func generateAndConvertEmbeddings(apiKey, text string) (map[string]bson.Binary, error) {
    54embeddingFormats := []string{"float", "int8", "ubinary"}
    55embeddingsData := make(map[string]bson.Binary)
    56
    57for _, outputDType := range embeddingFormats {
    58response := fetchEmbeddingsFromVoyageAPI(apiKey, text, outputDType)
    59embedding := createBSONVectorEmbeddings(outputDType, response)
    60embeddingsData[outputDType] = embedding
    61}
    62
    63return embeddingsData, nil
    64}
    65
    66// Fetch embeddings using Voyage AI Embedding REST API
    67func fetchEmbeddingsFromVoyageAPI(apiKey, text, outputDType string) map[string]interface{} {
    68url := "https://api.voyageai.com/v1/embeddings"
    69
    70requestBody := map[string]interface{}{
    71"input": []string{text},
    72"model": model,
    73"output_dtype": outputDType,
    74"output_dimension": outputDimension,
    75"input_type": "query",
    76}
    77
    78requestBytes, err := json.Marshal(requestBody)
    79if err != nil {
    80log.Fatalf("Failed to marshal request body: %v", err)
    81}
    82
    83req, err := http.NewRequestWithContext(context.TODO(), "POST", url, bytes.NewBuffer(requestBytes))
    84if err != nil {
    85log.Fatalf("Failed to create HTTP request: %v", err)
    86}
    87
    88req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
    89req.Header.Set("Content-Type", "application/json")
    90
    91client := &http.Client{}
    92resp, err := client.Do(req)
    93if err != nil {
    94log.Fatalf("Failed to make API request: %v", err)
    95}
    96defer resp.Body.Close()
    97
    98if resp.StatusCode != http.StatusOK {
    99body, _ := io.ReadAll(resp.Body)
    100log.Fatalf("Unexpected status code %d: %s", resp.StatusCode, string(body))
    101}
    102
    103var response struct {
    104Data []map[string]interface{} `json:"data"`
    105}
    106if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
    107log.Fatalf("Failed to parse API response: %v", err)
    108}
    109
    110if len(response.Data) == 0 {
    111log.Fatalf("No embeddings found in API response")
    112}
    113
    114return response.Data[0]
    115}
    116
    117// Convert embeddings to BSON vectors using MongoDB Go Driver
    118func createBSONVectorEmbeddings(dataType string, rawEmbedding map[string]interface{}) bson.Binary {
    119embeddingArray := rawEmbedding["embedding"].([]interface{})
    120
    121switch dataType {
    122case "float":
    123floatData := convertInterfaceToFloat32(embeddingArray)
    124floatVector := bson.NewVector(floatData)
    125return floatVector.Binary()
    126case "int8":
    127int8Data := convertInterfaceToInt8(embeddingArray)
    128int8Vector := bson.NewVector(int8Data)
    129return int8Vector.Binary()
    130case "ubinary":
    131int1Data := convertInterfaceToBytes(embeddingArray)
    132ubinaryVector, err := bson.NewPackedBitVector(int1Data, 0)
    133if err != nil {
    134log.Fatalf("Error creating PackedBitVector: %v", err)
    135}
    136return ubinaryVector.Binary()
    137default:
    138log.Fatalf("Unknown data type: %s", dataType)
    139return bson.Binary{}
    140}
    141}
    142
    143// Run $vectorSearch query using the embeddings
    144func runVectorSearchQuery(mongodbURI string, embeddingsData map[string]bson.Binary) error {
    145ctx := context.Background()
    146clientOptions := options.Client().ApplyURI(mongodbURI)
    147client, err := mongo.Connect(clientOptions)
    148if err != nil {
    149return fmt.Errorf("failed to connect to MongoDB: %w", err)
    150}
    151defer func() { _ = client.Disconnect(ctx) }()
    152
    153db := client.Database(dbName)
    154collection := db.Collection(collectionName)
    155
    156pathMap := map[string]string{
    157"float": "embeddings_float32",
    158"int8": "embeddings_int8",
    159"ubinary": "embeddings_int1",
    160}
    161
    162for pathKey, queryVector := range embeddingsData {
    163path, ok := pathMap[pathKey]
    164if !ok {
    165return fmt.Errorf("invalid path key: %s", pathKey)
    166}
    167
    168pipeline := mongo.Pipeline{
    169{
    170{"$vectorSearch", bson.D{
    171{"queryVector", queryVector},
    172{"index", vectorIndexName},
    173{"path", path},
    174{"numCandidates", candidates},
    175{"limit", numDocs},
    176}},
    177},
    178{
    179{"$project", bson.D{
    180{"_id", 1},
    181{dataFieldName, 1},
    182{"score", bson.D{
    183{"$meta", "vectorSearchScore"},
    184}},
    185}},
    186},
    187}
    188
    189cursor, err := collection.Aggregate(context.Background(), pipeline)
    190if err != nil {
    191return fmt.Errorf("failed to run vector search aggregation query: %w", err)
    192}
    193defer cursor.Close(ctx)
    194
    195var results []bson.M
    196if err = cursor.All(context.Background(), &results); err != nil {
    197return fmt.Errorf("failed to parse aggregation query results: %w", err)
    198}
    199
    200fmt.Printf("Results from %v embeddings:\n", path)
    201for _, result := range results {
    202fmt.Println(result)
    203}
    204}
    205
    206return nil
    207}
    208
    209// Converts []interface{} to []float32 safely
    210func convertInterfaceToFloat32(data []interface{}) []float32 {
    211f32s := make([]float32, len(data))
    212for i, v := range data {
    213switch val := v.(type) {
    214case float64:
    215f32s[i] = float32(val)
    216case int:
    217f32s[i] = float32(val)
    218default:
    219log.Fatalf("Unexpected type %T in float32 conversion at index %d", v, i)
    220}
    221}
    222return f32s
    223}
    224
    225// Converts []interface{} to []int8 safely
    226func convertInterfaceToInt8(data []interface{}) []int8 {
    227ints8 := make([]int8, len(data))
    228for i, v := range data {
    229switch val := v.(type) {
    230case float64:
    231ints8[i] = int8(val)
    232case int:
    233ints8[i] = int8(val)
    234default:
    235log.Fatalf("Unexpected type %T in int8 conversion at index %d", v, i)
    236}
    237}
    238return ints8
    239}
    240
    241// Converts []interface{} to []byte (uint8) safely
    242func convertInterfaceToBytes(data []interface{}) []byte {
    243bytesOut := make([]byte, len(data))
    244for i, v := range data {
    245switch val := v.(type) {
    246case float64:
    247bytesOut[i] = byte(val)
    248case int:
    249bytesOut[i] = byte(val)
    250default:
    251log.Fatalf("Unexpected type %T in byte conversion at index %d", v, i)
    252}
    253}
    254return bytesOut
    255}
  3. Replace the following placeholder values in the code and save the file.

    MONGODB_URI

    Your Atlas cluster connection string if you didn't set the environment variable.

    VOYAGE_API_KEY

    Your Voyage AI API key only if you didn't set the environment variable.

    <DATABASE-NAME>

    Name of the database in your Atlas cluster.

    <COLLECTION-NAME>

    Name of the collection where you ingested the data.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

    <TEXT-FIELD-NAME>

    Name of the field that contain the text from which you generated embeddings. For this example, use text.

    <QUERY-TEXT>

    Text for the query. For this example, use science fact.

    <NUMBER-OF-CANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to consider during the search. For this example, use 5.

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of documents to return in the results. For this example, use 2.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run CreateEmbeddingsAndRunQuery.go
    Results from embeddings_float32 embeddings:
    {"_id":{"$oid":"68630fc85cb353712a1c521d"},"text":"The Great Wall of China is visible from space.","score":{"$numberDouble":"0.7723928093910217"}}
    {"_id":{"$oid":"68630fc85cb353712a1c521f"},"text":"Mount Everest is the highest peak on Earth at 8,848m.","score":{"$numberDouble":"0.7363046407699585"}}
    Results from embeddings_int8 embeddings:
    {"_id":{"$oid":"68630fc85cb353712a1c521d"},"text":"The Great Wall of China is visible from space.","score":{"$numberDouble":"0.5051995515823364"}}
    {"_id":{"$oid":"68630fc85cb353712a1c521f"},"text":"Mount Everest is the highest peak on Earth at 8,848m.","score":{"$numberDouble":"0.5044659972190857"}}
    Results from embeddings_int1 embeddings:
    {"_id":{"$oid":"68630fc85cb353712a1c521d"},"text":"The Great Wall of China is visible from space.","score":{"$numberDouble":"0.6845703125"}}
    {"_id":{"$oid":"68630fc85cb353712a1c521f"},"text":"Mount Everest is the highest peak on Earth at 8,848m.","score":{"$numberDouble":"0.6650390625"}}
1

In a terminal window, run the following commands to create a new directory named ingest-binary-vectors and initialize your project:

mkdir ingest-binary-vectors-project
cd ingest-binary-vectors-project
go mod init ingest-binary-vectors-project
2

Run the following command to install the MongoDB Go Driver. This operation might take a few minutes to complete.

go get go.mongodb.org/mongo-driver/v2/mongo

You must install Go v2.1 or later driver. If necessary, you can also install libraries from your embedding model provider. For examples in this tutorial, we will use the Voyage AI REST API to generate embeddings. Therefore, you don't need to install any additional libraries.

3
  1. To access the embedding model provider for generating and converting embeddings, set the environment variable for the embedding model provider's API key, if necessary.

    For using embeddings from Voyage AI, set up the VOYAGE_API_KEY environment variable.

    export VOYAGE_API_KEY="<VOYAGE-API-KEY>"
  2. To access Atlas cluster, set the MONGODB_URI environment variable.

    export MONGODB_URI="<CONNECTION-STRING>"

    Your connection string should be in the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
4

You can use an embedding model provider to generate float, int8, and int1 embeddings for your data and then use the MongoDB Go driver to convert your native vector embedding to BSON vectors. The following sample code uses Voyage AI's voyage-3-large embedding model to generate full-precision vectors from the data in the sample_airbnb.listingsAndReviews namespace.

  1. Create a new file named GenerateAndConvertEmbeddings.go in your Go project.

    touch GenerateAndConvertEmbeddings.go
  2. Copy and paste the following code in the GenerateAndConvertEmbeddings.go file.

    This code does the following:

    • Gets the summary field from 50 documents in the sample_airbnb.listingsAndReviews namespace.

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's API.

    • Converts the embeddings to BSON binData vectors by using MongoDB Go driver.

    • Creates a file named embeddings.json and saves the data with embeddings in the file.

    GenerateAndConvertEmbeddings.go
    1package main
    2
    3import (
    4"bytes"
    5"context"
    6"encoding/json"
    7"fmt"
    8"io/ioutil"
    9"log"
    10"net/http"
    11"os"
    12
    13"go.mongodb.org/mongo-driver/v2/bson"
    14"go.mongodb.org/mongo-driver/v2/mongo"
    15"go.mongodb.org/mongo-driver/v2/mongo/options"
    16)
    17
    18const (
    19batchSize = 96
    20dbName = "sample_airbnb"
    21collName = "listingsAndReviews"
    22model = "voyage-3-large"
    23outputDimension = 1024
    24)
    25
    26func main() {
    27apiKey := os.Getenv("VOYAGE_API_KEY")
    28mongodbURI := os.Getenv("MONGODB_URI")
    29
    30if apiKey == "" || mongodbURI == "" {
    31log.Fatal("Ensure VOYAGE_API_KEY and MONGODB_URI are set.")
    32}
    33
    34summaries, err := fetchSummariesFromMongoDB(mongodbURI)
    35if err != nil {
    36log.Fatalf("Error fetching summaries: %v", err)
    37}
    38
    39for start := 0; start < len(summaries); start += batchSize {
    40end := start + batchSize
    41if end > len(summaries) {
    42end = len(summaries)
    43}
    44
    45floatEmbeddings, err := fetchEmbeddingsFromVoyage(apiKey, summaries[start:end], "float")
    46if err != nil {
    47log.Fatalf("Error fetching float embeddings: %v", err)
    48}
    49
    50int8Embeddings, err := fetchEmbeddingsFromVoyage(apiKey, summaries[start:end], "int8")
    51if err != nil {
    52log.Fatalf("Error fetching int8 embeddings: %v", err)
    53}
    54
    55ubinaryEmbeddings, err := fetchEmbeddingsFromVoyage(apiKey, summaries[start:end], "ubinary")
    56if err != nil {
    57log.Fatalf("Error fetching ubinary embeddings: %v", err)
    58}
    59
    60documents := convertEmbeddingsToBSON(summaries[start:end], floatEmbeddings, int8Embeddings, ubinaryEmbeddings)
    61
    62err = writeJSONToFile("embeddings.json", documents)
    63if err != nil {
    64log.Fatalf("Error writing embeddings to JSON: %v", err)
    65}
    66}
    67
    68fmt.Println("Embeddings successfully saved to embeddings.json")
    69}
    70
    71// Fetch documents with the summary field from the collection
    72func fetchSummariesFromMongoDB(uri string) ([]string, error) {
    73ctx := context.TODO()
    74clientOpts := options.Client().ApplyURI(uri)
    75
    76client, err := mongo.Connect(clientOpts)
    77if err != nil {
    78return nil, fmt.Errorf("failed to connect to MongoDB: %w", err)
    79}
    80defer func() {
    81if err := client.Disconnect(ctx); err != nil {
    82log.Fatalf("Failed to disconnect MongoDB client: %v", err)
    83}
    84}()
    85
    86collection := client.Database(dbName).Collection(collName)
    87filter := bson.M{"summary": bson.M{"$nin": []interface{}{nil, ""}}}
    88
    89cursor, err := collection.Find(ctx, filter, options.Find().SetLimit(50))
    90if err != nil {
    91return nil, fmt.Errorf("error finding documents: %w", err)
    92}
    93defer cursor.Close(ctx)
    94
    95var summaries []string
    96for cursor.Next(ctx) {
    97var result struct {
    98Summary string `bson:"summary"`
    99}
    100if err := cursor.Decode(&result); err != nil {
    101return nil, fmt.Errorf("error decoding document: %w", err)
    102}
    103if result.Summary != "" {
    104summaries = append(summaries, result.Summary)
    105}
    106}
    107
    108if err := cursor.Err(); err != nil {
    109return nil, fmt.Errorf("cursor error: %w", err)
    110}
    111
    112return summaries, nil
    113}
    114
    115// Fetch embeddings using Voyage AI REST API
    116func fetchEmbeddingsFromVoyage(apiKey string, texts []string, outputDType string) ([]map[string]interface{}, error) {
    117url := "https://api.voyageai.com/v1/embeddings"
    118
    119requestBody := map[string]interface{}{
    120"input": texts,
    121"model": model,
    122"output_dtype": outputDType,
    123"output_dimension": outputDimension,
    124"input_type": "document",
    125}
    126
    127requestBytes, err := json.Marshal(requestBody)
    128if err != nil {
    129return nil, fmt.Errorf("failed to marshal request body: %w", err)
    130}
    131
    132req, err := http.NewRequestWithContext(context.TODO(), "POST", url, bytes.NewBuffer(requestBytes))
    133if err != nil {
    134return nil, fmt.Errorf("failed to create HTTP request: %w", err)
    135}
    136
    137req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
    138req.Header.Set("Content-Type", "application/json")
    139
    140client := &http.Client{}
    141resp, err := client.Do(req)
    142if err != nil {
    143return nil, fmt.Errorf("failed to make API request: %w", err)
    144}
    145defer resp.Body.Close()
    146
    147if resp.StatusCode != http.StatusOK {
    148body, _ := ioutil.ReadAll(resp.Body)
    149return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
    150}
    151
    152var response struct {
    153Data []map[string]interface{} `json:"data"`
    154}
    155if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
    156return nil, fmt.Errorf("failed to parse API response: %w", err)
    157}
    158
    159return response.Data, nil
    160}
    161
    162// Convert embeddings to BSON binary vectors
    163func convertEmbeddingsToBSON(summaries []string, floatEmbeddings []map[string]interface{}, int8Embeddings []map[string]interface{}, ubinaryEmbeddings []map[string]interface{}) []bson.M {
    164var documents []bson.M
    165
    166for i, summary := range summaries {
    167floatEmbedding := convertInterfaceToFloat32(floatEmbeddings[i]["embedding"].([]interface{}))
    168int8Embedding := convertInterfaceToInt8(int8Embeddings[i]["embedding"].([]interface{}))
    169ubinaryEmbedding := convertInterfaceToBytes(ubinaryEmbeddings[i]["embedding"].([]interface{}))
    170
    171floatVector := bson.NewVector(floatEmbedding)
    172int8Vector := bson.NewVector(int8Embedding)
    173ubinaryVector, err := bson.NewPackedBitVector(ubinaryEmbedding, 0)
    174if err != nil {
    175log.Fatalf("Error creating PackedBitVector: %v", err)
    176}
    177
    178document := bson.M{
    179"text": summary,
    180"embeddings_float32": floatVector.Binary(),
    181"embeddings_int8": int8Vector.Binary(),
    182"embeddings_int1": ubinaryVector.Binary(),
    183}
    184
    185documents = append(documents, document)
    186}
    187
    188return documents
    189}
    190
    191// Write JSON file from in-memory BSON documents
    192func writeJSONToFile(filename string, docs []bson.M) error {
    193file, err := os.Create(filename)
    194if err != nil {
    195return fmt.Errorf("failed to create file: %w", err)
    196}
    197defer file.Close()
    198
    199var jsonDocuments []json.RawMessage
    200for _, document := range docs {
    201jsonBytes, err := bson.MarshalExtJSON(document, false, false)
    202if err != nil {
    203log.Fatalf("Error: %v", err)
    204}
    205jsonDocuments = append(jsonDocuments, jsonBytes)
    206}
    207
    208jsonData, err := json.MarshalIndent(jsonDocuments, "", " ")
    209if err != nil {
    210return fmt.Errorf("failed to marshal JSON: %w", err)
    211}
    212
    213_, err = file.Write(jsonData)
    214if err != nil {
    215return fmt.Errorf("failed to write JSON to file: %w", err)
    216}
    217
    218return nil
    219}
    220
    221// Converts slice of interface{} to []float32
    222func convertInterfaceToFloat32(data []interface{}) []float32 {
    223f32s := make([]float32, len(data))
    224for i, v := range data {
    225f32s[i] = float32(v.(float64))
    226}
    227return f32s
    228}
    229
    230// Converts slice of interface{} to []int8 safely
    231func convertInterfaceToInt8(data []interface{}) []int8 {
    232ints8 := make([]int8, len(data))
    233for i, v := range data {
    234switch val := v.(type) {
    235case float64:
    236ints8[i] = int8(val)
    237case int:
    238ints8[i] = int8(val)
    239default:
    240log.Fatalf("Unexpected type %T in int8 embedding at index %d", v, i)
    241}
    242}
    243return ints8
    244}
    245
    246// Converts slice of interface{} to []byte safely
    247func convertInterfaceToBytes(data []interface{}) []byte {
    248bytes := make([]byte, len(data))
    249for i, v := range data {
    250switch val := v.(type) {
    251case float64:
    252bytes[i] = byte(val)
    253case int:
    254bytes[i] = byte(val)
    255default:
    256log.Fatalf("Unexpected type %T in ubinary embedding at index %d", v, i)
    257}
    258}
    259return bytes
    260}
  3. Replace the following placeholder values in the code if you didn't set the environment variables and save the file.

    MONGODB_URI

    Your Atlas cluster connection string if you didn't set the environment variable.

    VOYAGE_API_KEY

    Your Voyage AI API key if you didn't set the environment variable.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run GenerateAndConvertEmbeddings.go
    Embeddings successfully saved to embeddings.json
  5. Verify the embeddings in the embeddings.json file.

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

5

You must upload your data and embeddings to a collection in your Atlas cluster and create an Atlas Vector Search index on the data to run $vectorSearch queries against the data.

  1. Create a new file named UploadDataAndCreateIndex.go in your Go project.

    touch UploadDataAndCreateIndex.go
  2. Copy and paste the following code in the UploadDataAndCreateIndex.go file.

    This code does the following:

    • Uploads the float32, int8, and int1 embeddings in the embeddings.json file to your Atlas cluster.

    • Creates an Atlas Vector Search index on the embeddings.float32, embeddings.int8, and embeddings.int1 fields.

    UploadDataAndCreateIndex.go
    1package main
    2
    3import (
    4"context"
    5"fmt"
    6"io/ioutil"
    7"log"
    8 "time"
    9"os"
    10
    11"go.mongodb.org/mongo-driver/v2/bson"
    12"go.mongodb.org/mongo-driver/v2/mongo"
    13"go.mongodb.org/mongo-driver/v2/mongo/options"
    14)
    15
    16var (
    17mongodbURI = os.Getenv("MONGODB_URI")
    18dbName = "sample_airbnb"
    19collectionName = "listingsAndReviews"
    20indexName = "<INDEX-NAME>"
    21numberOfDimensions = 1024
    22embeddingFields = []string{"embeddings_float32", "embeddings_int8", "embeddings_int1"}
    23embeddingSimilarity = []string{"dotProduct", "dotProduct", "euclidean"}
    24)
    25
    26func main() {
    27if mongodbURI == "" {
    28log.Fatal("MONGODB_URI environment variable not set")
    29}
    30
    31clientOptions := options.Client().ApplyURI(mongodbURI)
    32client, err := mongo.Connect(clientOptions)
    33if err != nil {
    34log.Fatalf("Error connecting to MongoDB: %v", err)
    35}
    36defer func() {
    37if err = client.Disconnect(context.TODO()); err != nil {
    38log.Fatal(err)
    39}
    40}()
    41
    42if err := uploadEmbeddingsData(client); err != nil {
    43log.Fatalf("Error uploading embeddings data: %v", err)
    44}
    45
    46setupVectorSearchIndex(client)
    47}
    48
    49func uploadEmbeddingsData(client *mongo.Client) error {
    50collection := client.Database(dbName).Collection(collectionName)
    51
    52// Load embeddings.json file
    53fileContent, err := ioutil.ReadFile("embeddings.json")
    54if err != nil {
    55return fmt.Errorf("error reading file: %w", err)
    56}
    57
    58// Convert JSON file content to BSON compatible format using UnmarshalExtJSON
    59var documents []bson.M
    60if err := bson.UnmarshalExtJSON(fileContent, false, &documents); err != nil {
    61return fmt.Errorf("failed to unmarshal JSON data: %w", err)
    62}
    63
    64// Update documents in MongoDB
    65for _, doc := range documents {
    66summary, exists := doc["text"].(string)
    67if !exists {
    68return fmt.Errorf("missing 'text' field in document")
    69}
    70
    71// Using bson.Binary ensures binary data is correctly interpreted
    72if float32Bin, ok := doc["embeddings_float32"].(bson.Binary); ok {
    73doc["embeddings_float32"] = float32Bin
    74}
    75if int8Bin, ok := doc["embeddings_int8"].(bson.Binary); ok {
    76doc["embeddings_int8"] = int8Bin
    77}
    78if int1Bin, ok := doc["embeddings_int1"].(bson.Binary); ok {
    79doc["embeddings_int1"] = int1Bin
    80}
    81
    82filter := bson.M{"summary": summary}
    83update := bson.M{
    84"$set": doc,
    85}
    86
    87// Set the upsert option
    88opts := options.UpdateMany().SetUpsert(true)
    89
    90_, err = collection.UpdateMany(context.TODO(), filter, update, opts)
    91if err != nil {
    92return fmt.Errorf("failed to update documents: %w", err)
    93}
    94}
    95
    96return nil
    97}
    98
    99// Sets up vector search index in MongoDB
    100func setupVectorSearchIndex(client *mongo.Client) {
    101database := client.Database(dbName)
    102collection := database.Collection(collectionName)
    103
    104ctx := context.TODO()
    105
    106type vectorDefinitionField struct {
    107Type string `bson:"type"`
    108Path string `bson:"path"`
    109NumDimensions int `bson:"numDimensions"`
    110Similarity string `bson:"similarity"`
    111}
    112
    113type vectorDefinition struct {
    114Fields []vectorDefinitionField `bson:"fields"`
    115}
    116
    117fields := make([]vectorDefinitionField, len(embeddingFields))
    118for i, field := range embeddingFields {
    119fields[i] = vectorDefinitionField{
    120Type: "vector",
    121Path: field,
    122NumDimensions: numberOfDimensions,
    123Similarity: embeddingSimilarity[i],
    124}
    125}
    126
    127opts := options.SearchIndexes().SetName(indexName).SetType("vectorSearch")
    128
    129indexModel := mongo.SearchIndexModel{
    130Definition: vectorDefinition{
    131Fields: fields,
    132},
    133Options: opts,
    134}
    135
    136// Create the index
    137log.Println("Creating the index.")
    138searchIndexName, err := collection.SearchIndexes().CreateOne(ctx, indexModel)
    139if err != nil {
    140log.Fatalf("Failed to create the search index: %v", err)
    141}
    142
    143// Polling to confirm successful index creation
    144log.Println("Polling to confirm successful index creation.")
    145log.Println("NOTE: This may take up to a minute.")
    146searchIndexes := collection.SearchIndexes()
    147var doc bson.Raw
    148
    149for doc == nil {
    150cursor, err := searchIndexes.List(ctx, options.SearchIndexes().SetName(searchIndexName))
    151if err != nil {
    152log.Fatalf("failed to list search indexes: %v", err)
    153}
    154
    155if !cursor.Next(ctx) {
    156break
    157}
    158
    159name := cursor.Current.Lookup("name").StringValue()
    160queryable := cursor.Current.Lookup("queryable").Boolean()
    161if name == searchIndexName && queryable {
    162doc = cursor.Current
    163} else {
    164time.Sleep(5 * time.Second)
    165}
    166}
    167
    168log.Println("Name of Index Created: " + searchIndexName)
    169}
  3. Replace the following placeholder values in the code and save the file.

    MONGODB_URI

    Your Atlas cluster connection string if you didn't set the environment variable.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run UploadDataAndCreateIndex.go
    Creating the index.
    Polling to confirm successful index creation.
    NOTE: This may take up to a minute.
    Name of Index Created: <INDEX-NAME>
  5. Log in to your Atlas cluster and verify the following:

    • Data in the namespace.

    • Atlas Vector Search index for the collection.

6

To test your embeddings, you can run a query against your collection. Use an embedding model provider to generate float, int8, and int1 embeddings for your query text. The following sample code uses Voyage AI's API to generate full-precision vectors. After generating the embeddings, use the MongoDB Go driver to convert your native vector embedding to BSON vectors and run $vectorSearch query against the collection.

  1. Create a new file named CreateEmbeddingsAndRunQuery.go in your Go project.

    touch CreateEmbeddingsAndRunQuery.go
  2. Copy and paste the following code in the CreateEmbeddingsAndRunQuery.go file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's API.

    • Converts the embeddings to BSON binData vectors by using MongoDB Go driver.

    • Runs the query against your collection and returns the results.

    CreateEmbeddingsAndRunQuery.go
    1package main
    2
    3import (
    4"bytes"
    5"context"
    6"encoding/json"
    7"fmt"
    8"io"
    9"log"
    10"net/http"
    11"os"
    12
    13"go.mongodb.org/mongo-driver/v2/bson"
    14"go.mongodb.org/mongo-driver/v2/mongo"
    15"go.mongodb.org/mongo-driver/v2/mongo/options"
    16)
    17
    18const (
    19dbName = "<DATABASE-NAME>"
    20collectionName = "<COLLECTION-NAME>"
    21vectorIndexName = "<INDEX-NAME>"
    22dataFieldName = "<TEXT-FIELD-NAME>"
    23queryText = "<QUERY-TEXT>"
    24model = "voyage-3-large"
    25outputDimension = 1024
    26candidates = <NUMBER-OF-CANDIDATES-TO-CONSIDER>
    27numDocs = <NUMBER-OF-DOCUMENTS-TO-RETURN>
    28)
    29
    30func main() {
    31apiKey := os.Getenv("VOYAGE_API_KEY")
    32mongodbURI := os.Getenv("MONGODB_URI")
    33
    34if apiKey == "" {
    35log.Fatal("API key not found. Set VOYAGE_API_KEY in your environment.")
    36}
    37if mongodbURI == "" {
    38log.Fatal("MongoDB URI not found. Set MONGODB_URI in your environment.")
    39}
    40
    41embeddingsData, err := generateAndConvertEmbeddings(apiKey, queryText)
    42if err != nil {
    43log.Fatalf("Error generating embeddings: %v", err)
    44}
    45
    46err = runVectorSearchQuery(mongodbURI, embeddingsData)
    47if err != nil {
    48log.Fatalf("Error running vector search query: %v", err)
    49}
    50}
    51
    52// Generate embeddings using Voyage AI's embedding API from the query text
    53func generateAndConvertEmbeddings(apiKey, text string) (map[string]bson.Binary, error) {
    54embeddingFormats := []string{"float", "int8", "ubinary"}
    55embeddingsData := make(map[string]bson.Binary)
    56
    57for _, outputDType := range embeddingFormats {
    58response := fetchEmbeddingsFromVoyageAPI(apiKey, text, outputDType)
    59embedding := createBSONVectorEmbeddings(outputDType, response)
    60embeddingsData[outputDType] = embedding
    61}
    62
    63return embeddingsData, nil
    64}
    65
    66// Fetch embeddings using Voyage AI Embedding REST API
    67func fetchEmbeddingsFromVoyageAPI(apiKey, text, outputDType string) map[string]interface{} {
    68url := "https://api.voyageai.com/v1/embeddings"
    69
    70requestBody := map[string]interface{}{
    71"input": []string{text},
    72"model": model,
    73"output_dtype": outputDType,
    74"output_dimension": outputDimension,
    75"input_type": "query",
    76}
    77
    78requestBytes, err := json.Marshal(requestBody)
    79if err != nil {
    80log.Fatalf("Failed to marshal request body: %v", err)
    81}
    82
    83req, err := http.NewRequestWithContext(context.TODO(), "POST", url, bytes.NewBuffer(requestBytes))
    84if err != nil {
    85log.Fatalf("Failed to create HTTP request: %v", err)
    86}
    87
    88req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
    89req.Header.Set("Content-Type", "application/json")
    90
    91client := &http.Client{}
    92resp, err := client.Do(req)
    93if err != nil {
    94log.Fatalf("Failed to make API request: %v", err)
    95}
    96defer resp.Body.Close()
    97
    98if resp.StatusCode != http.StatusOK {
    99body, _ := io.ReadAll(resp.Body)
    100log.Fatalf("Unexpected status code %d: %s", resp.StatusCode, string(body))
    101}
    102
    103var response struct {
    104Data []map[string]interface{} `json:"data"`
    105}
    106if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
    107log.Fatalf("Failed to parse API response: %v", err)
    108}
    109
    110if len(response.Data) == 0 {
    111log.Fatalf("No embeddings found in API response")
    112}
    113
    114return response.Data[0]
    115}
    116
    117// Convert embeddings to BSON vectors using MongoDB Go Driver
    118func createBSONVectorEmbeddings(dataType string, rawEmbedding map[string]interface{}) bson.Binary {
    119embeddingArray := rawEmbedding["embedding"].([]interface{})
    120
    121switch dataType {
    122case "float":
    123floatData := convertInterfaceToFloat32(embeddingArray)
    124floatVector := bson.NewVector(floatData)
    125return floatVector.Binary()
    126case "int8":
    127int8Data := convertInterfaceToInt8(embeddingArray)
    128int8Vector := bson.NewVector(int8Data)
    129return int8Vector.Binary()
    130case "ubinary":
    131int1Data := convertInterfaceToBytes(embeddingArray)
    132ubinaryVector, err := bson.NewPackedBitVector(int1Data, 0)
    133if err != nil {
    134log.Fatalf("Error creating PackedBitVector: %v", err)
    135}
    136return ubinaryVector.Binary()
    137default:
    138log.Fatalf("Unknown data type: %s", dataType)
    139return bson.Binary{}
    140}
    141}
    142
    143// Run $vectorSearch query using the embeddings
    144func runVectorSearchQuery(mongodbURI string, embeddingsData map[string]bson.Binary) error {
    145ctx := context.Background()
    146clientOptions := options.Client().ApplyURI(mongodbURI)
    147client, err := mongo.Connect(clientOptions)
    148if err != nil {
    149return fmt.Errorf("failed to connect to MongoDB: %w", err)
    150}
    151defer func() { _ = client.Disconnect(ctx) }()
    152
    153db := client.Database(dbName)
    154collection := db.Collection(collectionName)
    155
    156pathMap := map[string]string{
    157"float": "embeddings_float32",
    158"int8": "embeddings_int8",
    159"ubinary": "embeddings_int1",
    160}
    161
    162for pathKey, queryVector := range embeddingsData {
    163path, ok := pathMap[pathKey]
    164if !ok {
    165return fmt.Errorf("invalid path key: %s", pathKey)
    166}
    167
    168pipeline := mongo.Pipeline{
    169{
    170{"$vectorSearch", bson.D{
    171{"queryVector", queryVector},
    172{"index", vectorIndexName},
    173{"path", path},
    174{"numCandidates", candidates},
    175{"limit", numDocs},
    176}},
    177},
    178{
    179{"$project", bson.D{
    180{"_id", 1},
    181{dataFieldName, 1},
    182{"score", bson.D{
    183{"$meta", "vectorSearchScore"},
    184}},
    185}},
    186},
    187}
    188
    189cursor, err := collection.Aggregate(context.Background(), pipeline)
    190if err != nil {
    191return fmt.Errorf("failed to run vector search aggregation query: %w", err)
    192}
    193defer cursor.Close(ctx)
    194
    195var results []bson.M
    196if err = cursor.All(context.Background(), &results); err != nil {
    197return fmt.Errorf("failed to parse aggregation query results: %w", err)
    198}
    199
    200fmt.Printf("Results from %v embeddings:\n", path)
    201for _, result := range results {
    202fmt.Println(result)
    203}
    204}
    205
    206return nil
    207}
    208
    209// Converts []interface{} to []float32 safely
    210func convertInterfaceToFloat32(data []interface{}) []float32 {
    211f32s := make([]float32, len(data))
    212for i, v := range data {
    213switch val := v.(type) {
    214case float64:
    215f32s[i] = float32(val)
    216case int:
    217f32s[i] = float32(val)
    218default:
    219log.Fatalf("Unexpected type %T in float32 conversion at index %d", v, i)
    220}
    221}
    222return f32s
    223}
    224
    225// Converts []interface{} to []int8 safely
    226func convertInterfaceToInt8(data []interface{}) []int8 {
    227ints8 := make([]int8, len(data))
    228for i, v := range data {
    229switch val := v.(type) {
    230case float64:
    231ints8[i] = int8(val)
    232case int:
    233ints8[i] = int8(val)
    234default:
    235log.Fatalf("Unexpected type %T in int8 conversion at index %d", v, i)
    236}
    237}
    238return ints8
    239}
    240
    241// Converts []interface{} to []byte (uint8) safely
    242func convertInterfaceToBytes(data []interface{}) []byte {
    243bytesOut := make([]byte, len(data))
    244for i, v := range data {
    245switch val := v.(type) {
    246case float64:
    247bytesOut[i] = byte(val)
    248case int:
    249bytesOut[i] = byte(val)
    250default:
    251log.Fatalf("Unexpected type %T in byte conversion at index %d", v, i)
    252}
    253}
    254return bytesOut
    255}
  3. Replace the following placeholder values in the code and save the file.

    MONGODB_URI

    Your Atlas cluster connection string if you didn't set the environment variable.

    VOYAGE_API_KEY

    Your Voyage AI API key if you didn't set the environment variable.

    <DATABASE-NAME>

    Name of the database in your Atlas cluster. For this example, use sample_airbnb.

    <COLLECTION-NAME>

    Name of the collection where you ingested the data. For this example, use listingsAndReviews.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

    <TEXT-FIELD-NAME>

    Name of the field that contain the text from which you generated embeddings. For this example, use summary.

    <QUERY-TEXT>

    Text for the query. For this example, use ocean view.

    <NUMBER-OF-CANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to consider during the search. For this example, use 20.

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of documents to return in the results. For this example, use 5.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    go run CreateEmbeddingsAndRunQuery.go
    Results from embeddings_float32 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.7278661131858826"}}
    {"summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.688639760017395"},"_id":"1001265"}
    Results from embeddings_int8 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.5215557217597961"}}
    {"_id":"1001265","summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.5179016590118408"}}
    Results from embeddings_int1 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.6591796875"}}
    {"_id":"1001265","summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.6337890625"}}

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

Create a Java project in your IDE with the dependencies configured for the MongoDB Java Driver, and then perform the following steps in the project. To try the example, replace the placeholders with valid values.

1
  1. From your IDE, create a Java project using Maven or Gradle.

  2. Add the following dependencies, depending on your package manager:

    If you are using Maven, add the following dependencies to the dependencies array in your project's pom.xml file:

    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>5.3.1</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.16</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.16</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20250517</version>
    </dependency>
    <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
    </dependency>
    </dependencies>

    If you are using Gradle, add the following to the dependencies array in your project's build.gradle file:

    build.gradle
    dependencies {
    // JUnit for testing
    testImplementation 'junit:junit:3.8.1'
    // MongoDB synchronous driver
    implementation 'org.mongodb:mongodb-driver-sync:5.3.1'
    // SLF4J API for logging
    implementation 'org.slf4j:slf4j-api:2.0.16'
    // SLF4J Simple Logger (scope: test)
    testImplementation 'org.slf4j:slf4j-simple:2.0.16'
    // JSON library
    implementation 'org.json:json:20210307'
    // HTTP client for Java
    implementation 'com.squareup.okhttp3:okhttp:4.12.0' // Or the latest version
    }
  3. Run your package manager to install the dependencies to your project.

2

Note

This example sets the variables for the project in the IDE. Production applications might manage environment variables through a deployment configuration, CI/CD pipeline, or secrets manager, but you can adapt the provided code to fit your use case.

In your IDE, create a new configuration template and add the following variables to your project:

  • If you are using IntelliJ IDEA, create a new Application run configuration template, then add your variables as semicolon-separated values in the Environment variables field (for example, FOO=123;BAR=456). Apply the changes and click OK.

    To learn more, see the Create a run/debug configuration from a template section of the IntelliJ IDEA documentation.

  • If you are using Eclipse, create a new Java Application launch configuration, then add each variable as a new key-value pair in the Environment tab. Apply the changes and click OK.

    To learn more, see the Creating a Java application launch configuration section of the Eclipse IDE documentation.

Environment variables
VOYAGE_API_KEY=<api-key>
MONGODB_URI=<connection-string>

Update the placeholders with the following values:

  • Replace the <api-key> placeholder value with your Voyage AI API key.

  • Replace the <connection-string> placeholder value with the SRV connection string for your Atlas cluster.

    Your connection string should use the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
3

You can use an embedding model provider to generate float, int8, and int1 embeddings for your data and then use the MongoDB Java driver to convert your native vector embedding to BSON vectors. The following sample code uses Voyage AI's voyage-3-large API to generate full-precision vectors.

  1. Create a new file named GenerateAndConvertEmbeddings.java in your Java project.

    touch GenerateAndConvertEmbeddings.java
  2. Copy and paste the following code in the GenerateAndConvertEmbeddings.java file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's voyage-3-large embedding model.

    • Converts the embeddings to BSON binData vectors by using MongoDB Java driver.

    • Creates a file named embeddings.json and saves the data with embeddings in the file to upload to Atlas.

    GenerateAndConvertEmbeddings.java
    1import okhttp3.*;
    2import org.bson.BinaryVector;
    3import org.bson.Document;
    4import org.json.JSONArray;
    5import org.json.JSONObject;
    6
    7import java.io.FileOutputStream;
    8import java.io.IOException;
    9import java.util.ArrayList;
    10import java.util.List;
    11import java.util.Objects;
    12import java.util.concurrent.TimeUnit;
    13
    14public class GenerateAndConvertEmbeddings {
    15 // Sample Data
    16 private static final List<String> DATA = List.of(
    17 "The Great Wall of China is visible from space.",
    18 "The Eiffel Tower was completed in Paris in 1889.",
    19 "Mount Everest is the highest peak on Earth at 8,848m.",
    20 "Shakespeare wrote 37 plays and 154 sonnets during his lifetime.",
    21 "The Mona Lisa was painted by Leonardo da Vinci."
    22 );
    23
    24 // Configuration settings
    25 private static final String VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
    26 private static final int CONNECTION_TIMEOUT = 30;
    27 private static final int READ_TIMEOUT = 60;
    28
    29 public static void main(String[] args) {
    30 String apiKey = System.getenv("VOYAGE_API_KEY"); // Replace with your actual API key
    31
    32 if (Objects.isNull(apiKey) || apiKey.isEmpty()) {
    33 throw new RuntimeException("API key not found.");
    34 }
    35
    36 Document bsonEmbeddings = fetchEmbeddings(apiKey);
    37 writeToFile(bsonEmbeddings, "embeddings.json");
    38 }
    39
    40 // Fetch embeddings from Voyage AI API using the given API key
    41 private static Document fetchEmbeddings(String apiKey) {
    42 OkHttpClient client = new OkHttpClient.Builder()
    43 .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
    44 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
    45 .build();
    46
    47 List<List<List<Integer>>> embeddingsByOutputType = new ArrayList<>();
    48 List<String> outputDtypes = List.of("float", "int8", "ubinary");
    49
    50 try {
    51 for (String dtype : outputDtypes) {
    52 String responseBody = sendRequest(client, apiKey, dtype);
    53 embeddingsByOutputType.add(parseEmbeddings(responseBody, dtype));
    54 }
    55 } catch (IOException e) {
    56 throw new RuntimeException("Error fetching embeddings: " + e.getMessage(), e);
    57 }
    58
    59 return convertEmbeddingsToBson(embeddingsByOutputType);
    60 }
    61
    62 // Send API request to Voyage AI
    63 private static String sendRequest(OkHttpClient client, String apiKey, String outputDtype) throws IOException {
    64 String requestBody = new JSONObject()
    65 .put("input", DATA)
    66 .put("model", "voyage-3-large")
    67 .put("input_type", "document")
    68 .put("output_dtype", outputDtype)
    69 .put("output_dimension", 1024)
    70 .toString();
    71
    72 Request request = new Request.Builder()
    73 .url(VOYAGE_API_URL)
    74 .post(RequestBody.create(requestBody, MediaType.get("application/json")))
    75 .addHeader("Authorization", "Bearer " + apiKey)
    76 .build();
    77
    78 try (Response response = client.newCall(request).execute()) {
    79 if (!response.isSuccessful()) {
    80 throw new IOException("API error: HTTP " + response.code());
    81 }
    82 return response.body().string();
    83 }
    84 }
    85
    86 // Parse embeddings from Voyage AI API response
    87 private static List<List<Integer>> parseEmbeddings(String responseBody, String outputDtype) {
    88 JSONObject responseJson = new JSONObject(responseBody);
    89 JSONArray dataArray = responseJson.optJSONArray("data");
    90
    91 if (dataArray == null) {
    92 throw new RuntimeException("Invalid response format: 'data' field missing.");
    93 }
    94
    95 List<List<Integer>> embeddings = new ArrayList<>();
    96 for (int i = 0; i < dataArray.length(); i++) {
    97 JSONArray embeddingVector = dataArray.getJSONObject(i).getJSONArray("embedding");
    98
    99 List<Integer> vector = new ArrayList<>();
    100 for (int j = 0; j < embeddingVector.length(); j++) {
    101 int value = embeddingVector.getInt(j);
    102
    103 // Handle binary quantization offset
    104 if ("binary".equals(outputDtype)) {
    105 value = value - 128; // Offset binary method (signed int8 representation)
    106 }
    107
    108 vector.add(value);
    109 }
    110 embeddings.add(vector);
    111 }
    112 return embeddings;
    113 }
    114
    115 // Convert fetched embeddings into BSON format
    116 private static Document convertEmbeddingsToBson(List<List<List<Integer>>> embeddingsByOutputType) {
    117 List<Document> bsonEmbeddings = new ArrayList<>();
    118 for (int i = 0; i < DATA.size(); i++) {
    119 Document embedding = new Document()
    120 .append("text", DATA.get(i))
    121 .append("embeddings_float32", BinaryVector.floatVector(listToFloatArray(embeddingsByOutputType.get(0).get(i))))
    122 .append("embeddings_int8", BinaryVector.int8Vector(listToByteArray(embeddingsByOutputType.get(1).get(i)))) // Binary embeddings
    123 .append("embeddings_int1", BinaryVector.packedBitVector(listToByteArray(embeddingsByOutputType.get(2).get(i)), (byte) 0)); // Ubinary embeddings
    124 bsonEmbeddings.add(embedding);
    125 }
    126 return new Document("data", bsonEmbeddings);
    127 }
    128
    129 // Save BSON embeddings to a JSON file
    130 private static void writeToFile(Document bsonEmbeddings, String fileName) {
    131 try (FileOutputStream fos = new FileOutputStream(fileName)) {
    132 fos.write(bsonEmbeddings.toJson().getBytes());
    133 System.out.println("Embeddings saved to " + fileName);
    134 } catch (IOException e) {
    135 throw new RuntimeException("Error saving file: " + e.getMessage(), e);
    136 }
    137 }
    138
    139 private static float[] listToFloatArray(List<Integer> list) {
    140 float[] array = new float[list.size()];
    141 for (int i = 0; i < list.size(); i++) {
    142 array[i] = list.get(i).floatValue();
    143 }
    144 return array;
    145 }
    146
    147 private static byte[] listToByteArray(List<Integer> list) {
    148 byte[] array = new byte[list.size()];
    149 for (int i = 0; i < list.size(); i++) {
    150 array[i] = list.get(i).byteValue();
    151 }
    152 return array;
    153 }
    154}
  3. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac GenerateAndConvertEmbeddings.java
    java GenerateAndConvertEmbeddings
    Embeddings saved to embeddings.json
  4. Verify the embeddings in the embeddings.json file.

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

4

You must upload your data and embeddings to a collection in your Atlas cluster and create an Atlas Vector Search index on the data to run $vectorSearch queries against the data.

  1. Create a new file named UploadDataAndCreateIndex.java in your Java project.

    touch UploadDataAndCreateIndex.java
  2. Copy and paste the following code in the UploadDataAndCreateIndex.java file.

    This code does the following:

    • Uploads the data in the embeddings.json file to your Atlas cluster.

    • Creates an Atlas Vector Search index on the embeddings_float32, embeddings_int8, and embeddings_int1 fields.

    UploadDataAndCreateIndex.java
    1import com.mongodb.client.MongoClient;
    2import com.mongodb.client.MongoClients;
    3import com.mongodb.client.MongoCollection;
    4import com.mongodb.client.MongoDatabase;
    5import com.mongodb.client.model.SearchIndexModel;
    6import com.mongodb.client.model.SearchIndexType;
    7import org.bson.Document;
    8import org.bson.conversions.Bson;
    9
    10import java.io.IOException;
    11import java.nio.file.Files;
    12import java.nio.file.Path;
    13import java.util.Collections;
    14import java.util.List;
    15import java.util.concurrent.TimeUnit;
    16import java.util.stream.StreamSupport;
    17
    18public class UploadDataAndCreateIndex {
    19
    20 private static final String MONGODB_URI = System.getenv("MONGODB_URI");
    21 private static final String DB_NAME = "<DATABASE-NAME>";
    22 private static final String COLLECTION_NAME = "<COLLECTION-NAME>";
    23 private static final String INDEX_NAME = "<INDEX-NAME>";
    24
    25 public static void main(String[] args) {
    26 try (MongoClient mongoClient = MongoClients.create(MONGODB_URI)) {
    27 storeEmbeddings(mongoClient);
    28 setupVectorSearchIndex(mongoClient);
    29 } catch (IOException | InterruptedException e) {
    30 e.printStackTrace();
    31 }
    32 }
    33
    34 // Upload the documents in the file to the given MongoDB namespace
    35 public static void storeEmbeddings(MongoClient client) throws IOException {
    36 MongoDatabase database = client.getDatabase(DB_NAME);
    37 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    38
    39 String fileContent = Files.readString(Path.of("embeddings.json"));
    40 List<Document> documents = parseDocuments(fileContent);
    41
    42 collection.insertMany(documents);
    43 System.out.println("Inserted documents into MongoDB");
    44 }
    45
    46 private static List<Document> parseDocuments(String jsonContent) throws IOException {
    47 Document rootDoc = Document.parse(jsonContent);
    48 return rootDoc.getList("data", Document.class);
    49 }
    50
    51 // Create the Vector Search index
    52 public static void setupVectorSearchIndex(MongoClient client) throws InterruptedException {
    53 MongoDatabase database = client.getDatabase(DB_NAME);
    54 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    55
    56 Bson definition = new Document(
    57 "fields",
    58 List.of(
    59 new Document("type", "vector")
    60 .append("path", "embeddings_float32")
    61 .append("numDimensions", 1024)
    62 .append("similarity", "dotProduct"),
    63 new Document("type", "vector")
    64 .append("path", "embeddings_int8")
    65 .append("numDimensions", 1024)
    66 .append("similarity", "dotProduct"),
    67 new Document("type", "vector")
    68 .append("path", "embeddings_int1")
    69 .append("numDimensions", 1024)
    70 .append("similarity", "euclidean")
    71 )
    72 );
    73
    74 SearchIndexModel indexModel = new SearchIndexModel(
    75 INDEX_NAME,
    76 definition,
    77 SearchIndexType.vectorSearch()
    78 );
    79
    80 List<String> result = collection.createSearchIndexes(Collections.singletonList(indexModel));
    81 System.out.println("Successfully created vector index named: " + result.get(0));
    82 System.out.println("It may take up to a minute for the index to leave the BUILDING status and become queryable.");
    83
    84 System.out.println("Polling to confirm the index has changed from the BUILDING status.");
    85 waitForIndex(collection, INDEX_NAME);
    86 }
    87
    88 // Wait for the index build to complete
    89 public static <T> boolean waitForIndex(final MongoCollection<T> collection, final String indexName) {
    90 long startTime = System.nanoTime();
    91 long timeoutNanos = TimeUnit.SECONDS.toNanos(60);
    92 while (System.nanoTime() - startTime < timeoutNanos) {
    93 Document indexRecord = StreamSupport.stream(collection.listSearchIndexes().spliterator(), false)
    94 .filter(index -> indexName.equals(index.getString("name")))
    95 .findAny().orElse(null);
    96 if (indexRecord != null) {
    97 if ("FAILED".equals(indexRecord.getString("status"))) {
    98 throw new RuntimeException("Search index has FAILED status.");
    99 }
    100 if (indexRecord.getBoolean("queryable")) {
    101 System.out.println(indexName + " index is ready to query");
    102 return true;
    103 }
    104 }
    105 try {
    106 Thread.sleep(100); // busy-wait, avoid in production
    107 } catch (InterruptedException e) {
    108 Thread.currentThread().interrupt();
    109 throw new RuntimeException(e);
    110 }
    111 }
    112 return false;
    113 }
    114}
  3. Replace the following placeholder values in the code and save the file.

    <DATABASE-NAME>

    Name of the database in your Atlas cluster.

    <COLLECTION-NAME>

    Name of the collection where you want to upload the data.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac UploadDataAndCreateIndex.java
    java UploadDataAndCreateIndex
    Inserted documents into MongoDB
    Successfully created vector index named: <INDEX_NAME>
    It may take up to a minute for the index to leave the BUILDING status and become queryable.
    Polling to confirm the index has changed from the BUILDING status.
    <INDEX_NAME> index is ready to query
  5. Log in to your Atlas cluster and verify the following:

    • Data in the namespace.

    • Atlas Vector Search index for the collection.

5

To test your embeddings, you can run a query against your collection. Use an embedding model provider to generate float, int8, and int1 embeddings for your query text. The following sample code uses Voyage AI's voyage-3-large REST API to generate full-precision vectors. After generating the embeddings, use the MongoDB Java driver to convert your native vector embedding to BSON vectors and run $vectorSearch query against the collection.

  1. Create a new file named CreateEmbeddingsAndRunQuery.java in your Java project.

    touch CreateEmbeddingsAndRunQuery.java
  2. Copy and paste the following code in the CreateEmbeddingsAndRunQuery.java file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's voyage-3-large embedding model.

    • Converts the embeddings to BSON binData vectors by using MongoDB Java driver.

    • Runs the query against your collection.

    CreateEmbeddingsAndRunQuery.java
    1import okhttp3.*;
    2import com.mongodb.client.MongoClient;
    3import com.mongodb.client.MongoClients;
    4import com.mongodb.client.MongoCollection;
    5import com.mongodb.client.MongoDatabase;
    6import org.bson.BinaryVector;
    7import org.bson.Document;
    8import org.bson.conversions.Bson;
    9import org.json.JSONArray;
    10import org.json.JSONObject;
    11
    12import java.io.IOException;
    13import java.util.*;
    14import java.util.concurrent.TimeUnit;
    15
    16import static com.mongodb.client.model.Aggregates.project;
    17import static com.mongodb.client.model.Aggregates.vectorSearch;
    18import static com.mongodb.client.model.Projections.fields;
    19import static com.mongodb.client.model.Projections.include;
    20import static com.mongodb.client.model.Projections.exclude;
    21import static com.mongodb.client.model.Projections.metaVectorSearchScore;
    22import static com.mongodb.client.model.search.SearchPath.fieldPath;
    23import static com.mongodb.client.model.search.VectorSearchOptions.approximateVectorSearchOptions;
    24import static java.util.Arrays.asList;
    25
    26public class CreateEmbeddingsAndRunQuery {
    27
    28 // Configurations
    29 private static final String VOYAGE_API_KEY = System.getenv("VOYAGE_API_KEY");
    30 private static final String MONGODB_URI = System.getenv("MONGODB_URI");
    31 private static final String DB_NAME = "<DATABASE-NAME>";
    32 private static final String COLLECTION_NAME = "<COLLECTION-NAME>";
    33 private static final String VECTOR_INDEX_NAME = "<INDEX-NAME>";
    34 private static final String DATA_FIELD_NAME = "<DATA-FIELD-NAME>";
    35 private static final String QUERY_TEXT = "<QUERY-TEXT>";
    36
    37 // Voyage AI API Endpoint
    38 private static final String VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
    39
    40 // Timeout values for API requests
    41 private static final int CONNECTION_TIMEOUT = 30;
    42 private static final int READ_TIMEOUT = 60;
    43
    44 public static void main(String[] args) {
    45 if (VOYAGE_API_KEY == null || VOYAGE_API_KEY.isEmpty()) {
    46 throw new RuntimeException("API key not found. Set VOYAGE_API_KEY in your environment.");
    47 }
    48 if (MONGODB_URI == null || MONGODB_URI.isEmpty()) {
    49 throw new RuntimeException("MongoDB URI not found. Set MONGODB_URI in your environment.");
    50 }
    51
    52 String queryText = <QUERY-TEXT>; // Query text dynamically provided by the user
    53
    54 try {
    55 CreateEmbeddingsAndRunQuery processor = new CreateEmbeddingsAndRunQuery();
    56
    57 System.out.println("Fetching embeddings...");
    58 Document bsonEmbeddings = processor.fetchEmbeddingsForQuery(queryText);
    59
    60 System.out.println("Using embeddings in vector search queries...");
    61 processor.runVectorSearchQuery(bsonEmbeddings);
    62
    63 } catch (Exception e) {
    64 e.printStackTrace();
    65 }
    66 }
    67
    68 // Fetch embeddings from Voyage AI API for multiple output data types
    69 private Document fetchEmbeddingsForQuery(String queryText) {
    70 OkHttpClient client = new OkHttpClient.Builder()
    71 .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
    72 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
    73 .build();
    74
    75 List<List<List<Integer>>> embeddingsByOutputType = new ArrayList<>();
    76 List<String> outputDtypes = List.of("float", "int8", "ubinary"); // Supported output data types
    77
    78 try {
    79 for (String dtype : outputDtypes) {
    80 String responseBody = sendRequest(client, VOYAGE_API_KEY, queryText, dtype);
    81 embeddingsByOutputType.add(parseEmbeddings(responseBody, dtype));
    82 }
    83 } catch (IOException e) {
    84 throw new RuntimeException("Error fetching embeddings: " + e.getMessage(), e);
    85 }
    86
    87 return convertEmbeddingsToBson(queryText, embeddingsByOutputType); // Convert embeddings to BSON format
    88 }
    89
    90 // Send API request to Voyage AI to generate embeddings for a specific output data type
    91 private String sendRequest(OkHttpClient client, String apiKey, String queryText, String outputDtype) throws IOException {
    92 String requestBody = new JSONObject()
    93 .put("input", List.of(queryText)) // Dynamic user query text as input
    94 .put("model", "voyage-3-large") // Model type
    95 .put("input_type", "query") // Input type for query
    96 .put("output_dtype", outputDtype)
    97 .toString();
    98
    99 Request request = new Request.Builder()
    100 .url(VOYAGE_API_URL)
    101 .post(RequestBody.create(requestBody, MediaType.get("application/json")))
    102 .addHeader("Authorization", "Bearer " + apiKey)
    103 .build();
    104
    105 try (Response response = client.newCall(request).execute()) {
    106 if (!response.isSuccessful()) {
    107 throw new IOException("API error: HTTP " + response.code());
    108 }
    109 return response.body().string();
    110 }
    111 }
    112
    113 // Parse embeddings from API response
    114 private static List<List<Integer>> parseEmbeddings(String responseBody, String outputDtype) {
    115 JSONObject responseJson = new JSONObject(responseBody);
    116 JSONArray dataArray = responseJson.optJSONArray("data");
    117
    118 if (dataArray == null) {
    119 throw new RuntimeException("Invalid response format: 'data' field missing.");
    120 }
    121
    122 List<List<Integer>> embeddings = new ArrayList<>();
    123 for (int i = 0; i < dataArray.length(); i++) {
    124 JSONArray embeddingVector = dataArray.getJSONObject(i).getJSONArray("embedding");
    125
    126 List<Integer> vector = new ArrayList<>();
    127 for (int j = 0; j < embeddingVector.length(); j++) {
    128 int value = embeddingVector.getInt(j);
    129
    130 // Handle binary quantization offset
    131 if ("binary".equals(outputDtype)) {
    132 value = value - 128; // Offset binary method (signed int8 representation)
    133 }
    134
    135 vector.add(value);
    136 }
    137 embeddings.add(vector);
    138 }
    139 return embeddings;
    140 }
    141
    142 // Convert embeddings into BSON format
    143 private Document convertEmbeddingsToBson(String queryText, List<List<List<Integer>>> embeddingsByOutputType) {
    144 Document embedding = new Document()
    145 .append("text", queryText)
    146 .append("embeddings_float32", BinaryVector.floatVector(listToFloatArray(embeddingsByOutputType.get(0).get(0))))
    147 .append("embeddings_int8", BinaryVector.int8Vector(listToByteArray(embeddingsByOutputType.get(1).get(0))))
    148 .append("embeddings_int1", BinaryVector.packedBitVector(listToByteArray(embeddingsByOutputType.get(2).get(0)), (byte) 0));
    149
    150 return new Document("data", List.of(embedding));
    151 }
    152
    153 // Run MongoDB vector search query using the generated embeddings
    154 private void runVectorSearchQuery(Document bsonEmbeddings) {
    155 try (MongoClient mongoClient = MongoClients.create(MONGODB_URI)) {
    156 MongoDatabase database = mongoClient.getDatabase(DB_NAME);
    157 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    158
    159 List<Document> embeddedDocuments = bsonEmbeddings.getList("data", Document.class);
    160
    161 for (Document embedding : embeddedDocuments) {
    162 for (String embeddingType : List.of("embeddings_float32", "embeddings_int8", "embeddings_int1")) {
    163 System.out.println("Results from " + embeddingType.replace("embeddings_", "") + " embeddings:");
    164
    165 List<Bson> pipeline = asList(
    166 vectorSearch(
    167 fieldPath(embeddingType),
    168 embedding.get(embeddingType, BinaryVector.class),
    169 VECTOR_INDEX_NAME,
    170 2, approximateVectorSearchOptions(5)
    171 ),
    172 project(fields(
    173 exclude("_id"),
    174 include(DATA_FIELD_NAME),
    175 metaVectorSearchScore("vectorSearchScore"))));
    176
    177 List<Document> results = collection.aggregate(pipeline).into(new ArrayList<>());
    178
    179 for (Document result : results) {
    180 System.out.println(result.toJson());
    181 }
    182 }
    183 }
    184 }
    185 }
    186
    187 private static float[] listToFloatArray(List<Integer> list) {
    188 float[] array = new float[list.size()];
    189 for (int i = 0; i < list.size(); i++) {
    190 array[i] = list.get(i).floatValue();
    191 }
    192 return array;
    193 }
    194
    195 private static byte[] listToByteArray(List<Integer> list) {
    196 byte[] array = new byte[list.size()];
    197 for (int i = 0; i < list.size(); i++) {
    198 array[i] = list.get(i).byteValue();
    199 }
    200 return array;
    201 }
    202}
  3. Replace the following placeholder values in the code and save the file.

    <DATABASE-NAME>

    Name of the database in your Atlas cluster.

    <COLLECTION-NAME>

    Name of the collection where you ingested the data.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

    <DATA-FIELD-NAME>

    Name of the field that contain the text from which you generated embeddings. For this example, use text.

    <QUERY-TEXT>

    Text for the query. For this example, use science fact.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac CreateEmbeddingsAndRunQuery.java
    java CreateEmbeddingsAndRunQuery
    Fetching embeddings...
    Using embeddings in vector search queries...
    {"text": "The Great Wall of China is visible from space.", "vectorSearchScore": 0.5}
    {"text": "The Eiffel Tower was completed in Paris in 1889.", "vectorSearchScore": 0.5}
    Results from int8 embeddings:
    {"text": "The Great Wall of China is visible from space.", "vectorSearchScore": 0.5051995515823364}
    {"text": "Mount Everest is the highest peak on Earth at 8,848m.", "vectorSearchScore": 0.5044659972190857}
    Results from int1 embeddings:
    {"text": "The Great Wall of China is visible from space.", "vectorSearchScore": 0.6845703125}
    {"text": "Mount Everest is the highest peak on Earth at 8,848m.", "vectorSearchScore": 0.6650390625}

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

1
  1. From your IDE, create a Java project using Maven or Gradle.

  2. Add the following dependencies, depending on your package manager:

    If you are using Maven, add the following dependencies to the dependencies array in your project's pom.xml file:

    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>5.3.1</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.16</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.16</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20250517</version>
    </dependency>
    <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
    </dependency>
    </dependencies>

    If you are using Gradle, add the following to the dependencies array in your project's build.gradle file:

    build.gradle
    dependencies {
    // JUnit for testing
    testImplementation 'junit:junit:3.8.1'
    // MongoDB synchronous driver
    implementation 'org.mongodb:mongodb-driver-sync:5.3.1'
    // SLF4J API for logging
    implementation 'org.slf4j:slf4j-api:2.0.16'
    // SLF4J Simple Logger (scope: test)
    testImplementation 'org.slf4j:slf4j-simple:2.0.16'
    // JSON library
    implementation 'org.json:json:20210307'
    // HTTP client for Java
    implementation 'com.squareup.okhttp3:okhttp:4.12.0' // Or the latest version
    }
  3. Run your package manager to install the dependencies to your project.

2

Note

This example sets the variables for the project in the IDE. Production applications might manage environment variables through a deployment configuration, CI/CD pipeline, or secrets manager, but you can adapt the provided code to fit your use case.

In your IDE, create a new configuration template and add the following variables to your project:

  • If you are using IntelliJ IDEA, create a new Application run configuration template, then add your variables as semicolon-separated values in the Environment variables field (for example, FOO=123;BAR=456). Apply the changes and click OK.

    To learn more, see the Create a run/debug configuration from a template section of the IntelliJ IDEA documentation.

  • If you are using Eclipse, create a new Java Application launch configuration, then add each variable as a new key-value pair in the Environment tab. Apply the changes and click OK.

    To learn more, see the Creating a Java application launch configuration section of the Eclipse IDE documentation.

Environment variables
VOYAGE_API_KEY=<api-key>
MONGODB_URI=<connection-string>

Update the placeholders with the following values:

  • Replace the <api-key> placeholder value with your Voyage AI API key.

  • Replace the <connection-string> placeholder value with the SRV connection string for your Atlas cluster.

    Your connection string should use the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
3

You can use an embedding model provider to generate float, int8, and int1 embeddings for your data and then use the MongoDB Java driver to convert your native vector embedding to BSON vectors. The following sample code uses Voyage AI's REST API to generate full-precision vectors from the data in the sample_airbnb.listingsAndReviews namespace.

  1. Create a new file named GenerateAndConvertEmbeddings.java in your Java project.

    touch GenerateAndConvertEmbeddings.java
  2. Copy and paste the following code in the GenerateAndConvertEmbeddings.java file.

    This code does the following:

    • Gets the summary field from 50 documents in the sample_airbnb.listingsAndReviews namespace.

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's voyage-3-large embedding model.

    • Converts the embeddings to BSON binData vectors by using MongoDB Java driver.

    • Creates a file named embeddings.json and saves the data with embeddings in the file.

    GenerateAndConvertEmbeddings.java
    1import okhttp3.*;
    2
    3import com.mongodb.client.FindIterable;
    4import com.mongodb.client.MongoClient;
    5import com.mongodb.client.MongoClients;
    6import com.mongodb.client.MongoCollection;
    7import com.mongodb.client.MongoDatabase;
    8import org.bson.Document;
    9import org.bson.BinaryVector;
    10import org.slf4j.Logger;
    11import org.slf4j.LoggerFactory;
    12import org.json.JSONArray;
    13import org.json.JSONObject;
    14
    15import java.io.FileOutputStream;
    16import java.io.IOException;
    17import java.util.*;
    18import java.util.concurrent.TimeUnit;
    19
    20public class GenerateAndConvertEmbeddings {
    21 private static final Logger logger = LoggerFactory.getLogger(GenerateAndConvertEmbeddings.class);
    22
    23 // Configuration settings
    24 private static final String VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings"; // Voyage AI API URL
    25 private static final String VOYAGE_API_KEY = System.getenv("VOYAGE_API_KEY"); // Voyage API key
    26 private static final String MONGODB_URI = System.getenv("MONGODB_URI"); // MongoDB connection URI
    27
    28 // Timeout values for API requests
    29 private static final int CONNECTION_TIMEOUT = 30; // Timeout for API requests
    30 private static final int READ_TIMEOUT = 60; // Timeout for API responses
    31
    32 public static void main(String[] args) {
    33 try {
    34 List<String> summaries = fetchSummariesFromMongoDB();
    35 if (summaries.isEmpty()) {
    36 throw new RuntimeException("No summaries retrieved from MongoDB.");
    37 }
    38
    39 Document bsonEmbeddings = fetchEmbeddingsFromVoyage(summaries, VOYAGE_API_KEY);
    40 if (bsonEmbeddings == null || bsonEmbeddings.isEmpty()) {
    41 throw new RuntimeException("Failed to fetch embeddings.");
    42 }
    43
    44 convertAndSaveEmbeddings(bsonEmbeddings);
    45 } catch (Exception e) {
    46 logger.error("Unexpected error: {}", e.getMessage(), e);
    47 }
    48 }
    49
    50 // Fetch summaries from MongoDB collection
    51 private static List<String> fetchSummariesFromMongoDB() {
    52 List<String> summaries = new ArrayList<>();
    53 if (MONGODB_URI == null || MONGODB_URI.isEmpty()) {
    54 throw new RuntimeException("MongoDB URI is not set.");
    55 }
    56 logger.info("Connecting to MongoDB at URI: {}", MONGODB_URI);
    57
    58 try (MongoClient mongoClient = MongoClients.create(MONGODB_URI)) {
    59 String dbName = "sample_airbnb";
    60 String collName = "listingsAndReviews";
    61 MongoDatabase database = mongoClient.getDatabase(dbName);
    62 MongoCollection<Document> collection = database.getCollection(collName);
    63
    64 // Filter to exclude null or empty summaries
    65 Document filter = new Document("summary", new Document("$nin", Arrays.asList(null, "")));
    66 FindIterable<Document> documentsCursor = collection.find(filter).limit(50);
    67
    68 for (Document doc : documentsCursor) {
    69 String summary = doc.getString("summary");
    70 if (summary != null && !summary.isEmpty()) {
    71 summaries.add(summary);
    72 }
    73 }
    74 logger.info("Retrieved {} summaries from MongoDB.", summaries.size());
    75 } catch (Exception e) {
    76 logger.error("Error fetching from MongoDB: {}", e.getMessage(), e);
    77 throw new RuntimeException("Failed to fetch data from MongoDB", e);
    78 }
    79 return summaries;
    80 }
    81
    82 // Fetch embeddings from Voyage AI API for the given data input
    83 private static Document fetchEmbeddingsFromVoyage(List<String> data, String apiKey) {
    84 if (apiKey == null || apiKey.isEmpty()) {
    85 throw new RuntimeException("API key is not set.");
    86 }
    87
    88 OkHttpClient client = new OkHttpClient.Builder()
    89 .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
    90 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
    91 .build();
    92
    93 List<List<List<Integer>>> embeddingsByOutputType = new ArrayList<>();
    94 List<String> outputDtypes = List.of("float", "int8", "ubinary");
    95
    96 try {
    97 for (String dtype : outputDtypes) {
    98 String responseBody = sendRequest(client, apiKey, data, dtype);
    99 embeddingsByOutputType.add(parseEmbeddings(responseBody, dtype));
    100 }
    101 } catch (IOException e) {
    102 logger.error("Error fetching embeddings: {}", e.getMessage(), e);
    103 throw new RuntimeException("Error fetching embeddings from Voyage AI.", e);
    104 }
    105
    106 // Convert embeddings to BSON
    107 return convertEmbeddingsToBson(data, embeddingsByOutputType);
    108 }
    109
    110 // Send API request to Voyage AI
    111 private static String sendRequest(OkHttpClient client, String apiKey, List<String> inputData, String outputDtype) throws IOException {
    112 String requestBody = new JSONObject()
    113 .put("input", inputData)
    114 .put("model", "voyage-3-large")
    115 .put("input_type", "document")
    116 .put("output_dtype", outputDtype)
    117 .put("output_dimension", 1024)
    118 .toString();
    119
    120 Request request = new Request.Builder()
    121 .url(VOYAGE_API_URL)
    122 .post(RequestBody.create(requestBody, MediaType.get("application/json")))
    123 .addHeader("Authorization", "Bearer " + apiKey)
    124 .build();
    125
    126 try (Response response = client.newCall(request).execute()) {
    127 if (!response.isSuccessful()) {
    128 throw new IOException("API error: HTTP " + response.code());
    129 }
    130 return response.body().string();
    131 }
    132 }
    133
    134 // Parse embeddings from Voyage AI API response
    135 private static List<List<Integer>> parseEmbeddings(String responseBody, String outputDtype) {
    136 JSONObject responseJson = new JSONObject(responseBody);
    137 JSONArray dataArray = responseJson.optJSONArray("data");
    138
    139 if (dataArray == null) {
    140 throw new RuntimeException("Invalid response format: 'data' field missing.");
    141 }
    142
    143 List<List<Integer>> embeddings = new ArrayList<>();
    144 for (int i = 0; i < dataArray.length(); i++) {
    145 JSONArray embeddingVector = dataArray.getJSONObject(i).getJSONArray("embedding");
    146
    147 List<Integer> vector = new ArrayList<>();
    148 for (int j = 0; j < embeddingVector.length(); j++) {
    149 int value = embeddingVector.getInt(j);
    150
    151 // Handle binary quantization offset for signed int8 representations
    152 if ("binary".equals(outputDtype)) {
    153 value = value - 128; // Offset binary method
    154 }
    155
    156 vector.add(value);
    157 }
    158 embeddings.add(vector);
    159 }
    160 return embeddings;
    161 }
    162
    163 // Convert fetched embeddings into BSON format
    164 private static Document convertEmbeddingsToBson(List<String> inputData, List<List<List<Integer>>> embeddingsByOutputType) {
    165 List<Document> bsonEmbeddings = new ArrayList<>();
    166 for (int i = 0; i < inputData.size(); i++) {
    167 Document embedding = new Document()
    168 .append("text", inputData.get(i))
    169 .append("embeddings_float32", BinaryVector.floatVector(listToFloatArray(embeddingsByOutputType.get(0).get(i))))
    170 .append("embeddings_int8", BinaryVector.int8Vector(listToByteArray(embeddingsByOutputType.get(1).get(i))))
    171 .append("embeddings_int1", BinaryVector.packedBitVector(listToByteArray(embeddingsByOutputType.get(2).get(i)), (byte) 0));
    172 bsonEmbeddings.add(embedding);
    173 }
    174 return new Document("data", bsonEmbeddings);
    175 }
    176
    177 // Save BSON embeddings to a JSON file
    178 private static void convertAndSaveEmbeddings(Document bsonEmbeddings) {
    179 try (FileOutputStream fos = new FileOutputStream("embeddings.json")) {
    180 fos.write(bsonEmbeddings.toJson().getBytes());
    181 logger.info("Embeddings with BSON vectors have been saved to embeddings.json");
    182 } catch (IOException e) {
    183 logger.error("Error writing embeddings to file: {}", e.getMessage(), e);
    184 }
    185 }
    186
    187 private static float[] listToFloatArray(List<Integer> list) {
    188 float[] array = new float[list.size()];
    189 for (int i = 0; i < list.size(); i++) {
    190 array[i] = list.get(i).floatValue();
    191 }
    192 return array;
    193 }
    194
    195 private static byte[] listToByteArray(List<Integer> list) {
    196 byte[] array = new byte[list.size()];
    197 for (int i = 0; i < list.size(); i++) {
    198 array[i] = list.get(i).byteValue();
    199 }
    200 return array;
    201 }
    202}
  3. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac GenerateAndConvertEmbeddings.java
    java GenerateAndConvertEmbeddings
    [main] INFO GenerateAndConvertEmbeddings - Connecting to MongoDB at URI: <CONNECTION-STRING>
    ...
    [main] INFO GenerateAndConvertEmbeddings - Retrieved 50 summaries from MongoDB.
    [main] INFO GenerateAndConvertEmbeddings - Embeddings with BSON vectors have been saved to embeddings.json
  4. Verify the embeddings in the embeddings.json file.

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

4

You must upload your data and embeddings to a collection in your Atlas cluster and create an Atlas Vector Search index on the data to run $vectorSearch queries against the data.

  1. Create a new file named UploadDataAndCreateIndex.java in your Java project.

    touch UploadDataAndCreateIndex.java
  2. Copy and paste the following code in the UploadDataAndCreateIndex.java file.

    This code does the following:

    • Uploads the float32, int8, and int1 embeddings in the embeddings.json file to your Atlas cluster.

    • Creates an Atlas Vector Search index on the embeddings.float32, embeddings.int8, and embeddings.int1 fields.

    UploadDataAndCreateIndex.java
    1import com.mongodb.client.MongoClient;
    2import com.mongodb.client.MongoClients;
    3import com.mongodb.client.MongoCollection;
    4import com.mongodb.client.MongoDatabase;
    5import com.mongodb.client.model.SearchIndexModel;
    6import com.mongodb.client.model.SearchIndexType;
    7
    8import org.bson.Document;
    9import org.bson.conversions.Bson;
    10import org.bson.BinaryVector; // Import the BinaryVector
    11
    12import java.io.IOException;
    13import java.nio.file.Files;
    14import java.nio.file.Path;
    15import java.util.Collections;
    16import java.util.List;
    17import java.util.concurrent.TimeUnit;
    18import java.util.stream.StreamSupport;
    19
    20public class UploadDataAndCreateIndex {
    21
    22 private static final String MONGODB_URI = System.getenv("MONGODB_URI");
    23 private static final String DB_NAME = "<DATABASE-NAME>";
    24 private static final String COLLECTION_NAME = "<COLLECTION-NAME>";
    25 private static final String INDEX_NAME = "<INDEX-NAME>";
    26
    27 public static void main(String[] args) {
    28 try (MongoClient mongoClient = MongoClients.create(MONGODB_URI)) {
    29 uploadEmbeddingsData(mongoClient);
    30 setupVectorSearchIndex(mongoClient);
    31 } catch (Exception e) {
    32 e.printStackTrace();
    33 }
    34 }
    35
    36 // Upload the embeddings in the file to the given MongoDB namespace
    37 public static void uploadEmbeddingsData(MongoClient mongoClient) throws IOException {
    38 MongoDatabase database = mongoClient.getDatabase(DB_NAME);
    39 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    40 String filePath = "embeddings.json";
    41 String fileContent = Files.readString(Path.of(filePath));
    42
    43 Document rootDoc = Document.parse(fileContent);
    44 List<Document> embeddingsDocs = rootDoc.getList("data", Document.class);
    45
    46 for (Document doc : embeddingsDocs) {
    47 // Retrieve the string value from the document
    48 String summary = doc.getString("text");
    49
    50 // Get the BinaryVector objects from the document
    51 BinaryVector embeddingsFloat32 = doc.get("embeddings_float32", BinaryVector.class);
    52 BinaryVector embeddingsInt8 = doc.get("embeddings_int8", BinaryVector.class);
    53 BinaryVector embeddingsInt1 = doc.get("embeddings_int1", BinaryVector.class);
    54
    55 // Create filter and update documents
    56 Document filter = new Document("summary", summary);
    57 Document update = new Document("$set", new Document("summary", summary)
    58 .append("embeddings_float32", embeddingsFloat32)
    59 .append("embeddings_int8", embeddingsInt8)
    60 .append("embeddings_int1", embeddingsInt1));
    61
    62 // Perform update operation with upsert option
    63 collection.updateOne(filter, update, new com.mongodb.client.model.UpdateOptions().upsert(true));
    64 System.out.println("Processed document with summary: " + summary);
    65 }
    66 }
    67
    68 // Create a Vector Search index
    69 public static void setupVectorSearchIndex(MongoClient client) throws InterruptedException {
    70 MongoDatabase database = client.getDatabase(DB_NAME);
    71 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    72 // Define the index details
    73 Bson definition = new Document(
    74 "fields",
    75 List.of(
    76 new Document("type", "vector")
    77 .append("path", "embeddings_float32")
    78 .append("numDimensions", 1024)
    79 .append("similarity", "dotProduct"),
    80 new Document("type", "vector")
    81 .append("path", "embeddings_int8")
    82 .append("numDimensions", 1024)
    83 .append("similarity", "dotProduct"),
    84 new Document("type", "vector")
    85 .append("path", "embeddings_int1")
    86 .append("numDimensions", 1024)
    87 .append("similarity", "euclidean")
    88 )
    89 );
    90 // Define the index model
    91 SearchIndexModel indexModel = new SearchIndexModel(
    92 INDEX_NAME,
    93 definition,
    94 SearchIndexType.vectorSearch()
    95 );
    96 // Create the index using the defined model
    97 List<String> result = collection.createSearchIndexes(Collections.singletonList(indexModel));
    98 System.out.println("Successfully created vector index named: " + result.get(0));
    99 System.out.println("It may take up to a minute for the index to leave the BUILDING status and become queryable.");
    100 // Wait for Atlas to build the index
    101 System.out.println("Polling to confirm the index has changed from the BUILDING status.");
    102 waitForIndex(collection, INDEX_NAME);
    103 }
    104
    105 // Wait for the index build to complete
    106 public static <T> boolean waitForIndex(final MongoCollection<T> collection, final String indexName) {
    107 long startTime = System.nanoTime();
    108 long timeoutNanos = TimeUnit.SECONDS.toNanos(60);
    109 while (System.nanoTime() - startTime < timeoutNanos) {
    110 Document indexRecord = StreamSupport.stream(collection.listSearchIndexes().spliterator(), false)
    111 .filter(index -> indexName.equals(index.getString("name")))
    112 .findAny().orElse(null);
    113 if (indexRecord != null) {
    114 if ("FAILED".equals(indexRecord.getString("status"))) {
    115 throw new RuntimeException("Search index has FAILED status.");
    116 }
    117 if (indexRecord.getBoolean("queryable")) {
    118 System.out.println(indexName + " index is ready to query");
    119 return true;
    120 }
    121 }
    122 try {
    123 Thread.sleep(100); // busy-wait, avoid in production
    124 } catch (InterruptedException e) {
    125 Thread.currentThread().interrupt();
    126 throw new RuntimeException(e);
    127 }
    128 }
    129 return false;
    130 }
    131}
  3. Replace the following placeholder value in the code and save the file.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac UploadDataAndCreateIndex.java
    java UploadDataAndCreateIndex
    Processed document with summary: ...
    ...
    Successfully created vector index named: <INDEX_NAME>
    It may take up to a minute for the index to leave the BUILDING status and become queryable.
    Polling to confirm the index has changed from the BUILDING status.
    <INDEX_NAME> index is ready to query
  5. Log in to your Atlas cluster and verify the following:

    • Data in the namespace.

    • Atlas Vector Search index for the collection.

5

To test your embeddings, you can run a query against your collection. Use an embedding model provider to generate float, int8, and int1 embeddings for your query text. The following sample code uses Voyage AI's REST API to generate full-precision vectors. After generating the embeddings, use the MongoDB Java driver to convert your native vector embedding to BSON vectors and run $vectorSearch query against the collection.

  1. Create a new file named CreateEmbeddingsAndRunQuery.java in your Java project.

    touch CreateEmbeddingsAndRunQuery.java
  2. Copy and paste the following code in the CreateEmbeddingsAndRunQuery.java file.

    This code does the following:

    • Generates the float32, int8, and ubinary vector embeddings by using Voyage AI's voyage-3-large embedding model.

    • Converts the embeddings to BSON binData vectors by using MongoDB Java driver.

    • Runs the query against your collection and returns the results.

    CreateEmbeddingsAndRunQuery.java
    1import okhttp3.*;
    2import com.mongodb.client.MongoClient;
    3import com.mongodb.client.MongoClients;
    4import com.mongodb.client.MongoCollection;
    5import com.mongodb.client.MongoDatabase;
    6import org.bson.BinaryVector;
    7import org.bson.Document;
    8import org.bson.conversions.Bson;
    9import org.json.JSONArray;
    10import org.json.JSONObject;
    11
    12import java.io.IOException;
    13import java.util.*;
    14import java.util.concurrent.TimeUnit;
    15
    16import static com.mongodb.client.model.Aggregates.project;
    17import static com.mongodb.client.model.Aggregates.vectorSearch;
    18import static com.mongodb.client.model.Projections.fields;
    19import static com.mongodb.client.model.Projections.include;
    20import static com.mongodb.client.model.Projections.exclude;
    21import static com.mongodb.client.model.Projections.metaVectorSearchScore;
    22import static com.mongodb.client.model.search.SearchPath.fieldPath;
    23import static com.mongodb.client.model.search.VectorSearchOptions.approximateVectorSearchOptions;
    24import static java.util.Arrays.asList;
    25
    26public class CreateEmbeddingsAndRunQuery {
    27
    28 // Configurations
    29 private static final String VOYAGE_API_KEY = System.getenv("VOYAGE_API_KEY");
    30 private static final String MONGODB_URI = System.getenv("MONGODB_URI");
    31 private static final String DB_NAME = "<DATABASE-NAME>";
    32 private static final String COLLECTION_NAME = "<COLLECTION-NAME>";
    33 private static final String VECTOR_INDEX_NAME = "<INDEX-NAME>";
    34 private static final String DATA_FIELD_NAME = "<DATA-FIELD-NAME>";
    35 private static final String QUERY_TEXT = "<QUERY-TEXT>";
    36
    37 // Voyage AI API Endpoint
    38 private static final String VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
    39
    40 // Timeout values for API requests
    41 private static final int CONNECTION_TIMEOUT = 30;
    42 private static final int READ_TIMEOUT = 60;
    43
    44 public static void main(String[] args) {
    45 if (VOYAGE_API_KEY == null || VOYAGE_API_KEY.isEmpty()) {
    46 throw new RuntimeException("API key not found. Set VOYAGE_API_KEY in your environment.");
    47 }
    48 if (MONGODB_URI == null || MONGODB_URI.isEmpty()) {
    49 throw new RuntimeException("MongoDB URI not found. Set MONGODB_URI in your environment.");
    50 }
    51
    52 String queryText = <QUERY-TEXT>; // Query text dynamically provided by the user
    53
    54 try {
    55 CreateEmbeddingsAndRunQuery processor = new CreateEmbeddingsAndRunQuery();
    56
    57 System.out.println("Fetching embeddings...");
    58 Document bsonEmbeddings = processor.fetchEmbeddingsForQuery(queryText);
    59
    60 System.out.println("Using embeddings in vector search queries...");
    61 processor.runVectorSearchQuery(bsonEmbeddings);
    62
    63 } catch (Exception e) {
    64 e.printStackTrace();
    65 }
    66 }
    67
    68 // Fetch embeddings from Voyage AI API for multiple output data types
    69 private Document fetchEmbeddingsForQuery(String queryText) {
    70 OkHttpClient client = new OkHttpClient.Builder()
    71 .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
    72 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
    73 .build();
    74
    75 List<List<List<Integer>>> embeddingsByOutputType = new ArrayList<>();
    76 List<String> outputDtypes = List.of("float", "int8", "ubinary"); // Supported output data types
    77
    78 try {
    79 for (String dtype : outputDtypes) {
    80 String responseBody = sendRequest(client, VOYAGE_API_KEY, queryText, dtype);
    81 embeddingsByOutputType.add(parseEmbeddings(responseBody, dtype));
    82 }
    83 } catch (IOException e) {
    84 throw new RuntimeException("Error fetching embeddings: " + e.getMessage(), e);
    85 }
    86
    87 return convertEmbeddingsToBson(queryText, embeddingsByOutputType); // Convert embeddings to BSON format
    88 }
    89
    90 // Send API request to Voyage AI to generate embeddings for a specific output data type
    91 private String sendRequest(OkHttpClient client, String apiKey, String queryText, String outputDtype) throws IOException {
    92 String requestBody = new JSONObject()
    93 .put("input", List.of(queryText)) // Dynamic user query text as input
    94 .put("model", "voyage-3-large") // Model type
    95 .put("input_type", "query") // Input type for query
    96 .put("output_dtype", outputDtype)
    97 .toString();
    98
    99 Request request = new Request.Builder()
    100 .url(VOYAGE_API_URL)
    101 .post(RequestBody.create(requestBody, MediaType.get("application/json")))
    102 .addHeader("Authorization", "Bearer " + apiKey)
    103 .build();
    104
    105 try (Response response = client.newCall(request).execute()) {
    106 if (!response.isSuccessful()) {
    107 throw new IOException("API error: HTTP " + response.code());
    108 }
    109 return response.body().string();
    110 }
    111 }
    112
    113 // Parse embeddings from API response
    114 private static List<List<Integer>> parseEmbeddings(String responseBody, String outputDtype) {
    115 JSONObject responseJson = new JSONObject(responseBody);
    116 JSONArray dataArray = responseJson.optJSONArray("data");
    117
    118 if (dataArray == null) {
    119 throw new RuntimeException("Invalid response format: 'data' field missing.");
    120 }
    121
    122 List<List<Integer>> embeddings = new ArrayList<>();
    123 for (int i = 0; i < dataArray.length(); i++) {
    124 JSONArray embeddingVector = dataArray.getJSONObject(i).getJSONArray("embedding");
    125
    126 List<Integer> vector = new ArrayList<>();
    127 for (int j = 0; j < embeddingVector.length(); j++) {
    128 int value = embeddingVector.getInt(j);
    129
    130 // Handle binary quantization offset
    131 if ("binary".equals(outputDtype)) {
    132 value = value - 128; // Offset binary method (signed int8 representation)
    133 }
    134
    135 vector.add(value);
    136 }
    137 embeddings.add(vector);
    138 }
    139 return embeddings;
    140 }
    141
    142 // Convert embeddings into BSON format
    143 private Document convertEmbeddingsToBson(String queryText, List<List<List<Integer>>> embeddingsByOutputType) {
    144 Document embedding = new Document()
    145 .append("text", queryText)
    146 .append("embeddings_float32", BinaryVector.floatVector(listToFloatArray(embeddingsByOutputType.get(0).get(0))))
    147 .append("embeddings_int8", BinaryVector.int8Vector(listToByteArray(embeddingsByOutputType.get(1).get(0))))
    148 .append("embeddings_int1", BinaryVector.packedBitVector(listToByteArray(embeddingsByOutputType.get(2).get(0)), (byte) 0));
    149
    150 return new Document("data", List.of(embedding));
    151 }
    152
    153 // Run MongoDB vector search query using the generated embeddings
    154 private void runVectorSearchQuery(Document bsonEmbeddings) {
    155 try (MongoClient mongoClient = MongoClients.create(MONGODB_URI)) {
    156 MongoDatabase database = mongoClient.getDatabase(DB_NAME);
    157 MongoCollection<Document> collection = database.getCollection(COLLECTION_NAME);
    158
    159 List<Document> embeddedDocuments = bsonEmbeddings.getList("data", Document.class);
    160
    161 for (Document embedding : embeddedDocuments) {
    162 for (String embeddingType : List.of("embeddings_float32", "embeddings_int8", "embeddings_int1")) {
    163 System.out.println("Results from " + embeddingType.replace("embeddings_", "") + " embeddings:");
    164
    165 List<Bson> pipeline = asList(
    166 vectorSearch(
    167 fieldPath(embeddingType),
    168 embedding.get(embeddingType, BinaryVector.class),
    169 VECTOR_INDEX_NAME,
    170 2, approximateVectorSearchOptions(5)
    171 ),
    172 project(fields(
    173 exclude("_id"),
    174 include(DATA_FIELD_NAME),
    175 metaVectorSearchScore("vectorSearchScore"))));
    176
    177 List<Document> results = collection.aggregate(pipeline).into(new ArrayList<>());
    178
    179 for (Document result : results) {
    180 System.out.println(result.toJson());
    181 }
    182 }
    183 }
    184 }
    185 }
    186
    187 private static float[] listToFloatArray(List<Integer> list) {
    188 float[] array = new float[list.size()];
    189 for (int i = 0; i < list.size(); i++) {
    190 array[i] = list.get(i).floatValue();
    191 }
    192 return array;
    193 }
    194
    195 private static byte[] listToByteArray(List<Integer> list) {
    196 byte[] array = new byte[list.size()];
    197 for (int i = 0; i < list.size(); i++) {
    198 array[i] = list.get(i).byteValue();
    199 }
    200 return array;
    201 }
    202}
  3. Replace the following placeholder values in the code and save the file.

    <DATABASE-NAME>

    Name of the database in your Atlas cluster. For this example, use sample_airbnb.

    <COLLECTION-NAME>

    Name of the collection where you ingested the data. For this example, use listingsAndReviews.

    <INDEX-NAME>

    Name of the Atlas Vector Search index for the collection.

    <DATA-FIELD-NAME>

    Name of the field that contain the text from which you generated embeddings. For this example, use summary.

    <QUERY-TEXT>

    Text for the query. For this example, use ocean view.

  4. Compile and run the file using your application run configuration.

    If you are using a terminal, run the following commands to compile and execute your program.

    javac CreateEmbeddingsAndRunQuery.java
    java CreateEmbeddingsAndRunQuery
    Fetching embeddings...
    Using embeddings in vector search queries...
    Results from float32 embeddings:
    {"summary": "Fantastic duplex apartment with three bedrooms, located in the historic area of Porto, Ribeira (Cube) - UNESCO World Heritage Site. Centenary building fully rehabilitated, without losing their original character.", "vectorSearchScore": 0.5}
    {"summary": "One bedroom + sofa-bed in quiet and bucolic neighbourhood right next to the Botanical Garden. Small garden, outside shower, well equipped kitchen and bathroom with shower and tub. Easy for transport with many restaurants and basic facilities in the area.", "vectorSearchScore": 0.5}
    Results from int8 embeddings:
    {"summary": "A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.", "vectorSearchScore": 0.5056195259094238}
    {"summary": "THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!", "vectorSearchScore": 0.5048412084579468}
    Results from int1 embeddings:
    {"summary": "A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.", "vectorSearchScore": 0.7119140625}
    {"summary": "A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.", "vectorSearchScore": 0.6787109375}

To learn more about generating embeddings and converting the embeddings to binData vectors, see How to Create Vector Embeddings.

1

Configure your project to use ES modules by adding "type": "module" to your package.json file and then saving it.

{
"type": "module",
// other fields...
}
2

Run the following command to install the MongoDB Node.js Driver and the dotenv package. This operation might take a few minutes to complete.

npm install mongodb dotenv

You must install Node.js v6.11 or later driver.

If necessary, you must also install libraries from your embedding model provider. In this tutorial, you use the Voyage AI REST API to generate embeddings. Therefore, you don't need to install any additional libraries for Voyage AI.

3

In a terminal window, run the following commands to create a new directory named my-quantization-project and initialize your project:

mkdir my-quantization-project
cd my-quantization-project
npm init -y
4
  1. To access the embedding model provider for generating and converting embeddings, set the environment variable for the embedding model provider's API key, if necessary.

    For using embeddings from Voyage AI, set up the VOYAGE_API_KEY environment variable.

    export VOYAGE_API_KEY="<VOYAGEAI-API-KEY>"

    If you don't set the environment variable, replace the <VOYAGE-API-KEY> in the sample code with the API key before running the code.

  2. To access Atlas cluster, set the MONGODB_URI environment variable.

    export MONGODB_URI="<CONNECTION-STRING>"

    Your connection string should be in the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

    If you don't set the environment variable, replace the <CONNECTION-STRING> in the sample code with your connection string before running the code.

5
  1. Create a file named get-embeddings.js to generate float32, int8, and int1 vector embeddings by using Voyage AI's embed API.

    touch get-embeddings.js
  2. Copy and paste the following code in the get-embeddings.js file.

    This code does the following:

    • Generates float32, int8, and int1 embeddings for the given data by using Voyage AI's voyage-3-large embedding model.

    • Stores the float32, int8, and int1 embeddings in fields named float, int8, and ubinary respectively.

    • Creates a file named embeddings.json and saves the embeddings in the file.

    get-embeddings.js
    1import { writeFile } from "fs/promises"; // For saving JSON files
    2
    3// Retrieve API key from environment or use placeholder value
    4const apiKey = process.env.VOYAGE_API_KEY || "<VOYAGE-API-KEY>";
    5
    6if (!apiKey || apiKey === "<VOYAGE-API-KEY>") {
    7 throw new Error("API key not found. Please set VOYAGE_API_KEY in your environment.");
    8}
    9
    10// Define the VoyageAI REST API endpoint
    11const apiEndpoint = "https://api.voyageai.com/v1/embeddings";
    12
    13/**
    14 * Fetch embeddings using VoyageAI REST API for a specific data type (output_dtype)
    15 */
    16async function fetchEmbeddings(data, model, outputDtype, dimension) {
    17 const response = await fetch(apiEndpoint, {
    18 method: "POST",
    19 headers: {
    20 "Content-Type": "application/json",
    21 Authorization: `Bearer ${apiKey}`,
    22 },
    23 body: JSON.stringify({
    24 input: data,
    25 model,
    26 input_type: "document",
    27 output_dtype: outputDtype,
    28 output_dimension: dimension,
    29 }),
    30 });
    31
    32 // Check for non-success status codes
    33 if (!response.ok) {
    34 const errorResponse = await response.text();
    35 throw new Error(`API request failed with status ${response.status}: ${errorResponse}`);
    36 }
    37
    38 const responseData = await response.json();
    39
    40 // Ensure the response contains valid data
    41 if (!responseData.data || !Array.isArray(responseData.data)) {
    42 throw new Error(`Invalid API response for dtype "${outputDtype}": 'data' array is missing.`);
    43 }
    44
    45 // Extract embeddings from the response
    46 const embeddings = responseData.data.map((item) => item.embedding);
    47
    48 // Validate embeddings
    49 if (!Array.isArray(embeddings) || embeddings.length !== data.length) {
    50 throw new Error(`Invalid embeddings received for dtype "${outputDtype}".`);
    51 }
    52
    53 return embeddings; // Return embeddings for the requested dtype
    54}
    55
    56/**
    57 * Generate embeddings for predefined texts and save them to a JSON file
    58 */
    59async function generateEmbeddings() {
    60 const data = [
    61 "The Great Wall of China is visible from space.",
    62 "The Eiffel Tower was completed in Paris in 1889.",
    63 "Mount Everest is the highest peak on Earth at 8,848m.",
    64 "Shakespeare wrote 37 plays and 154 sonnets during his lifetime.",
    65 "The Mona Lisa was painted by Leonardo da Vinci.",
    66 ];
    67
    68 const model = "voyage-3-large";
    69 const dimension = 1024; // Output embedding dimension
    70
    71 try {
    72 // Fetch embeddings for different output types
    73 const floatEmbeddings = await fetchEmbeddings(data, model, "float", dimension);
    74 const int8Embeddings = await fetchEmbeddings(data, model, "int8", dimension); // Use "int8" dtype
    75 const ubinaryEmbeddings = await fetchEmbeddings(data, model, "ubinary", dimension); // Use "ubinary" dtype
    76
    77 // Map embeddings to their corresponding texts
    78 const embeddingsData = data.map((text, index) => ({
    79 text,
    80 embeddings: {
    81 float: floatEmbeddings[index], // Store float embeddings
    82 int8: int8Embeddings[index], // Store int8 embeddings
    83 ubinary: ubinaryEmbeddings[index], // Store ubinary embeddings
    84 },
    85 }));
    86
    87 // Save embeddings to a JSON file
    88 const fileName = "embeddings.json";
    89 await writeFile(fileName, JSON.stringify(embeddingsData, null, 2));
    90 console.log(`Embeddings saved to ${fileName}`);
    91 } catch (error) {
    92 console.error("Error during embedding generation:", error.message);
    93 throw error; // Optionally rethrow to halt execution if desired
    94 }
    95}
    96
    97// Main process
    98(async function main() {
    99 try {
    100 await generateEmbeddings(); // Execute embedding generation
    101 } catch (error) {
    102 console.error("Error in main process:", error.message);
    103 }
    104})();
  3. Replace the <VOYAGE_API_KEY> placeholder if you didn't set your API Key for Voyage AI as an environment variable and then save the file.

  4. Run the code to generate embeddings.

    node get-embeddings.js
    Embeddings saved to embeddings.json
  5. Verify the generated embeddings in the generated embeddings.json file.

6
  1. Create a file named convert-embeddings.js to convert the float32, int8, and int1 vector embeddings from Voyage AI to BSON binData vectors by using the MongoDB Node.js driver.

    touch convert-embeddings.js
  2. Copy and paste the following code in the convert-embeddings.js file.

    This code does the following:

    • Generates BSON binData vectors for the float32, int8, and int1 embeddings.

    • Appends the float32, int8, and ubinary BSON binData vectors to the embeddings.json file.

    convert-embeddings.js
    1import fs from "fs/promises";
    2import { BSON } from "mongodb";
    3const { Binary } = BSON;
    4
    5async function main() {
    6 try {
    7 // Read the contents of the original 'embeddings.json' file
    8 const fileContent = await fs.readFile("embeddings.json", "utf8");
    9 const embeddingsData = JSON.parse(fileContent); // Parse JSON into a JavaScript object
    10
    11 // Validate the structure of the original input data
    12 if (!Array.isArray(embeddingsData)) {
    13 throw new Error("'embeddings.json' must contain an array of objects.");
    14 }
    15
    16 // Convert embeddings to BSON-compatible format
    17 const convertEmbeddingsData = embeddingsData.map(({ text, embeddings }) => {
    18 // Field validation to ensure all required embeddings are present
    19 if (
    20 !embeddings ||
    21 !Array.isArray(embeddings.float) ||
    22 !Array.isArray(embeddings.int8) ||
    23 !Array.isArray(embeddings.ubinary)
    24 ) {
    25 throw new Error(`Embeddings are missing or invalid for text: "${text}"`);
    26 }
    27
    28 // Convert embeddings to BSON-compatible binary format
    29 const bsonFloat32 = Binary.fromFloat32Array(new Float32Array(embeddings.float));
    30 const bsonInt8 = Binary.fromInt8Array(new Int8Array(embeddings.int8));
    31 const bsonPackedBits = Binary.fromPackedBits(new Uint8Array(embeddings.ubinary));
    32
    33 // Return the updated object structure
    34 return {
    35 text,
    36 embeddings: { // Original embeddings
    37 float: embeddings.float,
    38 int8: embeddings.int8,
    39 ubinary: embeddings.ubinary,
    40 },
    41 bsonEmbeddings: { // BSON embeddings
    42 float32: bsonFloat32,
    43 int8: bsonInt8,
    44 packedBits: bsonPackedBits,
    45 },
    46 };
    47 });
    48
    49 // Serialize the updated data to BSON-compatible JSON using EJSON
    50 const ejsonSerializedData = BSON.EJSON.stringify(convertEmbeddingsData, null, 2, { relaxed: false });
    51
    52 // Write the updated BSON-converted data back to the same 'embeddings.json' file
    53 await fs.writeFile("embeddings.json", ejsonSerializedData);
    54
    55 console.log("Embeddings with BSON vectors have been saved to embeddings.json");
    56 } catch (error) {
    57 // Print detailed error information
    58 console.error("Error processing embeddings:", error);
    59 }
    60}
    61
    62// Execute the conversion process
    63main();
  3. Run the program to generate the BSON binData vectors.

    node convert-embeddings.js
    Embeddings with BSON vectors have been saved to embeddings.json
  4. Verify the generated BSON embeddings in the embeddings.json file.

7
  1. Create a file named upload-data.js to connect to the Atlas cluster and create a collection in a database for the data in the embeddings.json file.

    touch upload-data.js
  2. Copy and paste the following code in the upload-data.js file.

    This code does the following:

    • Connects to your Atlas cluster and creates a namespace with the database and collection name that you specify.

    • Uploads the data including the embeddings in the embeddings.json file to the specified namespace.

    upload-data.js
    1import fs from 'fs/promises';
    2import { MongoClient, BSON } from 'mongodb';
    3const { Binary } = BSON;
    4
    5async function main() {
    6 const MONGODB_URI = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    7 const DB_NAME = "<DATABASE-NAME>";
    8 const COLLECTION_NAME = "<COLLECTION-NAME>";
    9
    10 let client;
    11 try {
    12 client = new MongoClient(MONGODB_URI);
    13 await client.connect();
    14 console.log("Connected to MongoDB");
    15
    16 const db = client.db(DB_NAME);
    17 const collection = db.collection(COLLECTION_NAME);
    18
    19 // Read and parse the contents of 'embeddings.json' file using EJSON
    20 const fileContent = await fs.readFile('embeddings.json', 'utf8');
    21 const embeddingsData = BSON.EJSON.parse(fileContent);
    22
    23 // Map embeddings data to recreate BSON binary representations with the correct subtype
    24 const documents = embeddingsData.map(({ text, bsonEmbeddings }) => {
    25 return {
    26 text,
    27 bsonEmbeddings: {
    28 float32: bsonEmbeddings.float32,
    29 int8: bsonEmbeddings.int8,
    30 int1: bsonEmbeddings.packedBits
    31 }
    32 };
    33 });
    34
    35 const result = await collection.insertMany(documents);
    36 console.log(`Inserted ${result.insertedCount} documents into MongoDB`);
    37
    38 } catch (error) {
    39 console.error('Error storing embeddings in MongoDB:', error);
    40 } finally {
    41 if (client) {
    42 await client.close();
    43 }
    44 }
    45}
    46
    47// Run the store function
    48main();
  3. Replace the following settings and save the file.

    <CONNECTION-STRING>

    Connection string to connect to the Atlas cluster where you want to create the database and collection.

    Replace this value only if you didn't set the MONGODB_URI environment variable.

    <DB-NAME>

    Name of the database where you want to create the collection.

    <COLLECTION-NAME>

    Name of the collection where you want to store the generated embeddings.

  4. Run the following command to upload the data.

    node upload-data.js
  5. Verify that the documents exist in the collection on your Atlas cluster.

8
  1. Create a file named create-index.js to define an Atlas Vector Search index on the collection.

    touch create-index.js
  2. Copy and paste the following code to create the index in the create-index.js file.

    The code does the following:

    • Connects to the Atlas cluster and creates an index with the specified name for the specified namespace.

    • Indexes the bsonEmbeddings.float32 and bsonEmbeddings.int8 fields as vector type that uses the dotProduct similarity function, and the bsonEmbeddings.int1 field also as vector type that uses the euclidean function.

    create-index.js
    1import { MongoClient, BSON } from "mongodb";
    2import { setTimeout } from "timers/promises";
    3
    4// Connect to your Atlas deployment
    5const uri = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    6
    7const client = new MongoClient(uri);
    8
    9async function main() {
    10 try {
    11 const DB_NAME = "<DATABASE-NAME>";
    12 const COLLECTION_NAME = "<COLLECTION-NAME>";
    13 const db = client.db(DB_NAME);
    14 const collection = db.collection(COLLECTION_NAME);
    15
    16 // Define your Atlas Vector Search index
    17 const index = {
    18 name: "<INDEX-NAME>",
    19 type: "vectorSearch",
    20 definition: {
    21 fields: [
    22 {
    23 type: "vector",
    24 numDimensions: 1024,
    25 path: "bsonEmbeddings.float32",
    26 similarity: "dotProduct",
    27 },
    28 {
    29 type: "vector",
    30 numDimensions: 1024,
    31 path: "bsonEmbeddings.int8",
    32 similarity: "dotProduct",
    33 },
    34 {
    35 type: "vector",
    36 numDimensions: 1024,
    37 path: "bsonEmbeddings.int1",
    38 similarity: "euclidean",
    39 },
    40 ],
    41 },
    42 };
    43
    44 // Run the helper method
    45 const result = await collection.createSearchIndex(index);
    46 console.log(`New search index named ${result} is building.`);
    47
    48 // Wait for the index to be ready to query
    49 console.log("Polling to check if the index is ready. This may take up to a minute.");
    50 let isQueryable = false;
    51
    52 // Use filtered search for index readiness
    53 while (!isQueryable) {
    54 const [indexData] = await collection.listSearchIndexes(index.name).toArray();
    55
    56 if (indexData) {
    57 isQueryable = indexData.queryable;
    58 if (!isQueryable) {
    59 await setTimeout(5000); // Wait for 5 seconds before checking again
    60 }
    61 } else {
    62 // Handle the case where the index might not be found
    63 console.log(`Index ${index.name} not found.`);
    64 await setTimeout(5000); // Wait for 5 seconds before checking again
    65 }
    66 }
    67
    68 console.log(`${result} is ready for querying.`);
    69 } catch (error) {
    70 console.error("Error:", error);
    71 } finally {
    72 await client.close();
    73 }
    74}
    75
    76main().catch((err) => {
    77 console.error("Unhandled error:", err);
    78});
  3. Replace the following settings and save the file.

    <CONNECTION-STRING>

    Connection string to connect to the Atlas cluster where you want to create the index.

    Replace this value only if you didn't set the MONGODB_URI environment variable.

    <DB-NAME>

    Name of the database where you want to create the collection.

    <COLLECTION-NAME>

    Name of the collection where you want to store the generated embeddings.

    <INDEX-NAME>

    Name of the index for the collection.

  4. Create the index.

    node create-index.js
9
  1. Create a file named get-query-embedding.js.

    touch get-query-embeddings.js
  2. Copy and paste the code in the get-query-embedding.js file.

    The sample code does the following:

    • Generates float32, int8, and int1 embeddings for the query text by using Voyage AI.

    • Converts the generated embeddings to BSON binData vectors by using PyMongo.

    • Saves the generated embeddings to a file named query-embeddings.json.

    get-query-embedding.js
    1import { BSON } from "mongodb";
    2import { writeFile } from "fs/promises";
    3import dotenv from "dotenv";
    4
    5// Load environment variables
    6dotenv.config();
    7
    8const { Binary, EJSON } = BSON; // Import BSON utilities
    9
    10// Set your API key from environment or fallback to hardcoded value (not recommended for production)
    11const apiKey = process.env.VOYAGE_API_KEY || "<VOYAGEAI-API-KEY>";
    12const QUERY_TEXT = <QUERY-TEXT>;
    13
    14if (!apiKey || apiKey === "<VOYAGEAI-API-KEY>") {
    15 throw new Error("API key not found. Provide the VOYAGEAI_API_KEY in environment variables.");
    16}
    17
    18// Define the VoyageAI REST API endpoint
    19const apiEndpoint = "https://api.voyageai.com/v1/embeddings";
    20
    21/**
    22 * Fetch embeddings using VoyageAI REST API
    23 */
    24async function fetchEmbeddings(data, model, inputType, outputDtype, outputDimension) {
    25 try {
    26 const response = await fetch(apiEndpoint, {
    27 method: "POST",
    28 headers: {
    29 "Content-Type": "application/json",
    30 Authorization: `Bearer ${apiKey}`,
    31 },
    32 body: JSON.stringify({
    33 input: data,
    34 model,
    35 input_type: inputType,
    36 output_dtype: outputDtype,
    37 output_dimension: outputDimension,
    38 }),
    39 });
    40
    41 // Check for non-success status codes
    42 if (!response.ok) {
    43 const errorResponse = await response.text();
    44 throw new Error(`API request failed with status ${response.status}: ${errorResponse}`);
    45 }
    46
    47 const responseData = await response.json();
    48
    49 // Ensure the response contains valid data
    50 if (!responseData.data || !Array.isArray(responseData.data)) {
    51 console.error("Full API Response:", responseData);
    52 throw new Error("Embeddings are not present or not returned in array format.");
    53 }
    54
    55 return responseData.data.map((item) => item.embedding); // Extract embeddings
    56 } catch (error) {
    57 console.error(`Error fetching embeddings for output_dtype "${outputDtype}":`, error);
    58 throw error;
    59 }
    60}
    61
    62/**
    63 * Create BSON Binary objects using VECTOR_TYPE for all embedding types
    64 */
    65function convertEmbeddingsToBSON(data, float, int8, ubinary) {
    66 return data.map((text, index) => ({
    67 text,
    68 bsonEmbeddings: {
    69 float32: Binary.fromFloat32Array(new Float32Array(float[index])),
    70 int8: Binary.fromInt8Array(new Int8Array(int8[index])),
    71 int1: Binary.fromPackedBits(new Uint8Array(ubinary[index])),
    72 },
    73 }));
    74}
    75
    76/**
    77 * Serialize BSON embeddings and save to JSON file
    78 */
    79async function saveBSONEmbeddingsToFile(bsonEmbeddingsData, outputFileName) {
    80 try {
    81 // Serialize BSON data to JSON format using EJSON
    82 const ejsonSerializedData = EJSON.stringify(bsonEmbeddingsData, null, 2, {
    83 relaxed: true, // Store binary as raw binary data without base64 encoding
    84 });
    85
    86 // Write serialized data to a file
    87 await writeFile(outputFileName, ejsonSerializedData);
    88 console.log(`Embeddings with BSON vectors have been saved to ${outputFileName}`);
    89 } catch (error) {
    90 console.error(`Error saving BSON embeddings to file "${outputFileName}":`, error);
    91 throw error;
    92 }
    93}
    94
    95/**
    96 * Process query text, fetch embeddings, convert to BSON, and write to JSON
    97 */
    98async function main(queryText) {
    99 try {
    100 if (!queryText || typeof queryText !== "string" || queryText.trim() === "") {
    101 throw new Error("Invalid query text. It must be a non-empty string.");
    102 }
    103
    104 const data = [queryText];
    105 const model = "voyage-3-large";
    106 const inputType = "query";
    107 const dimension = 1024;
    108
    109 // Fetch embeddings for different data types
    110 const floatEmbeddings = await fetchEmbeddings(data, model, inputType, "float", dimension);
    111 const int8Embeddings = await fetchEmbeddings(data, model, inputType, "int8", dimension);
    112 const packedBitsEmbeddings = await fetchEmbeddings(data, model, inputType, "ubinary", dimension);
    113
    114 // Convert embeddings into BSON-compatible format
    115 const bsonEmbeddingsData = convertEmbeddingsToBSON(
    116 data,
    117 floatEmbeddings,
    118 int8Embeddings,
    119 packedBitsEmbeddings
    120 );
    121
    122 // Save BSON embeddings to JSON file
    123 const outputFileName = "query-embeddings.json";
    124 await saveBSONEmbeddingsToFile(bsonEmbeddingsData, outputFileName);
    125 } catch (error) {
    126 console.error("Error processing query text:", error);
    127 }
    128}
    129
    130// Main function invocation
    131(async () => {
    132 const queryText = QUERY-TEXT;
    133 await main(queryText);
    134})();
  3. Replace the following settings and save the file.

    <VOYAGE-API-KEY>

    Your API Key for Voyage AI. Only replace this value if you didn't set the environment variable.

    <QUERY-TEXT>

    Your query text. For this tutorial, use science fact.

  4. Run the code to generate the embeddings for the query text.

    node get-query-embeddings.js
    Embeddings with BSON vectors have been saved to query-embeddings.json
10
  1. Create a file named run-query.js.

    touch run-query.js
  2. Copy and paste the following sample $vectorSearch query in the run-query.js file.

    The sample query does the following:

    • Connects to your Atlas cluster and runs the $vectorSearch query against the bsonEmbeddings.float32, bsonEmbeddings.int8, and bsonEmbeddings.int1 fields in the specified collection by using the embeddings in the query-embeddings.json file.

    • Prints the results from Float32, Int8, and Packed Binary (Int1) embeddings to the console.

    run-query.js
    1import { MongoClient } from "mongodb";
    2import fs from "fs/promises";
    3import { BSON } from "bson"; // Use the BSON package for EJSON parsing
    4import dotenv from "dotenv";
    5
    6dotenv.config();
    7
    8// MongoDB connection details
    9const mongoUri = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    10const dbName = "<DATABASE-NAME>";
    11const collectionName = "<COLLECTION-NAME>";
    12const VECTOR_INDEX_NAME = "<INDEX-NAME>";
    13const NUM_CANDIDATES = <NUMBER-OF-CANDIDATES-TO-CONSIDER>;
    14const LIMIT = <NUMBER-OF-DOCUMENTS-TO-RETURN>;
    15const dataField = "<TEXT-FIELD-NAME>";
    16
    17// Fields in the collection containing BSON-compatible query vectors
    18const FIELDS = [
    19 { path: "float32", subtype: 9 },
    20 { path: "int8", subtype: 9 },
    21 { path: "int1", subtype: 9 },
    22];
    23
    24async function main() {
    25 const client = new MongoClient(mongoUri);
    26
    27 try {
    28 await client.connect();
    29 console.log("Connected to MongoDB");
    30
    31 const db = client.db(dbName);
    32 const collection = db.collection(collectionName);
    33
    34 // Read the query embeddings from the JSON file
    35 const fileContent = await fs.readFile("query-embeddings.json", "utf8");
    36 const embeddingsData = BSON.EJSON.parse(fileContent, { relaxed: true });
    37
    38 if (!Array.isArray(embeddingsData) || embeddingsData.length === 0) {
    39 throw new Error("No embeddings found in the JSON file");
    40 }
    41
    42 const results = {};
    43
    44 // Perform vector search for each embedding type
    45 for (const field of FIELDS) {
    46 const { path } = field;
    47 const bsonBinary = embeddingsData[0]?.bsonEmbeddings?.[path];
    48
    49 if (!bsonBinary) {
    50 console.warn(`Embedding for path "${path}" not found. Skipping.`);
    51 continue;
    52 }
    53
    54 const pipeline = [
    55 {
    56 $vectorSearch: {
    57 index: VECTOR_INDEX_NAME,
    58 path: `bsonEmbeddings.${path}`,
    59 queryVector: bsonBinary, // Direct raw binary
    60 numCandidates: NUM_CANDIDATES,
    61 limit: LIMIT,
    62 },
    63 },
    64 {
    65 $project: {
    66 _id: 0,
    67 [dataField]: 1,
    68 score: { $meta: "vectorSearchScore" },
    69 },
    70 },
    71 ];
    72
    73 console.log(`Running vector search using "${path}" embedding...`);
    74 results[path] = await collection.aggregate(pipeline).toArray();
    75 }
    76
    77 return results;
    78 } catch (error) {
    79 console.error("Error during vector search:", error);
    80 } finally {
    81 await client.close();
    82 console.log("MongoDB connection closed");
    83 }
    84}
    85
    86// Parse and display search results for each embedding type
    87(async () => {
    88 const results = await main();
    89
    90 if (results) {
    91 console.log("Results from Float32 embeddings:");
    92 (results.float32 || []).forEach((result, index) => {
    93 console.log(`Result ${index + 1}:`, result);
    94 });
    95
    96 console.log("Results from Int8 embeddings:");
    97 (results.int8 || []).forEach((result, index) => {
    98 console.log(`Result ${index + 1}:`, result);
    99 });
    100
    101 console.log("Results from Int1 (PackedBits) embeddings:");
    102 (results.int1 || []).forEach((result, index) => {
    103 console.log(`Result ${index + 1}:`, result);
    104 });
    105 }
    106})();
  3. Replace the following settings and save the run-query.js file.

    <CONNECTION-STRING>

    Connection string to connect to the Atlas cluster where you want to run the query.

    Replace this value only if you didn't set the MONGODB_URI environment variable.

    <DB-NAME>

    Name of the database which contains the collection.

    <COLLECTION-NAME>

    Name of the collection that you want to query.

    <INDEX-NAME>

    Name of the index for the collection.

    <NUMBER-OF-CAANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to consider during the search. For this example, specify 5.

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of results to return. For this example, specify 2.

    <TEXT-FIELD-NAME>

    Name of the field that contains the text data. For this example, specify text.

  4. Run the following command to execute the query.

    node run-query.js
    Connected to MongoDB
    Running vector search using "float32" embedding...
    Running vector search using "int8" embedding...
    Running vector search using "int1" embedding...
    MongoDB connection closed
    Results from Float32 embeddings:
    Result 1: {
    text: 'The Great Wall of China is visible from space.',
    score: 0.7719700336456299
    }
    Result 2: {
    text: 'Mount Everest is the highest peak on Earth at 8,848m.',
    score: 0.735608696937561
    }
    Results from Int8 embeddings:
    Result 1: {
    text: 'The Great Wall of China is visible from space.',
    score: 0.5051995515823364
    }
    Result 2: {
    text: 'Mount Everest is the highest peak on Earth at 8,848m.',
    score: 0.5044659972190857
    }
    Results from Int1 (PackedBits) embeddings:
    Result 1: {
    text: 'The Great Wall of China is visible from space.',
    score: 0.6845703125
    }
    Result 2: {
    text: 'Mount Everest is the highest peak on Earth at 8,848m.',
    score: 0.6650390625
    }
1

Configure your project to use ES modules by adding "type": "module" to your package.json file and then saving it.

{
"type": "module",
// other fields...
}
2

Run the following command to install the MongoDB Node.js Driver and the dotenv package. This operation might take a few minutes to complete.

npm install mongodb dotenv

You must install Node.js v6.11 or later driver.

If necessary, you must also install libraries from your embedding model provider. In this tutorial, you use the Voyage AI REST API to generate embeddings. Therefore, you don't need to install any additional libraries for Voyage AI.

3

In a terminal window, run the following commands to create a new directory named my-quantization-project and initialize your project:

mkdir my-quantization-project
cd my-quantization-project
npm init -y
4
  1. To access the embedding model provider for generating and converting embeddings, set the environment variable for the embedding model provider's API key, if necessary.

    For using embeddings from Voyage AI, set up the VOYAGE_API_KEY environment variable.

    export VOYAGE_API_KEY="<VOYAGEAI-API-KEY>"

    If you don't set the environment variable, replace the <VOYAGE-API-KEY> in the sample code with the API key before running the code.

  2. To access Atlas cluster, set the MONGODB_URI environment variable.

    export MONGODB_URI="<CONNECTION-STRING>"

    Your connection string should be in the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

    If you don't set the environment variable, replace the <CONNECTION-STRING> in the sample code with your connection string before running the code.

5
  1. Create a file named get-data.js.

    touch get-data.js
  2. Copy and paste the following sample code to fetch the data from the sample_airbnb.listingsAndReviews namespace in your Atlas cluster.

    The sample code does the following:

    • Connects to your Atlas cluster and finds documents with the summary field.

    • Creates a file named subset.json to which it writes the data from the collection.

    get-data.js
    1import { MongoClient, BSON } from 'mongodb';
    2import fs from 'fs/promises';
    3import { writeFile } from "fs/promises";
    4
    5async function main() {
    6 // Replace with your Atlas connection string
    7 const uri = process.env.MONGODB_URI || '<CONNECTION-STRING>';
    8
    9 // Create a new MongoClient instance
    10 const client = new MongoClient(uri);
    11
    12 try {
    13 // Connect to your Atlas cluster
    14 await client.connect();
    15
    16 // Specify the database and collection
    17 const db = client.db('sample_airbnb');
    18 const collection = db.collection('listingsAndReviews');
    19
    20 // Filter to exclude null or empty summary fields
    21 const filter = { summary: { $nin: [null, ''] } };
    22
    23 // Get a subset of documents in the collection
    24 const documentsCursor = collection.find(filter).limit(50);
    25
    26 // Convert the cursor to an array to get the documents
    27 const documents = await documentsCursor.toArray();
    28
    29 // Write the documents to a local file called "subset.json"
    30 const outputFilePath = './subset.json';
    31 fs.writeFile(outputFilePath, JSON.stringify(documents, null, 2), 'utf-8');
    32
    33 // Print the count of documents written to the file
    34 console.log(`Written ${documents.length} documents to ${outputFilePath}`);
    35 } catch (error) {
    36 console.error('An error occurred:', error);
    37 } finally {
    38 // Ensure the client is closed when finished
    39 await client.close();
    40 }
    41}
    42
    43main().catch(console.error);
  3. Replace the <CONNECTION-STRING> placeholder if you didn't set the environment variable for your Atlas connection string and then save the file.

  4. Run the following command to fetch the data:

    node get-data.js
    Subset of documents written to: ./subset.json
6

If you already have float32, int8, or int1 vector embeddings in your collection, skip this step.

  1. Create a file named get-embeddings.js to generate float32, int8, and int1 vector embeddings by using Voyage AI's embed API.

    touch get-embeddings.js
  2. Copy and paste the following code in the get-embeddings.js file.

    This code does the following:

    • Generates float32, int8, and int1 embeddings for the given data by using Voyage AI's embed-english-v3.0 embedding model.

    • Stores the float32, int8, and int1 embeddings in fields named float, int8, and ubinary respectively.

    • Creates a file named embeddings.json and saves the embeddings in the file.

    get-embeddings.js
    1import { readFile, writeFile } from "fs/promises";
    2import dotenv from "dotenv";
    3import fetch from "node-fetch";
    4
    5// Load environment variables from `.env` file
    6dotenv.config();
    7
    8// Set up API key from environment or fallback to hardcoded value
    9const apiKey = process.env.VOYAGE_API_KEY || "<VOYAGE-API-KEY>";
    10
    11if (!apiKey || apiKey === "<VOYAGE-API-KEY>") {
    12 throw new Error("API key not found. Please set VOYAGE_API_KEY in your environment.");
    13}
    14
    15// Define the VoyageAI REST API endpoint
    16const apiEndpoint = "https://api.voyageai.com/v1/embeddings";
    17
    18/**
    19 * Fetch embeddings using VoyageAI REST API for a specific output data type
    20 */
    21async function fetchEmbeddings(data, model, outputDtype, dimension) {
    22 const response = await fetch(apiEndpoint, {
    23 method: "POST",
    24 headers: {
    25 "Content-Type": "application/json",
    26 Authorization: `Bearer ${apiKey}`,
    27 },
    28 body: JSON.stringify({
    29 input: data,
    30 model,
    31 input_type: "document",
    32 output_dtype: outputDtype,
    33 output_dimension: dimension,
    34 }),
    35 });
    36
    37 // Check for non-success status codes
    38 if (!response.ok) {
    39 const errorResponse = await response.text();
    40 throw new Error(`API request failed with status ${response.status}: ${errorResponse}`);
    41 }
    42
    43 const responseData = await response.json();
    44
    45 // Ensure the response contains valid data
    46 if (!responseData.data || !Array.isArray(responseData.data)) {
    47 throw new Error(`Invalid API response for dtype "${outputDtype}": 'data' array is missing.`);
    48 }
    49
    50 // Extract embeddings from the response
    51 const embeddings = responseData.data.map((item) => item.embedding);
    52
    53 // Validate embeddings
    54 if (!Array.isArray(embeddings) || embeddings.length !== data.length) {
    55 throw new Error(`Invalid embeddings received for dtype "${outputDtype}".`);
    56 }
    57
    58 return embeddings; // Return embeddings for the requested dtype
    59}
    60
    61/**
    62 * Main function to read input data, fetch embeddings, and save them to JSON
    63 */
    64async function main() {
    65 try {
    66 // Read and parse the contents of `subset.json`
    67 const subsetData = await readFile("subset.json", "utf-8");
    68 const documents = JSON.parse(subsetData);
    69
    70 // Extract the `summary` fields and keep only non-empty strings
    71 const data = documents
    72 .map((doc) => doc.summary)
    73 .filter((summary) => typeof summary === "string" && summary.trim().length > 0);
    74
    75 // If no valid data is found, throw an error
    76 if (data.length === 0) {
    77 throw new Error("No valid summary texts available in the input file.");
    78 }
    79
    80 // Configuration for embeddings
    81 const model = "voyage-3-large";
    82 const dimension = 1024;
    83
    84 // Fetch embeddings for different output types (float, int8, ubinary)
    85 const floatEmbeddings = await fetchEmbeddings(data, model, "float", dimension);
    86 const int8Embeddings = await fetchEmbeddings(data, model, "int8", dimension);
    87 const ubinaryEmbeddings = await fetchEmbeddings(data, model, "ubinary", dimension);
    88
    89 // Map embeddings to their corresponding texts
    90 const embeddingsData = data.map((text, index) => ({
    91 text,
    92 embeddings: {
    93 float: floatEmbeddings[index],
    94 int8: int8Embeddings[index],
    95 ubinary: ubinaryEmbeddings[index],
    96 },
    97 }));
    98
    99 // Save embeddings to a JSON file
    100 const fileName = "embeddings.json";
    101 await writeFile(fileName, JSON.stringify(embeddingsData, null, 2));
    102 console.log(`Embeddings saved to ${fileName}`);
    103 } catch (error) {
    104 console.error("Error during embedding generation:", error.message);
    105 }
    106}
    107
    108// Execute the main function
    109main();
  3. If you didn't set the environment variable for your Voyage AI API Key, replace the <VOYAGEAI-API-KEY> placeholder and save the file.

  4. Run the code to generate the embeddings.

    node get-embeddings.js
    Embeddings saved to embeddings.json
  5. Verify the generated embeddings by opening the generated embeddings.json file.

7
  1. Create a file named convert-embeddings.js to convert the float32, int8, and int1 vector embeddings from Voyage AI to BSON binData vectors.

    touch convert-embeddings.js
  2. Copy and paste the following code in the convert-embeddings.js file.

    This code does the following:

    • Generates BSON binData vectors for the float32, int8, and int1 embeddings.

    • Appends the float32, int8, and ubinary BSON binData vectors to the embeddings.json file.

    convert-embeddings.js
    1import fs from "fs/promises";
    2import { BSON } from "mongodb";
    3const { Binary } = BSON;
    4
    5async function main() {
    6 try {
    7 // Read the contents of the original 'embeddings.json' file
    8 const fileContent = await fs.readFile("embeddings.json", "utf8");
    9 const embeddingsData = JSON.parse(fileContent); // Parse JSON into a JavaScript object
    10
    11 // Validate the structure of the original input data
    12 if (!Array.isArray(embeddingsData)) {
    13 throw new Error("'embeddings.json' must contain an array of objects.");
    14 }
    15
    16 // Convert embeddings to BSON-compatible format
    17 const convertEmbeddingsData = embeddingsData.map(({ text, embeddings }) => {
    18 // Field validation to ensure all required embeddings are present
    19 if (
    20 !embeddings ||
    21 !Array.isArray(embeddings.float) ||
    22 !Array.isArray(embeddings.int8) ||
    23 !Array.isArray(embeddings.ubinary)
    24 ) {
    25 throw new Error(`Embeddings are missing or invalid for text: "${text}"`);
    26 }
    27
    28 // Convert embeddings to BSON-compatible binary format
    29 const bsonFloat32 = Binary.fromFloat32Array(new Float32Array(embeddings.float));
    30 const bsonInt8 = Binary.fromInt8Array(new Int8Array(embeddings.int8));
    31 const bsonPackedBits = Binary.fromPackedBits(new Uint8Array(embeddings.ubinary));
    32
    33 // Return the updated object structure
    34 return {
    35 text,
    36 embeddings: { // Original embeddings
    37 float: embeddings.float,
    38 int8: embeddings.int8,
    39 ubinary: embeddings.ubinary,
    40 },
    41 bsonEmbeddings: { // BSON embeddings
    42 float32: bsonFloat32,
    43 int8: bsonInt8,
    44 packedBits: bsonPackedBits,
    45 },
    46 };
    47 });
    48
    49 // Serialize the updated data to BSON-compatible JSON using EJSON
    50 const ejsonSerializedData = BSON.EJSON.stringify(convertEmbeddingsData, null, 2, { relaxed: false });
    51
    52 // Write the updated BSON-converted data back to the same 'embeddings.json' file
    53 await fs.writeFile("embeddings.json", ejsonSerializedData);
    54
    55 console.log("Embeddings with BSON vectors have been saved to embeddings.json");
    56 } catch (error) {
    57 // Print detailed error information
    58 console.error("Error processing embeddings:", error);
    59 }
    60}
    61
    62// Execute the conversion process
    63main();
  3. Run the program to generate the BSON binData vectors.

    node convert-embeddings.js
    Embeddings with BSON vectors have been saved to embeddings.json
  4. Verify the generated BSON embeddings in the embeddings.json file.

8
  1. Create a file named upload-data.js to connect to the Atlas cluster and upload the data to the sample_airbnb.listingsAndReviews namespace.

    touch upload-data.js
  2. Copy and paste the following code in the upload-data.js file.

    This code does the following:

    • Connects to your Atlas cluster and creates a namespace with the database and collection name that you specify.

    • Uploads the data including the embeddings into the sample_airbnb.listingsAndReviews namespace.

    upload-data.js
    1import fs from 'fs/promises';
    2import { MongoClient, BSON } from 'mongodb';
    3import { EJSON, Binary } from 'bson';
    4
    5async function main() {
    6 const MONGODB_URI = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    7 const DB_NAME = "sample_airbnb";
    8 const COLLECTION_NAME = "listingsAndReviews";
    9
    10 let client;
    11 try {
    12 // Connect to MongoDB
    13 client = new MongoClient(MONGODB_URI);
    14 await client.connect();
    15 console.log("Connected to MongoDB");
    16
    17 // Access database and collection
    18 const db = client.db(DB_NAME);
    19 const collection = db.collection(COLLECTION_NAME);
    20
    21 // Load embeddings from JSON using EJSON.parse
    22 const fileContent = await fs.readFile('embeddings.json', 'utf8');
    23 const embeddingsData = EJSON.parse(fileContent); // Use EJSON.parse
    24
    25 // Map embeddings data to recreate BSON binary representations
    26 const documents = embeddingsData.map(({ text, bsonEmbeddings }) => {
    27 return {
    28 summary: text,
    29 bsonEmbeddings: {
    30 float32: bsonEmbeddings.float32,
    31 int8: bsonEmbeddings.int8,
    32 int1: bsonEmbeddings.packedBits
    33 }
    34 };
    35 });
    36
    37 // Iterate over documents and upsert each into the MongoDB collection
    38 for (const doc of documents) {
    39 const filter = { summary: doc.summary };
    40 const update = { $set: doc };
    41
    42 // Update the document with the BSON binary data
    43 const result = await collection.updateOne(filter, update, { upsert: true });
    44 if (result.matchedCount > 0) {
    45 console.log(`Updated document with summary: ${doc.summary}`);
    46 } else {
    47 console.log(`Inserted new document with summary: ${doc.summary}`);
    48 }
    49 }
    50
    51 console.log("Embeddings stored in MongoDB successfully.");
    52 } catch (error) {
    53 console.error('Error storing embeddings in MongoDB:', error);
    54 } finally {
    55 if (client) {
    56 await client.close();
    57 }
    58 }
    59}
    60
    61// Run the main function to load the data
    62main();
  3. Replace the <CONNECTION-STRING> placeholder if you didn't set the environment variable for your Atlas connection string and then save the file.

  4. Run the following command to upload the data.

    node upload-data.js
    Connected to MongoDB
    Updated document with text: ...
    ...
    Embeddings stored in MongoDB successfully.
  5. Verify by logging into your Atlas cluster and checking the namespace in the Data Explorer.

9
  1. Create a file named create-index.js.

    touch create-index.js
  2. Copy and paste the following code to create the index in the create-index.js file.

    The code does the following:

    • Connects to the Atlas cluster and creates an index with the specified name for the specified namespace.

    • Indexes the bsonEmbeddings.float32 and bsonEmbeddings.int8 fields as vector type by using the dotProduct similarity function, and the bsonEmbeddings.int1 field also as vector type by using the euclidean function.

    create-index.js
    1import { MongoClient, BSON } from "mongodb";
    2import { setTimeout } from "timers/promises";
    3
    4// Connect to your Atlas deployment
    5const uri = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    6
    7const client = new MongoClient(uri);
    8
    9async function main() {
    10 try {
    11 const DB_NAME = "<DATABASE-NAME>";
    12 const COLLECTION_NAME = "<COLLECTION-NAME>";
    13 const db = client.db(DB_NAME);
    14 const collection = db.collection(COLLECTION_NAME);
    15
    16 // Define your Atlas Vector Search index
    17 const index = {
    18 name: "<INDEX-NAME>",
    19 type: "vectorSearch",
    20 definition: {
    21 fields: [
    22 {
    23 type: "vector",
    24 numDimensions: 1024,
    25 path: "bsonEmbeddings.float32",
    26 similarity: "dotProduct",
    27 },
    28 {
    29 type: "vector",
    30 numDimensions: 1024,
    31 path: "bsonEmbeddings.int8",
    32 similarity: "dotProduct",
    33 },
    34 {
    35 type: "vector",
    36 numDimensions: 1024,
    37 path: "bsonEmbeddings.int1",
    38 similarity: "euclidean",
    39 },
    40 ],
    41 },
    42 };
    43
    44 // Run the helper method
    45 const result = await collection.createSearchIndex(index);
    46 console.log(`New search index named ${result} is building.`);
    47
    48 // Wait for the index to be ready to query
    49 console.log("Polling to check if the index is ready. This may take up to a minute.");
    50 let isQueryable = false;
    51
    52 // Use filtered search for index readiness
    53 while (!isQueryable) {
    54 const [indexData] = await collection.listSearchIndexes(index.name).toArray();
    55
    56 if (indexData) {
    57 isQueryable = indexData.queryable;
    58 if (!isQueryable) {
    59 await setTimeout(5000); // Wait for 5 seconds before checking again
    60 }
    61 } else {
    62 // Handle the case where the index might not be found
    63 console.log(`Index ${index.name} not found.`);
    64 await setTimeout(5000); // Wait for 5 seconds before checking again
    65 }
    66 }
    67
    68 console.log(`${result} is ready for querying.`);
    69 } catch (error) {
    70 console.error("Error:", error);
    71 } finally {
    72 await client.close();
    73 }
    74}
    75
    76main().catch((err) => {
    77 console.error("Unhandled error:", err);
    78});
  3. Replace the following settings and save the file.

    <CONNECTION-STRING>

    Connection string to connect to your Atlas cluster that you want to create the database and collection.

    Replace this value only if you didn't set the MONGODB_URI environment variable.

    <DB-NAME>

    Name of the collection, which is sample_airbnb.

    <COLLECTION-NAME>

    Name of the collection, which is listingsAndReviews.

    <INDEX-NAME>

    Name of the index for the collection.

  4. Create the index.

    node create-index.js
    New search index named vector_index is building.
    Polling to check if the index is ready. This may take up to a minute.
    <INDEX-NAME> is ready for querying.
10
  1. Create a file named get-query-embeddings.js.

    touch get-query-embeddings.js
  2. Copy and paste the code in the get-query-embedding.js file.

    The sample code does the following:

    • Generates float32, int8, and int1 embeddings for the query text by using Voyage AI.

    • Converts the generated embeddings to BSON binData vectors by using PyMongo.

    • Saves the generated embeddings to a file named query-embeddings.json.

    get-query-embedding.js
    1import { BSON } from "mongodb";
    2import { writeFile } from "fs/promises";
    3import dotenv from "dotenv";
    4
    5// Load environment variables
    6dotenv.config();
    7
    8const { Binary, EJSON } = BSON; // Import BSON utilities
    9
    10// Set your API key from environment or fallback to hardcoded value (not recommended for production)
    11const apiKey = process.env.VOYAGE_API_KEY || "<VOYAGEAI-API-KEY>";
    12const QUERY_TEXT = <QUERY-TEXT>;
    13
    14if (!apiKey || apiKey === "<VOYAGEAI-API-KEY>") {
    15 throw new Error("API key not found. Provide the VOYAGEAI_API_KEY in environment variables.");
    16}
    17
    18// Define the VoyageAI REST API endpoint
    19const apiEndpoint = "https://api.voyageai.com/v1/embeddings";
    20
    21/**
    22 * Fetch embeddings using VoyageAI REST API
    23 */
    24async function fetchEmbeddings(data, model, inputType, outputDtype, outputDimension) {
    25 try {
    26 const response = await fetch(apiEndpoint, {
    27 method: "POST",
    28 headers: {
    29 "Content-Type": "application/json",
    30 Authorization: `Bearer ${apiKey}`,
    31 },
    32 body: JSON.stringify({
    33 input: data,
    34 model,
    35 input_type: inputType,
    36 output_dtype: outputDtype,
    37 output_dimension: outputDimension,
    38 }),
    39 });
    40
    41 // Check for non-success status codes
    42 if (!response.ok) {
    43 const errorResponse = await response.text();
    44 throw new Error(`API request failed with status ${response.status}: ${errorResponse}`);
    45 }
    46
    47 const responseData = await response.json();
    48
    49 // Ensure the response contains valid data
    50 if (!responseData.data || !Array.isArray(responseData.data)) {
    51 console.error("Full API Response:", responseData);
    52 throw new Error("Embeddings are not present or not returned in array format.");
    53 }
    54
    55 return responseData.data.map((item) => item.embedding); // Extract embeddings
    56 } catch (error) {
    57 console.error(`Error fetching embeddings for output_dtype "${outputDtype}":`, error);
    58 throw error;
    59 }
    60}
    61
    62/**
    63 * Create BSON Binary objects using VECTOR_TYPE for all embedding types
    64 */
    65function convertEmbeddingsToBSON(data, float, int8, ubinary) {
    66 return data.map((text, index) => ({
    67 text,
    68 bsonEmbeddings: {
    69 float32: Binary.fromFloat32Array(new Float32Array(float[index])),
    70 int8: Binary.fromInt8Array(new Int8Array(int8[index])),
    71 int1: Binary.fromPackedBits(new Uint8Array(ubinary[index])),
    72 },
    73 }));
    74}
    75
    76/**
    77 * Serialize BSON embeddings and save to JSON file
    78 */
    79async function saveBSONEmbeddingsToFile(bsonEmbeddingsData, outputFileName) {
    80 try {
    81 // Serialize BSON data to JSON format using EJSON
    82 const ejsonSerializedData = EJSON.stringify(bsonEmbeddingsData, null, 2, {
    83 relaxed: true, // Store binary as raw binary data without base64 encoding
    84 });
    85
    86 // Write serialized data to a file
    87 await writeFile(outputFileName, ejsonSerializedData);
    88 console.log(`Embeddings with BSON vectors have been saved to ${outputFileName}`);
    89 } catch (error) {
    90 console.error(`Error saving BSON embeddings to file "${outputFileName}":`, error);
    91 throw error;
    92 }
    93}
    94
    95/**
    96 * Process query text, fetch embeddings, convert to BSON, and write to JSON
    97 */
    98async function main(queryText) {
    99 try {
    100 if (!queryText || typeof queryText !== "string" || queryText.trim() === "") {
    101 throw new Error("Invalid query text. It must be a non-empty string.");
    102 }
    103
    104 const data = [queryText];
    105 const model = "voyage-3-large";
    106 const inputType = "query";
    107 const dimension = 1024;
    108
    109 // Fetch embeddings for different data types
    110 const floatEmbeddings = await fetchEmbeddings(data, model, inputType, "float", dimension);
    111 const int8Embeddings = await fetchEmbeddings(data, model, inputType, "int8", dimension);
    112 const packedBitsEmbeddings = await fetchEmbeddings(data, model, inputType, "ubinary", dimension);
    113
    114 // Convert embeddings into BSON-compatible format
    115 const bsonEmbeddingsData = convertEmbeddingsToBSON(
    116 data,
    117 floatEmbeddings,
    118 int8Embeddings,
    119 packedBitsEmbeddings
    120 );
    121
    122 // Save BSON embeddings to JSON file
    123 const outputFileName = "query-embeddings.json";
    124 await saveBSONEmbeddingsToFile(bsonEmbeddingsData, outputFileName);
    125 } catch (error) {
    126 console.error("Error processing query text:", error);
    127 }
    128}
    129
    130// Main function invocation
    131(async () => {
    132 const queryText = QUERY-TEXT;
    133 await main(queryText);
    134})();
  3. Replace the following settings and save the file.

    <VOYAGEAI-API-KEY>

    Your API Key for Voyage AI. Only replace this value if you didn't set the key as an environment variable.

    <QUERY-TEXT>

    Your query text. For this example, use ocean view.

  4. Run the code to generate the embeddings for the query text.

    node get-query-embeddings.js
    Embeddings with BSON vectors have been saved to query-embeddings.json
11
  1. Create a file named run-query.js.

    touch run-query.js
  2. Copy and paste the following sample $vectorSearch query in the run-query.js file.

    The sample query does the following:

    • Connects to your Atlas cluster and runs the $vectorSearch query against the bsonEmbeddings.float32, bsonEmbeddings.int8, and bsonEmbeddings.int1 fields in the sample_airbnb.listingsAndReviews namespace by using the embeddings in the query-embeddings.json file.

    • Prints the results from Float32, Int8, and Packed Binary (Int1) embeddings to the console.

    run-query.js
    1import { MongoClient } from "mongodb";
    2import fs from "fs/promises";
    3import { BSON } from "bson"; // Use the BSON package for EJSON parsing
    4import dotenv from "dotenv";
    5
    6dotenv.config();
    7
    8// MongoDB connection details
    9const mongoUri = process.env.MONGODB_URI || "<CONNECTION-STRING>";
    10const dbName = "<DATABASE-NAME>";
    11const collectionName = "<COLLECTION-NAME>";
    12const VECTOR_INDEX_NAME = "<INDEX-NAME>";
    13const NUM_CANDIDATES = <NUMBER-OF-CANDIDATES-TO-CONSIDER>;
    14const LIMIT = <NUMBER-OF-DOCUMENTS-TO-RETURN>;
    15const dataField = "<TEXT-FIELD-NAME>";
    16
    17// Fields in the collection containing BSON-compatible query vectors
    18const FIELDS = [
    19 { path: "float32", subtype: 9 },
    20 { path: "int8", subtype: 9 },
    21 { path: "int1", subtype: 9 },
    22];
    23
    24async function main() {
    25 const client = new MongoClient(mongoUri);
    26
    27 try {
    28 await client.connect();
    29 console.log("Connected to MongoDB");
    30
    31 const db = client.db(dbName);
    32 const collection = db.collection(collectionName);
    33
    34 // Read the query embeddings from the JSON file
    35 const fileContent = await fs.readFile("query-embeddings.json", "utf8");
    36 const embeddingsData = BSON.EJSON.parse(fileContent, { relaxed: true });
    37
    38 if (!Array.isArray(embeddingsData) || embeddingsData.length === 0) {
    39 throw new Error("No embeddings found in the JSON file");
    40 }
    41
    42 const results = {};
    43
    44 // Perform vector search for each embedding type
    45 for (const field of FIELDS) {
    46 const { path } = field;
    47 const bsonBinary = embeddingsData[0]?.bsonEmbeddings?.[path];
    48
    49 if (!bsonBinary) {
    50 console.warn(`Embedding for path "${path}" not found. Skipping.`);
    51 continue;
    52 }
    53
    54 const pipeline = [
    55 {
    56 $vectorSearch: {
    57 index: VECTOR_INDEX_NAME,
    58 path: `bsonEmbeddings.${path}`,
    59 queryVector: bsonBinary, // Direct raw binary
    60 numCandidates: NUM_CANDIDATES,
    61 limit: LIMIT,
    62 },
    63 },
    64 {
    65 $project: {
    66 _id: 0,
    67 [dataField]: 1,
    68 score: { $meta: "vectorSearchScore" },
    69 },
    70 },
    71 ];
    72
    73 console.log(`Running vector search using "${path}" embedding...`);
    74 results[path] = await collection.aggregate(pipeline).toArray();
    75 }
    76
    77 return results;
    78 } catch (error) {
    79 console.error("Error during vector search:", error);
    80 } finally {
    81 await client.close();
    82 console.log("MongoDB connection closed");
    83 }
    84}
    85
    86// Parse and display search results for each embedding type
    87(async () => {
    88 const results = await main();
    89
    90 if (results) {
    91 console.log("Results from Float32 embeddings:");
    92 (results.float32 || []).forEach((result, index) => {
    93 console.log(`Result ${index + 1}:`, result);
    94 });
    95
    96 console.log("Results from Int8 embeddings:");
    97 (results.int8 || []).forEach((result, index) => {
    98 console.log(`Result ${index + 1}:`, result);
    99 });
    100
    101 console.log("Results from Int1 (PackedBits) embeddings:");
    102 (results.int1 || []).forEach((result, index) => {
    103 console.log(`Result ${index + 1}:`, result);
    104 });
    105 }
    106})();
  3. Replace the following settings and save the run-query.js file.

    <CONNECTION-STRING>

    Connection string to connect to the Atlas cluster where you want to create the index.

    Replace this value only if you didn't set the MONGODB_URI environment variable.

    <DB-NAME>

    Name of the database where you want to create the collection. For this example, specify sample_airbnb.

    <COLLECTION-NAME>

    Name of the collection where you want to store the generated embeddings. For this example, specify listingsAndReviews.

    <INDEX-NAME>

    Name of the index for the collection.

    <NUMBER-OF-CANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to consider. For this example, specify 20.

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of documents to return in the results. For this example, specify 5.

    <DATA-FIELD-NAME>

    Name of the field that contains text data. For this example, specify summary.

  4. Run the query.

    To execute the query, run the following command:

    node run-query.js
    Results from embeddings_float32 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.799713134765625"}}
    {"_id":"10227000","summary":"THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!","score":{"$numberDouble":"0.7568193078041077"}}
    {"_id":"1001265","summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.7500505447387695"}}
    {"summary":"Quarto com vista para a Lagoa Rodrigo de Freitas, cartão postal do Rio de Janeiro. Linda Vista. 1 Quarto e 1 banheiro Amplo, arejado, vaga na garagem. Prédio com piscina, sauna e playground. Fácil acesso, próximo da praia e shoppings.","score":{"$numberDouble":"0.7367454171180725"},"_id":"10030955"}
    {"_id":"10220130","summary":"Cozy and comfortable apartment. Ideal for families and vacations. 3 bedrooms, 2 of them suites. Located 20-min walk to the beach and close to the Rio 2016 Olympics Venues. Situated in a modern and secure condominium, with many entertainment available options around.","score":{"$numberDouble":"0.7315733432769775"}}
    Results from embeddings_int8 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.5056195259094238"}}
    {"_id":"10227000","summary":"THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!","score":{"$numberDouble":"0.5048412084579468"}}
    {"summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.5047098398208618"},"_id":"1001265"}
    {"_id":"10030955","summary":"Quarto com vista para a Lagoa Rodrigo de Freitas, cartão postal do Rio de Janeiro. Linda Vista. 1 Quarto e 1 banheiro Amplo, arejado, vaga na garagem. Prédio com piscina, sauna e playground. Fácil acesso, próximo da praia e shoppings.","score":{"$numberDouble":"0.5043320655822754"}}
    {"_id":"10220130","summary":"Cozy and comfortable apartment. Ideal for families and vacations. 3 bedrooms, 2 of them suites. Located 20-min walk to the beach and close to the Rio 2016 Olympics Venues. Situated in a modern and secure condominium, with many entertainment available options around.","score":{"$numberDouble":"0.5043137073516846"}}
    Results from embeddings_int1 embeddings:
    {"_id":"10266175","summary":"A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.","score":{"$numberDouble":"0.7119140625"}}
    {"_id":"1001265","summary":"A short distance from Honolulu's billion dollar mall, and the same distance to Waikiki. Parking included. A great location that work perfectly for business, education, or simple visit. Experience Yacht Harbor views and 5 Star Hilton Hawaiian Village.","score":{"$numberDouble":"0.6787109375"}}
    {"summary":"A friendly apartment block where everyone knows each other and there is a strong communal vibe. Property has a huge backyard with vege garden and skate ramp. 7min walk to the beach and 2min to buses.","score":{"$numberDouble":"0.671875"},"_id":"10209136"}
    {"_id":"10227000","summary":"THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!","score":{"$numberDouble":"0.6669921875"}}
    {"_id":"10264100","summary":"Having a large airy living room. The apartment is well divided. Fully furnished and cozy. The building has a 24h doorman and camera services in the corridors. It is very well located, close to the beach, restaurants, pubs and several shops and supermarkets. And it offers a good mobility being close to the subway.","score":{"$numberDouble":"0.6669921875"}}

    Your results might be different because the generated embeddings can vary depending on your environment.

Create an interactive Python notebook by saving a file with the .ipynb extension, and then perform the following steps in the notebook. To try the example, replace the placeholders with valid values.

Work with a runnable version of this tutorial as a Python notebook.

1

Run the following command to install Voyage AI and the PyMongo Driver. You must install PyMongo v4.10 or later driver.

pip install --quiet --upgrade voyageai pymongo

This operation might take a few minutes to complete.

2

In this step, you define functions for the following purposes:

  • Generate embeddings by using Voyage AI.

  • Convert embeddings to BSON vectors by using the PyMongo driver.

Copy, paste, and run the sample code below after replacing the following placeholder value (highlighted in the code):

Placeholder
Valid Value

<VOYAGE-API-KEY>

Voyage AI API key to use for generating embeddings.

1import os
2import voyageai
3from bson.binary import Binary, BinaryVectorDtype
4
5# Initialize the VoyageAI Client
6os.environ["VOYAGE_API_KEY"] = "<VOYAGEAI-API-KEY>"
7vo = voyageai.Client()
8
9# Define a function to generate embeddings for all strings in `texts`
10def generate_embeddings(texts, model: str, dtype: str, output_dimension: int):
11 embeddings = []
12 for text in texts: # Process eachstring in the data list
13 embedding = vo.embed(
14 texts=[text], # Pass each string as a list with a single item
15 model=model,
16 output_dtype=dtype,
17 output_dimension=output_dimension,
18 ).embeddings[0]
19 embeddings.append(embedding) # Collect the embedding for the current text
20 return embeddings
21
22# Convert embeddings to BSON vectors
23def generate_bson_vector(vector, vector_dtype):
24 return Binary.from_vector(vector, vector_dtype)
3

For this example, use the sample sentences in the following code.

1data = [
2 "The Great Wall of China is visible from space.",
3 "The Eiffel Tower was completed in Paris in 1889.",
4 "Mount Everest is the highest peak on Earth at 8,848m.",
5 "Shakespeare wrote 37 plays and 154 sonnets during his lifetime.",
6 "The Mona Lisa was painted by Leonardo da Vinci.",
7]
4

In this step, you generate embeddings for the sample data and then convert the embeddings to BSON vectors by using the generate_embeddings and generate_bson_vector functions respectively.

  1. Generate the embeddings using Voyage AI.

    This step is required if you haven't yet generated embeddings from your data. If you've already generated embeddings, skip this step. To learn more about generating embeddings from your data, see How to Create Vector Embeddings.

    Copy, paste, and run the sample code below after replacing the following placeholder values (highlighted in the code):

    Placeholder
    Valid Value

    <EMBEDDING-MODEL>

    Embedding model to use for generating the embeddings. For this example, specify voyage-3-large.

    <NUMBER-OF-DIMENSIONS>

    Number of dimensions for the resulting output embeddings. For this example, specify 1024.

    1# Use the function with different output data types to generate embeddings
    2model_name = "<EMBEDDING-MODEL>"
    3output_dimension = <NUMBER-OF-DIMENSIONS>
    4
    5# Generate embeddings in all supported data types
    6float32_embeddings = generate_embeddings(data, model=model_name, dtype="float", output_dimension=output_dimension)
    7int8_embeddings = generate_embeddings(data, model=model_name, dtype="int8", output_dimension=output_dimension)
    8int1_embeddings = generate_embeddings(data, model=model_name, dtype="ubinary", output_dimension=output_dimension)
  2. Convert the embeddings to BSON vectors.

    Copy, paste, and run the following code:

    1# For all vectors in your collection, generate BSON vectors of float32, int8, and int1 embeddings
    2bson_float32_embeddings = []
    3bson_int8_embeddings = []
    4bson_int1_embeddings = []
    5for i, (f32_emb, int8_emb, int1_emb) in enumerate(zip(float32_embeddings, int8_embeddings, int1_embeddings)):
    6 bson_float32_embeddings.append(generate_bson_vector(f32_emb, BinaryVectorDtype.FLOAT32))
    7 bson_int8_embeddings.append(generate_bson_vector(int8_emb, BinaryVectorDtype.INT8))
    8 bson_int1_embeddings.append(generate_bson_vector(int1_emb, BinaryVectorDtype.PACKED_BIT))
5

You can load your data from the Atlas UI and programmatically. To learn how to load your data from the Atlas UI, see Insert Your Data.

  1. Create documents from the sample data and embeddings.

    Copy, paste, and run the sample code below after replacing the following placeholder values (highlighted in the code):

    Placeholder
    Valid Value

    <FIELD-NAME-FOR-FLOAT32-TYPE>

    Name of field with float32 values.

    <FIELD-NAME-FOR-INT8-TYPE>

    Name of field with int8 values.

    <FIELD-NAME-FOR-INT1-TYPE>

    Name of field with int1 values.

    <TEXT-FIELD-NAME>

    Name of the field where you want to store the text data.

    1# Specify the field names for the float32, int8, and int1 embeddings
    2float32_field = "<FIELD-NAME-FOR-FLOAT32-TYPE>"
    3int8_field = "<FIELD-NAME-FOR-INT8-TYPE>"
    4int1_field = "<FIELD-NAME-FOR-INT1-TYPE>"
    5
    6# Define function to create documents with BSON vector embeddings
    7def create_new_docs_with_bson_vectors(bson_float32_embeddings, bson_int8_embeddings, bson_int1_embeddings, data):
    8 docs = []
    9 for i, (bson_f32_emb, bson_int8_emb, bson_int1_emb, text) in enumerate(zip(bson_float32_embeddings, bson_int8_embeddings, bson_int1_embeddings, data)):
    10
    11 doc = {
    12 "_id": i,
    13 "<TEXT-FIELD-NAME>": text,
    14 float32_field: bson_f32_emb,
    15 int8_field: bson_int8_emb,
    16 int1_field: bson_int1_emb
    17 }
    18 docs.append(doc)
    19 return docs
    20
    21# Create the documents
    22documents = create_new_docs_with_bson_vectors(bson_float32_embeddings, bson_int8_embeddings, bson_int1_embeddings, data)
  2. Load your data into your Atlas cluster.

    Copy, paste, and run the sample code below after replacing the following placeholder values (highlighted in the code):

    Placeholder
    Valid Value

    <CONNECTION-STRING>

    Cluster connection string. To learn more, see Connect via Drivers.

    <DATABASE-NAME>

    Name of the database.

    <COLLECTION-NAME>

    Name of the collection in the specified database.

    1import pymongo
    2
    3mongo_client = pymongo.MongoClient("<CONNECTION-STRING>")
    4# Insert documents into a new database and collection
    5db = mongo_client["<DATABASE-NAME>"]
    6collection_name = "<COLLECTION-NAME>"
    7db.create_collection(collection_name)
    8collection = db[collection_name]
    9
    10collection.insert_many(documents)
6

You can create Atlas Vector Search indexes by using the Atlas UI, Atlas CLI, Atlas Administration API, and MongoDB drivers. To learn more, see How to Index Fields for Vector Search.

Copy, paste, and run the sample code below after replacing the following placeholder value (highlighted in the code):

Placeholder
Valid Value

<INDEX-NAME>

Name of vector type index.

1from pymongo.operations import SearchIndexModel
2import time
3
4# Define and create the vector search index
5index_name = "<INDEX-NAME>"
6search_index_model = SearchIndexModel(
7 definition={
8 "fields": [
9 {
10 "type": "vector",
11 "path": float32_field,
12 "similarity": "dotProduct",
13 "numDimensions": 1024
14 },
15 {
16 "type": "vector",
17 "path": int8_field,
18 "similarity": "dotProduct",
19 "numDimensions": 1024
20 },
21 {
22 "type": "vector",
23 "path": int1_field,
24 "similarity": "euclidean",
25 "numDimensions": 1024
26 }
27 ]
28 },
29 name=index_name,
30 type="vectorSearch"
31)
32result = collection.create_search_index(model=search_index_model)
33print("New search index named " + result + " is building.")
34
35# Wait for initial sync to complete
36print("Polling to check if the index is ready. This may take up to a minute.")
37predicate=None
38if predicate is None:
39 predicate = lambda index: index.get("queryable") is True
40while True:
41 indices = list(collection.list_search_indexes(index_name))
42 if len(indices) and predicate(indices[0]):
43 break
44 time.sleep(5)
45print(result + " is ready for querying.")
New search index named <INDEX-NAME> is building.
Polling to check if the index is ready. This may take up to a minute.
<INDEX-NAME> is ready for querying.
7
  1. Define a function to run a vector search query.

    The function to run Atlas Vector Search queries performs the following actions:

    • Generates embeddings using Voyage AI for the query text.

    • Converts the embeddings to BSON vectors.

    • Defines the aggregation pipeline for the vector search.

    • Runs the aggregation pipeline and returns the results.

    Copy, paste, and run the sample code below after replacing the following placeholder values (highlighted in the code):

    Placeholder
    Valid Value

    <NUMBER-OF-CANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to use during the search. For this example, specify 5.

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of documents to return in the results. For this example, specify 2.

    <EMBEDDING-MODEL>

    Embedding model to use for generating the embeddings. For this example, specify voyage-3-large.

    <TEXT-FIELD-NAME>

    Name of the field that contains the text data.

    1import voyageai
    2from bson.binary import Binary, BinaryVectorDtype
    3
    4# Define a function to run a vector search query
    5def run_vector_search(query_text, collection, path):
    6 # Map path to output dtype and BSON vector type
    7 path_to_dtype = {
    8 float32_field: ("float", BinaryVectorDtype.FLOAT32),
    9 int8_field: ("int8", BinaryVectorDtype.INT8),
    10 int1_field: ("ubinary", BinaryVectorDtype.PACKED_BIT),
    11 }
    12
    13 if path not in path_to_dtype:
    14 raise ValueError("Invalid path. Must be one of float32_field, int8_field, int1_field.")
    15
    16 # Get Voyage AI output dtype and BSON vector type based on the path
    17 output_dtype, bson_dtype = path_to_dtype[path]
    18
    19 # Generate query embeddings using Voyage AI
    20 query_vector = vo.embed(
    21 texts=[query_text],
    22 model="<EMBEDDING-MODEL>",
    23 input_type="query",
    24 output_dtype=output_dtype
    25 ).embeddings[0]
    26
    27 # Convert the query vector to BSON format
    28 bson_query_vector = Binary.from_vector(query_vector, bson_dtype)
    29
    30 # Define the aggregation pipeline for vector search
    31 pipeline = [
    32 {
    33 "$vectorSearch": {
    34 "index": index_name, # Replace with your index name
    35 "path": path, # Path to the embedding field
    36 "queryVector": bson_query_vector, # BSON-encoded query vector
    37 "numCandidates": <NUMBER-OF-CANDIDATES-TO-CONSIDER>,
    38 "limit": <NUMBER-OF-DOCUMENTS-TO-RETURN>
    39 }
    40 },
    41 {
    42 "$project": {
    43 "_id": 0,
    44 "<TEXT-FIELD-NAME>": 1,
    45 "score": { "$meta": "vectorSearchScore" } # Include the similarity score
    46 }
    47 }
    48 ]
    49
    50 # Run the aggregation pipeline and return results
    51 return collection.aggregate(pipeline)
  2. Run the Atlas Vector Search query.

    Copy, paste, and run the sample code below after replacing the following placeholder value as highlighted in the code:

    Placeholder
    Valid Value

    <QUERY-TEXT>

    Text string for which to retrieve semantically similar documents. For this example, specify science fact.

    1from pprint import pprint
    2
    3# Define a list of embedding fields to query
    4embedding_fields = [float32_field, int8_field, int1_field]
    5results = {}
    6
    7# Run vector search queries for each embedding type
    8query_text = "<QUERY-TEXT>"
    9for field in embedding_fields:
    10 results[field] = list(run_vector_search(query_text, collection, field))
    11
    12# Print the results
    13for field, field_results in results.items():
    14 print(f"Results from {field}")
    15 pprint(field_results)
    Results from float32-embeddings embeddings
    [{'data': 'The Great Wall of China is visible from space.',
    'score': 0.7810189723968506},
    {'data': 'Mount Everest is the highest peak on Earth at 8,848m.',
    'score': 0.7339795827865601}]
    Results from int8-embeddings embeddings
    [{'data': 'The Great Wall of China is visible from space.',
    'score': 0.5053843259811401},
    {'data': 'Mount Everest is the highest peak on Earth at 8,848m.',
    'score': 0.5043729543685913}]
    Results from int1-embeddings embeddings
    [{'data': 'The Great Wall of China is visible from space.', 'score': 0.6640625},
    {'data': 'Mount Everest is the highest peak on Earth at 8,848m.',
    'score': 0.6220703125}]

    To learn more about Atlas Vector Search queries, see Run Vector Search Queries.

Work with a runnable version of this tutorial as a Python notebook.

1

Run the following command to install Voyage AI and the PyMongo Driver. You must install PyMongo v4.10 or later driver.

pip install --quiet --upgrade voyageai pymongo

This operation might take a few minutes to complete.

2

In this step, you define functions for the following purposes:

  • Generate embeddings by using Voyage AI.

  • Convert embeddings to BSON vectors by using the PyMongo driver.

Copy, paste, and run the sample code below after replacing the following placeholder value (highlighted in the code):

Placeholder
Valid Value

<VOYAGE-API-KEY>

Voyage AI API key to use for generating embeddings.

1import os
2import voyageai
3from bson.binary import Binary, BinaryVectorDtype
4
5# Initialize the VoyageAI Client
6os.environ["VOYAGE_API_KEY"] = "<VOYAGEAI-API-KEY>"
7vo = voyageai.Client()
8
9# Define a function to generate embeddings for all strings in `texts`
10def generate_embeddings(texts, model: str, dtype: str, output_dimension: int):
11 embeddings = []
12 for text in texts: # Process eachstring in the data list
13 embedding = vo.embed(
14 texts=[text], # Pass each string as a list with a single item
15 model=model,
16 output_dtype=dtype,
17 output_dimension=output_dimension,
18 ).embeddings[0]
19 embeddings.append(embedding) # Collect the embedding for the current text
20 return embeddings
21
22# Convert embeddings to BSON vectors
23def generate_bson_vector(vector, vector_dtype):
24 return Binary.from_vector(vector, vector_dtype)
3

You must provide the following:

  • Connection string to connect to your Atlas cluster that contains the database and collection for which you want to generate embeddings.

  • Name of the database that contains the collection for which you want to generate embeddings.

  • Name of the collection for which you want to generate embeddings.

To retrieve the data, copy, paste, and run the sample code below after replacing the placeholder values (highlighted in the code):

Placeholder
Valid Value

<CONNECTION-STRING>

Cluster connection string. To learn more, see Connect via Drivers.

<DATABASE-NAME>

Name of the database that contains the collection for which you want to generate and convert embeddings. For this example, specify sample_airbnb.

<COLLECTION-NAME>

Name of the collection for which you want to generate and convert embeddings. For this example, specify listingsAndReviews.

<TEXT-FIELD-NAME>

Name of the text field for which you want to generate embeddings. For this example, specify summary.

1import pymongo
2
3# Connect to your Atlas cluster
4mongo_client = pymongo.MongoClient("<CONNECTION-STRING>")
5db = mongo_client["<DATABASE-NAME>"]
6collection = db["<COLLECTION-NAME>"]
7
8# Filter to exclude null or empty summary fields
9filter = { "<TEXT-FIELD-NAME>": {"$nin": [None, ""]} }
10
11# Get a subset of documents in the collection
12documents = collection.find(filter).limit(50)
13
14# Initialize the count of updated documents
15updated_doc_count = 0
4

The sample code performs the following actions:

  1. Generates embeddings from your data using any embedding model if your data doesn't already have embeddings. To learn more about generating embeddings from your data, see How to Create Vector Embeddings.

  2. Converts the embeddings to BSON vectors (as shown on line 7 in the following example).

  3. Uploads the embeddings to your collection on the Atlas cluster.

These operation might take a few minutes to complete.

Copy, paste, and run the code below after replacing the following placeholder values (highlighted in the code):

Placeholder
Valid Value

<EMBEDDING-MODEL>

Embedding model to use for generating the embeddings. For this example, specify voyage-3-large.

<NUMBER-OF-DIMENSIONS>

Number of dimensions for the resulting output embeddings. For this example, specify 1024.

<FIELD-NAME-FOR-FLOAT32-TYPE>

Name of field with float32 values.

<FIELD-NAME-FOR-INT8-TYPE>

Name of field with int8 values.

<FIELD-NAME-FOR-INT1-TYPE>

Name of field with int1 values.

<EMBEDDING-MODEL>

Embedding model to use for generating the embeddings. For this example, specify voyage-3-large.

<TEXT-FIELD-NAME>

Name of the text field for which you generated embeddings. For this example, specify summary.

1model_name = "<EMBEDDING-MODEL>"
2output_dimension = <NUMBER-OF-DIMENSIONS>
3float32_field = "<FIELD-NAME-FOR-FLOAT32-TYPE>"
4int8_field = "<FIELD-NAME-FOR-INT8-TYPE>"
5int1_field = "<FIELD-NAME-FOR-INT1-TYPE>"
6
7# Process and update each document
8updated_doc_count = 0
9for document in documents:
10 summary = document.get("<TEXT-FIELD-NAME>")
11 if not summary:
12 continue
13
14 # Generate embeddings for the summary field
15 float_embeddings = generate_embeddings([summary], model=model_name, dtype="float", output_dimension=output_dimension)
16 int8_embeddings = generate_embeddings([summary], model=model_name, dtype="int8", output_dimension=output_dimension)
17 ubinary_embeddings = generate_embeddings([summary], model=model_name, dtype="ubinary", output_dimension=output_dimension)
18
19 # Convert embeddings to BSON-compatible format
20 bson_float = generate_bson_vector(float_embeddings[0], BinaryVectorDtype.FLOAT32)
21 bson_int8 = generate_bson_vector(int8_embeddings[0], BinaryVectorDtype.INT8)
22 bson_ubinary = generate_bson_vector(ubinary_embeddings[0], BinaryVectorDtype.PACKED_BIT)
23
24 # Prepare the updated document
25 updated_fields = {
26 float32_field: bson_float,
27 int8_field: bson_int8,
28 int1_field: bson_ubinary,
29 }
30
31 # Update the document in MongoDB
32 result = collection.update_one({"_id": document["_id"]}, {"$set": updated_fields})
33 if result.modified_count > 0:
34 updated_doc_count += 1
35
36# Print the results
37print(f"Number of documents updated: {updated_doc_count}")
5

You can create Atlas Vector Search indexes by using the Atlas UI, Atlas CLI, Atlas Administration API, and MongoDB drivers. To learn more, see How to Index Fields for Vector Search.

To create the index, copy, paste, and run the sample code below after replacing the following placeholder value (highlighted in the code):

Placeholder
Valid Value

<INDEX-NAME>

Name of vector type index.

1from pymongo.operations import SearchIndexModel
2import time
3
4# Define and create the vector search index
5index_name = "<INDEX-NAME>"
6search_index_model = SearchIndexModel(
7 definition={
8 "fields": [
9 {
10 "type": "vector",
11 "path": float32_field,
12 "similarity": "dotProduct",
13 "numDimensions": 1024
14 },
15 {
16 "type": "vector",
17 "path": int8_field,
18 "similarity": "dotProduct",
19 "numDimensions": 1024
20 },
21 {
22 "type": "vector",
23 "path": int1_field,
24 "similarity": "euclidean",
25 "numDimensions": 1024
26 }
27 ]
28 },
29 name=index_name,
30 type="vectorSearch"
31)
32result = collection.create_search_index(model=search_index_model)
33print("New search index named " + result + " is building.")
34
35# Wait for initial sync to complete
36print("Polling to check if the index is ready. This may take up to a minute.")
37predicate=None
38if predicate is None:
39 predicate = lambda index: index.get("queryable") is True
40while True:
41 indices = list(collection.list_search_indexes(index_name))
42 if len(indices) and predicate(indices[0]):
43 break
44 time.sleep(5)
45print(result + " is ready for querying.")
New search index named <INDEX-NAME> is building.
Polling to check if the index is ready. This may take up to a minute.
<INDEX-NAME> is ready for querying.
6
  1. Define a function to run a vector search query.

    The function to run Atlas Vector Search queries performs the following actions:

    • Generates embeddings using Voyage AI for the query text.

    • Converts the embeddings to BSON vectors.

    • Defines the aggregation pipeline for the vector search.

    • Runs the aggregation pipeline and returns the results.

    Placeholder
    Valid Value

    <NUMBER-OF-CANDIDATES-TO-CONSIDER>

    Number of nearest neighbors to use during the search. For this example, specify 20

    <NUMBER-OF-DOCUMENTS-TO-RETURN>

    Number of documents to return in the results. For this example, specify 5.

    <TEXT-FIELD-NAME>

    Name of the field that contains the text data. For this example, specify summary.

    1import voyageai
    2from bson.binary import Binary, BinaryVectorDtype
    3
    4# Define a function to run a vector search query
    5def run_vector_search(query_text, collection, path):
    6 # Map path to output dtype and BSON vector type
    7 path_to_dtype = {
    8 float32_field: ("float", BinaryVectorDtype.FLOAT32),
    9 int8_field: ("int8", BinaryVectorDtype.INT8),
    10 int1_field: ("ubinary", BinaryVectorDtype.PACKED_BIT),
    11 }
    12
    13 if path not in path_to_dtype:
    14 raise ValueError("Invalid path. Must be one of float32_field, int8_field, int1_field.")
    15
    16 # Get Voyage AI output dtype and BSON vector type based on the path
    17 output_dtype, bson_dtype = path_to_dtype[path]
    18
    19 # Generate query embeddings using Voyage AI
    20 query_vector = vo.embed(
    21 texts=[query_text],
    22 model="<EMBEDDING-MODEL>",
    23 input_type="query",
    24 output_dtype=output_dtype
    25 ).embeddings[0]
    26
    27 # Convert the query vector to BSON format
    28 bson_query_vector = Binary.from_vector(query_vector, bson_dtype)
    29
    30 # Define the aggregation pipeline for vector search
    31 pipeline = [
    32 {
    33 "$vectorSearch": {
    34 "index": index_name, # Replace with your index name
    35 "path": path, # Path to the embedding field
    36 "queryVector": bson_query_vector, # BSON-encoded query vector
    37 "numCandidates": <NUMBER-OF-CANDIDATES-TO-CONSIDER>,
    38 "limit": <NUMBER-OF-DOCUMENTS-TO-RETURN>
    39 }
    40 },
    41 {
    42 "$project": {
    43 "_id": 0,
    44 "<TEXT-FIELD-NAME>": 1,
    45 "score": { "$meta": "vectorSearchScore" } # Include the similarity score
    46 }
    47 }
    48 ]
    49
    50 # Run the aggregation pipeline and return results
    51 return collection.aggregate(pipeline)
  2. Run the Atlas Vector Search query.

    You can run Atlas Vector Search queries programmatically. To learn more, see Run Vector Search Queries.

    Placeholder
    Valid Value

    <QUERY-TEXT>

    Text string for which to retrieve semantically similar documents. For this example, specify ocean view.

    1from pprint import pprint
    2
    3# Define a list of embedding fields to query
    4embedding_fields = [float32_field, int8_field, int1_field]
    5results = {}
    6
    7# Run vector search queries for each embedding type
    8query_text = "<QUERY-TEXT>"
    9for field in embedding_fields:
    10 results[field] = list(run_vector_search(query_text, collection, field))
    11
    12# Print the results
    13for field, field_results in results.items():
    14 print(f"Results from {field}")
    15 pprint(field_results)
    Results from float32-embeddings
    [{'score': 0.8044508695602417,
    'summary': 'A beautiful and comfortable 1 Bedroom Air Conditioned Condo in '
    'Makaha Valley - stunning Ocean & Mountain views All the '
    'amenities of home, suited for longer stays. Full kitchen & large '
    "bathroom. Several gas BBQ's for all guests to use & a large "
    'heated pool surrounded by reclining chairs to sunbathe. The '
    'Ocean you see in the pictures is not even a mile away, known as '
    'the famous Makaha Surfing Beach. Golfing, hiking,snorkeling '
    'paddle boarding, surfing are all just minutes from the front '
    'door.'},
    {'score': 0.7622430920600891,
    'summary': 'THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE '
    'BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU '
    'WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO '
    'THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!'},
    {'score': 0.7484776973724365,
    'summary': 'Para 2 pessoas. Vista de mar a 150 mts. Prédio com 2 elevadores. '
    'Tem: - quarto com roupeiro e cama de casal (colchão '
    'magnetizado); - cozinha: placa de discos, exaustor, frigorifico, '
    'micro-ondas e torradeira; casa de banho completa; - sala e '
    'varanda.'},
    {'score': 0.7452666759490967,
    'summary': 'Quarto com vista para a Lagoa Rodrigo de Freitas, cartão postal '
    'do Rio de Janeiro. Linda Vista. 1 Quarto e 1 banheiro Amplo, '
    'arejado, vaga na garagem. Prédio com piscina, sauna e '
    'playground. Fácil acesso, próximo da praia e shoppings.'},
    {'score': 0.73777174949646,
    'summary': 'próximo aos principais pontos turísticos,,do lado do metro, '
    'vista p o CRISTO REDENTOR, GARAGEM, FAXINEIRA, PLAY.'}]
    Results from int8-embeddings embeddings
    [{'score': 0.5057082176208496,
    'summary': 'A beautiful and comfortable 1 Bedroom Air Conditioned Condo in '
    'Makaha Valley - stunning Ocean & Mountain views All the '
    'amenities of home, suited for longer stays. Full kitchen & large '
    "bathroom. Several gas BBQ's for all guests to use & a large "
    'heated pool surrounded by reclining chairs to sunbathe. The '
    'Ocean you see in the pictures is not even a mile away, known as '
    'the famous Makaha Surfing Beach. Golfing, hiking,snorkeling '
    'paddle boarding, surfing are all just minutes from the front '
    'door.'},
    {'score': 0.5048595666885376,
    'summary': 'THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE '
    'BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU '
    'WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO '
    'THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!'},
    {'score': 0.5045757293701172,
    'summary': 'Para 2 pessoas. Vista de mar a 150 mts. Prédio com 2 elevadores. '
    'Tem: - quarto com roupeiro e cama de casal (colchão '
    'magnetizado); - cozinha: placa de discos, exaustor, frigorifico, '
    'micro-ondas e torradeira; casa de banho completa; - sala e '
    'varanda.'},
    {'score': 0.5044537782669067,
    'summary': 'Quarto com vista para a Lagoa Rodrigo de Freitas, cartão postal '
    'do Rio de Janeiro. Linda Vista. 1 Quarto e 1 banheiro Amplo, '
    'arejado, vaga na garagem. Prédio com piscina, sauna e '
    'playground. Fácil acesso, próximo da praia e shoppings.'},
    {'score': 0.5044353604316711,
    'summary': 'The ultimate way to experience Sydney Harbour; fireworks, the '
    'bridge, and the proximity to the city means you can experience '
    'everything this city has to offer. Tucked into the Balmain '
    "Peninsula, you're close to parks, pubs, shops, buses, and more!"}]
    Results from int1-embeddings embeddings
    [{'score': 0.7158203125,
    'summary': 'A beautiful and comfortable 1 Bedroom Air Conditioned Condo in '
    'Makaha Valley - stunning Ocean & Mountain views All the '
    'amenities of home, suited for longer stays. Full kitchen & large '
    "bathroom. Several gas BBQ's for all guests to use & a large "
    'heated pool surrounded by reclining chairs to sunbathe. The '
    'Ocean you see in the pictures is not even a mile away, known as '
    'the famous Makaha Surfing Beach. Golfing, hiking,snorkeling '
    'paddle boarding, surfing are all just minutes from the front '
    'door.'},
    {'score': 0.6865234375,
    'summary': 'Para 2 pessoas. Vista de mar a 150 mts. Prédio com 2 elevadores. '
    'Tem: - quarto com roupeiro e cama de casal (colchão '
    'magnetizado); - cozinha: placa de discos, exaustor, frigorifico, '
    'micro-ondas e torradeira; casa de banho completa; - sala e '
    'varanda.'},
    {'score': 0.677734375,
    'summary': 'próximo aos principais pontos turísticos,,do lado do metro, '
    'vista p o CRISTO REDENTOR, GARAGEM, FAXINEIRA, PLAY.'},
    {'score': 0.6748046875,
    'summary': 'Cozy and comfortable apartment. Ideal for families and '
    'vacations. 3 bedrooms, 2 of them suites. Located 20-min walk '
    'to the beach and close to the Rio 2016 Olympics Venues. Situated '
    'in a modern and secure condominium, with many entertainment '
    'available options around.'},
    {'score': 0.6728515625,
    'summary': 'THIS IS A VERY SPACIOUS 1 BEDROOM FULL CONDO (SLEEPS 4) AT THE '
    'BEAUTIFUL VALLEY ISLE RESORT ON THE BEACH IN LAHAINA, MAUI!! YOU '
    'WILL LOVE THE PERFECT LOCATION OF THIS VERY NICE HIGH RISE! ALSO '
    'THIS SPACIOUS FULL CONDO, FULL KITCHEN, BIG BALCONY!!'}]

You can measure the accuracy of your Atlas Vector Search query by evaluating how closely the results for an ANN search match the results of an ENN search against your quantized vectors. That is, you can compare the results of ANN search with the results of ENN search for the same query criteria and measure how frequently the ANN search results include the nearest neighbors in the results from the ENN search.

For a demonstration of evaluating your query results, see How to Measure the Accuracy of Your Query Results.

Back

Transform Documents & Filter Collections

On this page