Docs Menu
Docs Home
/
Atlas
/ /

Get Started with the LangChainGo Integration

You can integrate Atlas Vector Search with LangChainGo to build large language model (LLM) applications and implement retrieval-augmented generation (RAG). This tutorial demonstrates how to start using Atlas Vector Search with LangChainGo to perform semantic search on your data and build a RAG implementation. Specifically, you perform the following actions:

  1. Set up the environment.

  2. Store custom data on Atlas.

  3. Create an Atlas Vector Search index on your data.

  4. Run the following vector search queries:

    • Semantic search.

    • Semantic search with metadata pre-filtering.

  5. Implement RAG by using Atlas Vector Search to answer questions on your data.

LangChainGo is the Go programming language implementation of LangChain. It is a community-driven, third-party port of the LangChain framework.

LangChain is an open-source framework that simplifies the creation of LLM applications through the use of "chains." Chains are LangChain-specific components that can be combined for a variety of AI use cases, including RAG.

By integrating Atlas Vector Search with LangChain, you can use Atlas as a vector database and use Atlas Vector Search to implement RAG by retrieving semantically similar documents from your data. To learn more about RAG, see Retrieval-Augmented Generation (RAG) with Atlas Vector Search.

LangChainGo facilitates the orchestration of LLMs for AI applications, bringing the capabilities of LangChain into the Go ecosystem. It also allows developers to connect to their preferred databases using vector stores, including MongoDB.

To complete this tutorial, you must have the following:

  • An Atlas account with a cluster running MongoDB version 6.0.11, 7.0.2, or later (including RCs). Ensure that your IP address is included in your Atlas project's access list. To learn more, see Create a Cluster.

  • An OpenAI API Key. You must have an OpenAI account with credits available for API requests. To learn more about registering an OpenAI account, see the OpenAI API website.

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

  • Go installed on your machine.

You must first set up the environment for this tutorial. Complete the following steps to set up your environment.

1

Run the following commands in your terminal to create a new directory named langchaingo-mongodb and initialize your project:

mkdir langchaingo-mongodb
cd langchaingo-mongodb
go mod init langchaingo-mongodb
2

Run the following commands:

go get github.com/joho/godotenv
go get github.com/tmc/langchaingo/chains
go get github.com/tmc/langchaingo/llms
go get github.com/tmc/langchaingo/prompts
go get github.com/tmc/langchaingo/vectorstores/mongovector
go get go.mongodb.org/mongo-driver/v2/mongo
go mod tidy
3

In your langchaingo-mongodb project directory, create a .env file and add the following lines:

OPENAI_API_KEY="<api-key>"
ATLAS_CONNECTION_STRING="<connection-string>"

Replace the placeholder values with your OpenAI API Key and the SRV connection string for your Atlas cluster. Your connection string should use the following format:

mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
4

In your langchaingo-mongodb project directory, create a file named main.go. You will add code to this file throughout the tutorial.

In this section, you define an asynchronous function to load custom data into Atlas and instantiate Atlas as a vector database, also called a vector store.

1

Add the following imports to the top of your main.go file.

package main
import (
"context"
"log"
"os"
"github.com/joho/godotenv"
"github.com/tmc/langchaingo/embeddings"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/schema"
"github.com/tmc/langchaingo/vectorstores/mongovector"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
2

The following code performs these actions:

  • Configures Atlas as a vector store by specifying the following:

    • langchaingo_db.test as the collection in Atlas to store the documents.

    • vector_index as the index to use for querying the vector store.

    • text as the name of the field containing the raw text content.

    • embedding as the name of the field containing the vector embeddings.

  • Prepares your custom data by doing the following:

    • Defines text for each document.

    • Uses LangChainGo's mongovector package to generate embeddings for the texts. This package stores document embeddings in MongoDB and enables searches on stored embeddings.

    • Constructs documents that include text, embeddings, and metadata.

  • Ingests the constructed documents into Atlas and instantiates the vector store.

Paste the following code into your main.go file:

// Defines the document structure
type Document struct {
PageContent string `bson:"text"`
Embedding []float32 `bson:"embedding"`
Metadata map[string]string `bson:"metadata"`
}
func main() {
const (
openAIEmbeddingModel = "text-embedding-3-small"
openAIEmbeddingDim = 1536
similarityAlgorithm = "dotProduct"
indexName = "vector_index"
databaseName = "langchaingo_db"
collectionName = "test"
)
if err := godotenv.Load(); err != nil {
log.Fatal("No .env file found")
}
// Loads the MongoDB URI from environment
uri := os.Getenv("ATLAS_CONNECTION_STRING")
if uri == "" {
log.Fatal("Set your 'ATLAS_CONNECTION_STRING' environment variable in the .env file")
}
// Loads the API key from environment
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("Set your OPENAI_API_KEY environment variable in the .env file")
}
// Connects to MongoDB Atlas
client, err := mongo.Connect(options.Client().ApplyURI(uri))
if err != nil {
log.Fatalf("Failed to connect to server: %v", err)
}
defer func() {
if err := client.Disconnect(context.Background()); err != nil {
log.Fatalf("Error disconnecting the client: %v", err)
}
}()
log.Println("Connected to MongoDB Atlas.")
// Selects the database and collection
coll := client.Database(databaseName).Collection(collectionName)
// Creates an OpenAI LLM embedder client
llm, err := openai.New(openai.WithEmbeddingModel(openAIEmbeddingModel))
if err != nil {
log.Fatalf("Failed to create an embedder client: %v", err)
}
// Creates an embedder from the embedder client
embedder, err := embeddings.NewEmbedder(llm)
if err != nil {
log.Fatalf("Failed to create an embedder: %v", err)
}
// Creates a new MongoDB Atlas vector store
store := mongovector.New(coll, embedder, mongovector.WithIndex(indexName), mongovector.WithPath("embeddings"))
// Checks if the collection is empty, and if empty, adds documents to the MongoDB Atlas database vector store
if isCollectionEmpty(coll) {
documents := []schema.Document{
{
PageContent: "Proper tuber planting involves site selection, proper timing, and exceptional care. Choose spots with well-drained soil and adequate sun exposure. Tubers are generally planted in spring, but depending on the plant, timing varies. Always plant with the eyes facing upward at a depth two to three times the tuber's height. Ensure 4 inch spacing between small tubers, expand to 12 inches for large ones. Adequate moisture is needed, yet do not overwater. Mulching can help preserve moisture and prevent weed growth.",
Metadata: map[string]any{
"author": "A",
"type": "post",
},
},
{
PageContent: "Successful oil painting necessitates patience, proper equipment, and technique. Begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space.",
Metadata: map[string]any{
"author": "B",
"type": "post",
},
},
{
PageContent: "For a natural lawn, selection of the right grass type suitable for your climate is crucial. Balanced watering, generally 1 to 1.5 inches per week, is important; overwatering invites disease. Opt for organic fertilizers over synthetic versions to provide necessary nutrients and improve soil structure. Regular lawn aeration helps root growth and prevents soil compaction. Practice natural pest control and consider overseeding to maintain a dense sward, which naturally combats weeds and pest.",
Metadata: map[string]any{
"author": "C",
"type": "post",
},
},
}
_, err := store.AddDocuments(context.Background(), documents)
if err != nil {
log.Fatalf("Error adding documents: %v", err)
}
log.Printf("Successfully added %d documents to the collection.\n", len(documents))
} else {
log.Println("Documents already exist in the collection, skipping document addition.")
}
}
func isCollectionEmpty(coll *mongo.Collection) bool {
count, err := coll.EstimatedDocumentCount(context.Background())
if err != nil {
log.Fatalf("Failed to count documents in the collection: %v", err)
}
return count == 0
}
3

Save the file, then run the following command to load your data into Atlas.

go run main.go
Connected to MongoDB Atlas.
Successfully added 3 documents to the collection.

Tip

After running main.go, you can view your vector embeddings in the Atlas UI by navigating to the langchaingo_db.test collection in your cluster.

Note

To create an Atlas Vector Search index, you must have Project Data Access Admin or higher access to the Atlas project.

To enable vector search queries on your vector store, create an Atlas Vector Search index on the langchaingo_db.test collection.

Add the following imports to the top of your main.go file:

import (
// Other imports...
"fmt"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)

Define the following functions in your main.go file outside of your main() function. These functions create and manage a vector search index for your MongoDB collection:

  1. The SearchIndexExists function checks if a search index with the specified name exists and is queryable.

  2. The CreateVectorSearchIndex functions creates a vector search index on the specified collection. This function blocks until the index is created and queryable.

// Checks if the search index exists
func SearchIndexExists(ctx context.Context, coll *mongo.Collection, idx string) (bool, error) {
log.Println("Checking if search index exists.")
view := coll.SearchIndexes()
siOpts := options.SearchIndexes().SetName(idx).SetType("vectorSearch")
cursor, err := view.List(ctx, siOpts)
if err != nil {
return false, fmt.Errorf("failed to list search indexes: %w", err)
}
for cursor.Next(ctx) {
index := struct {
Name string `bson:"name"`
Queryable bool `bson:"queryable"`
}{}
if err := cursor.Decode(&index); err != nil {
return false, fmt.Errorf("failed to decode search index: %w", err)
}
if index.Name == idx && index.Queryable {
return true, nil
}
}
if err := cursor.Err(); err != nil {
return false, fmt.Errorf("cursor error: %w", err)
}
return false, nil
}
// Creates a vector search index. This function blocks until the index has been
// created.
func CreateVectorSearchIndex(
ctx context.Context,
coll *mongo.Collection,
idxName string,
openAIEmbeddingDim int,
similarityAlgorithm string,
) (string, error) {
type vectorField struct {
Type string `bson:"type,omitempty"`
Path string `bson:"path,omitempty"`
NumDimensions int `bson:"numDimensions,omitempty"`
Similarity string `bson:"similarity,omitempty"`
}
fields := []vectorField{
{
Type: "vector",
Path: "embeddings",
NumDimensions: openAIEmbeddingDim,
Similarity: similarityAlgorithm,
},
{
Type: "filter",
Path: "metadata.author",
},
{
Type: "filter",
Path: "metadata.type",
},
}
def := struct {
Fields []vectorField `bson:"fields"`
}{
Fields: fields,
}
log.Println("Creating vector search index...")
view := coll.SearchIndexes()
siOpts := options.SearchIndexes().SetName(idxName).SetType("vectorSearch")
searchName, err := view.CreateOne(ctx, mongo.SearchIndexModel{Definition: def, Options: siOpts})
if err != nil {
return "", fmt.Errorf("failed to create the search index: %w", err)
}
// Awaits the creation of the index
var doc bson.Raw
for doc == nil {
cursor, err := view.List(ctx, options.SearchIndexes().SetName(searchName))
if err != nil {
return "", fmt.Errorf("failed to list search indexes: %w", err)
}
if !cursor.Next(ctx) {
break
}
name := cursor.Current.Lookup("name").StringValue()
queryable := cursor.Current.Lookup("queryable").Boolean()
if name == searchName && queryable {
doc = cursor.Current
} else {
time.Sleep(5 * time.Second)
}
}
return searchName, nil
}

Create the vector store collection and index by calling the preceding functions in your main() function. Add the following code to the end of your main() function:

// SearchIndexExists will return true if the provided index is defined for the
// collection. This operation blocks until the search completes.
if ok, _ := SearchIndexExists(context.Background(), coll, indexName); !ok {
// Creates the vector store collection
err = client.Database(databaseName).CreateCollection(context.Background(), collectionName)
if err != nil {
log.Fatalf("failed to create vector store collection: %v", err)
}
_, err = CreateVectorSearchIndex(context.Background(), coll, indexName, openAIEmbeddingDim, similarityAlgorithm)
if err != nil {
log.Fatalf("failed to create index: %v", err)
}
log.Println("Successfully created vector search index.")
} else {
log.Println("Vector search index already exists.")
}

Save the file, then run the following command to create your Atlas Vector Search index.

go run main.go
Checking if search index exists.
Creating vector search index...
Successfully created vector search index.

Tip

After running main.go, you can view your vector search index in the Atlas UI by navigating to the langchaingo_db.test collection in your cluster.

This section demonstrates various queries that you can run on your vectorized data. Now that you've created the index, you can run vector search queries.

Select the Basic Semantic Search or Semantic Search with Filtering tab to see the corresponding code.

1

Semantic search retrieves information that is meaningfully related to a query. The following code uses the SimilaritySearch() method to perform a semantic search for the string "Prevent weeds" and limits the results to the first document.

// Performs basic semantic search
docs, err := store.SimilaritySearch(context.Background(), "Prevent weeds", 1)
if err != nil {
fmt.Println("Error performing search:", err)
}
fmt.Println("Semantic Search Results:", docs)
2
go run main.go
Semantic Search Results: [{For a natural lawn, selection of
the right grass type suitable for your climate is crucial.
Balanced watering, generally 1 to 1.5 inches per week, is
important; overwatering invites disease. Opt for organic
fertilizers over synthetic versions to provide necessary
nutrients and improve soil structure. Regular lawn aeration
helps root growth and prevents soil compaction. Practice
natural pest control and consider overseeding to maintain a
dense sward, which naturally combats weeds and pest.
map[author:C type:post] 0.69752026}]

You can pre-filter your data by using an MQL match expression that compares the indexed field with another value in your collection. You must index any metadata fields that you want to filter by as the filter type. To learn more, see How to Index Fields for Vector Search.

1

Add the following dependencies to your main.go file:

import (
// Other imports...
"github.com/tmc/langchaingo/vectorstores"
)
2

The following code uses the SimilaritySearch() method to perform a semantic search for the string "Tulip care". It specifies the following parameters:

  • The number of documents to return as 1.

  • A score threshold of 0.60.

It returns the document that matches the filter metadata.type: post and includes the score threshold.

// Performs semantic search with metadata filter
filter := map[string]interface{}{
"metadata.type": "post",
}
docs, err := store.SimilaritySearch(context.Background(), "Tulip care", 1,
vectorstores.WithScoreThreshold(0.60),
vectorstores.WithFilters(filter))
if err != nil {
fmt.Println("Error performing search:", err)
}
fmt.Println("Filter Search Results:", docs)
3
go run main.go
Filter Search Results: [{Proper tuber planting involves site
selection, proper timing, and exceptional care. Choose spots
with well-drained soil and adequate sun exposure. Tubers are
generally planted in spring, but depending on the plant,
timing varies. Always plant with the eyes facing upward at a
depth two to three times the tuber's height. Ensure 4 inch
spacing between small tubers, expand to 12 inches for large
ones. Adequate moisture is needed, yet do not overwater.
Mulching can help preserve moisture and prevent weed growth.
map[author:A type:post] 0.64432365}]

This section demonstrates a RAG implementation using Atlas Vector Search and LangChainGo. Now that you've used Atlas Vector Search to retrieve semantically similar documents, use the following code example to prompt the LLM to answer questions against the documents returned by Atlas Vector Search.

1

Add the following imports to the top of your main.go file.

import (
// Other imports...
"strings"
"github.com/tmc/langchaingo/chains"
"github.com/tmc/langchaingo/prompts"
)
2

This code does the following:

  • Instantiates Atlas Vector Search as a retriever to query for semantically similar documents.

  • Defines a LangChainGo prompt template to instruct the LLM to use the retrieved documents as context for your query. LangChainGo populates these documents into the {{.context}} input variable and your query into the {{.question}} variable.

  • Constructs a chain that uses OpenAI's chat model to generate context-aware responses based on the provided prompt template.

  • Sends a sample query about painting for beginners to the chain, using the prompt and the retriever to gather relevant context.

  • Returns and prints the LLM's response and the documents used as context.

// Implements RAG to answer questions on your data
optionsVector := []vectorstores.Option{
vectorstores.WithScoreThreshold(0.60),
}
retriever := vectorstores.ToRetriever(&store, 1, optionsVector...)
prompt := prompts.NewPromptTemplate(
`Answer the question based on the following context:
{{.context}}
Question: {{.question}}`,
[]string{"context", "question"},
)
llmChain := chains.NewLLMChain(llm, prompt)
ctx := context.Background()
const question = "How do I get started painting?"
documents, err := retriever.GetRelevantDocuments(ctx, question)
if err != nil {
log.Fatalf("Failed to retrieve documents: %v", err)
}
var contextBuilder strings.Builder
for i, document := range documents {
contextBuilder.WriteString(fmt.Sprintf("Document %d: %s\n", i+1, document.PageContent))
}
contextStr := contextBuilder.String()
inputs := map[string]interface{}{
"context": contextStr,
"question": question,
}
out, err := chains.Call(ctx, llmChain, inputs)
if err != nil {
log.Fatalf("Failed to run LLM chain: %v", err)
}
log.Println("Source documents:")
for i, doc := range documents {
log.Printf("Document %d: %s\n", i+1, doc.PageContent)
}
responseText, ok := out["text"].(string)
if !ok {
log.Println("Unexpected response type")
return
}
log.Println("Question:", question)
log.Println("Generated Answer:", responseText)
3

After you save the file, run the following command. The generated response might vary.

go run main.go
Source documents:
Document 1: "Successful oil painting necessitates patience,
proper equipment, and technique. Begin with a carefully
prepared, primed canvas. Sketch your composition lightly before
applying paint. Use high-quality brushes and oils to create
vibrant, long-lasting artworks. Remember to paint 'fat over
lean,' meaning each subsequent layer should contain more oil to
prevent cracking. Allow each layer to dry before applying
another. Clean your brushes often and avoid solvents that might
damage them. Finally, always work in a well-ventilated space."
Question: How do I get started painting?
Generated Answer: To get started painting, you should begin with a
carefully prepared, primed canvas. Sketch your composition lightly
before applying paint. Use high-quality brushes and oils to create
vibrant, long-lasting artworks. Remember to paint 'fat over lean,'
meaning each subsequent layer should contain more oil to prevent
cracking. Allow each layer to dry before applying another. Clean
your brushes often and avoid solvents that might damage them.
Finally, always work in a well-ventilated space.

After completing this tutorial, you have successfully integrated Atlas Vector Search with LangChainGo to build a RAG application. You have accomplished the following:

  • Initiated and configured the necessary environment to support your application

  • Stored custom data in Atlas and instantiated Atlas as a vector store

  • Built an Atlas Vector Search index on your data, enabling semantic search capabilities

  • Used vector embeddings to retrieve semantically relevant data

  • Enhanced search results by incorporating metadata filters

  • Implemented a RAG workflow using Atlas Vector Search to provide meaningful answers to questions based on your data

MongoDB also provides the following developer resource:

See also:

To learn more about integrating LangChainGo, OpenAI, and MongoDB, see Using MongoDB Atlas as a Vector Store with OpenAI Embeddings.

Back

Build Agents