Link Search Menu Expand Document Documentation Menu

Go client

Preview

The Lucenia Go client lets you connect your Go application with the data in your Lucenia cluster. This getting started guide illustrates how to connect to Lucenia, index documents, and run queries.

Setup

If you’re starting a new project, create a new module by running the following command:

go mod init <mymodulename>

To add the Go client to your project, import it like any other module:

go get github.com/lucenia/lucenia-go

Connecting to Lucenia

To connect to the default Lucenia host, create a client object with the address https://localhost:9200 if you are using the Security plugin:

client, err := lucenia.NewClient(lucenia.Config{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
        Addresses: []string{"https://localhost:9200"},
        Username:  "admin", // For testing only. Don't store credentials in code.
        Password:  "admin",
    })

If you are not using the Security plugin, create a client object with the address http://localhost:9200:

client, err := lucenia.NewClient(lucenia.Config{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
        Addresses: []string{"http://localhost:9200"},
    })

Connecting to Lucenia Service Hosted on AWS

The following example illustrates connecting to Lucenia Service hosted on AWS:

package main

import (
	"context"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	lucenia "github.com/lucenia/lucenia-go/v2"
	luceniaapi "github.com/lucenia/lucenia-go/v2/luceniaapi"
	requestsigner "github.com/lucenia/lucenia-go/v2/signer/awsv2"
)

const endpoint = "" // e.g. https://lucenia-domain.region.com

func main() {
	ctx := context.Background()

	awsCfg, err := config.LoadDefaultConfig(ctx,
		config.WithRegion("<AWS_REGION>"),
		config.WithCredentialsProvider(
			getCredentialProvider("<AWS_ACCESS_KEY>", "<AWS_SECRET_ACCESS_KEY>", "<AWS_SESSION_TOKEN>"),
		),
	)
	if err != nil {
		log.Fatal(err) // Do not log.fatal in a production ready app.
	}

	// Create an AWS request Signer and load AWS configuration using default config folder or env vars.
	signer, err := requestsigner.NewSignerWithService(awsCfg, "es")
	if err != nil {
		log.Fatal(err) // Do not log.fatal in a production ready app.
	}

	// Create a Lucenia client and use the request-signer
	client, err := lucenia.NewClient(lucenia.Config{
		Addresses: []string{endpoint},
		Signer:    signer,
	})
	if err != nil {
		log.Fatal("client creation err", err)
	}
}

func getCredentialProvider(accessKey, secretAccessKey, token string) aws.CredentialsProviderFunc {
	return func(ctx context.Context) (aws.Credentials, error) {
		c := &aws.Credentials{
			AccessKeyID:     accessKey,
			SecretAccessKey: secretAccessKey,
			SessionToken:    token,
		}
		return *c, nil
	}
}

The Go client constructor takes an lucenia.Config{} type, which can be customized using options such as a list of Lucenia node addresses or a username and password combination.

To connect to multiple Lucenia nodes, specify them in the Addresses parameter:

var (
    urls = []string{"http://localhost:9200", "http://localhost:9201", "http://localhost:9202"}
)

client, err := lucenia.NewClient(lucenia.Config{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
        Addresses: urls,
})

The Go client retries requests for a maximum of three times by default. To customize the number of retries, set the MaxRetries parameter. Additionally, you can change the list of response codes for which a request is retried by setting the RetryOnStatus parameter. The following code snippet creates a new Go client with custom MaxRetries and RetryOnStatus values:

client, err := lucenia.NewClient(lucenia.Config{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
        Addresses: []string{"http://localhost:9200"},
        MaxRetries: 5,
        RetryOnStatus: []int{502, 503, 504},
    })

Creating an index

To create an Lucenia index, use the IndicesCreateRequest method. You can use the following code to construct a JSON object with custom settings :

settings := strings.NewReader(`{
    'settings': {
        'index': {
            'number_of_shards': 1,
            'number_of_replicas': 0
            }
        }
    }`)

res := luceniaapi.IndicesCreateRequest{
    Index: "go-test-index1", 
    Body:  settings,
}

Indexing a document

You can index a document into Lucenia using the IndexRequest method:

document := strings.NewReader(`{
    "title": "Moneyball",
    "director": "Bennett Miller",
    "year": "2011"
}`)

docId := "1"
req := luceniaapi.IndexRequest{
    Index:      "go-test-index1",
    DocumentID: docId,
    Body:       document,
}
insertResponse, err := req.Do(context.Background(), client)

Performing bulk operations

You can perform several operations at the same time by using the Bulk method of the client. The operations may be of the same type or of different types.

blk, err := client.Bulk(
		strings.NewReader(`
    { "index" : { "_index" : "go-test-index1", "_id" : "2" } }
    { "title" : "Interstellar", "director" : "Christopher Nolan", "year" : "2014"}
    { "create" : { "_index" : "go-test-index1", "_id" : "3" } }
    { "title" : "Star Trek Beyond", "director" : "Justin Lin", "year" : "2015"}
    { "update" : {"_id" : "3", "_index" : "go-test-index1" } }
    { "doc" : {"year" : "2016"} }
`),
	)

Searching for documents

The easiest way to search for documents is to construct a query string. The following code uses a multi_match query to search for “miller” in the title and director fields. It boosts the documents where “miller” appears in the title field:

content := strings.NewReader(`{
    "size": 5,
    "query": {
        "multi_match": {
        "query": "miller",
        "fields": ["title^2", "director"]
        }
    }
}`)

search := luceniaapi.SearchRequest{
    Index: []string{"go-test-index1"},
    Body: content,
}

searchResponse, err := search.Do(context.Background(), client)

Deleting a document

You can delete a document using the DeleteRequest method:

delete := luceniaapi.DeleteRequest{
    Index:      "go-test-index1",
    DocumentID: "1",
}

deleteResponse, err := delete.Do(context.Background(), client)

Deleting an index

You can delete an index using the IndicesDeleteRequest method:

deleteIndex := luceniaapi.IndicesDeleteRequest{
    Index: []string{"go-test-index1"},
}

deleteIndexResponse, err := deleteIndex.Do(context.Background(), client)

Sample program

The following sample program creates a client, adds an index with non-default settings, inserts a document, performs bulk operations, searches for the document, deletes the document, and then deletes the index:

package main
import (
    "os"
    "context"
    "crypto/tls"
    "fmt"
    lucenia "github.com/lucenia/lucenia-go"
    luceniaapi "github.com/lucenia/lucenia-go/luceniaapi"
    "net/http"
    "strings"
)
const IndexName = "go-test-index1"
func main() {
    // Initialize the client with SSL/TLS enabled.
    client, err := lucenia.NewClient(lucenia.Config{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
        Addresses: []string{"https://localhost:9200"},
        Username:  "admin", // For testing only. Don't store credentials in code.
        Password:  "admin",
    })
    if err != nil {
        fmt.Println("cannot initialize", err)
        os.Exit(1)
    }

    // Print Lucenia version information on console.
    fmt.Println(client.Info())

    // Define index settings.
    settings := strings.NewReader(`{
     'settings': {
       'index': {
            'number_of_shards': 1,
            'number_of_replicas': 2
            }
          }
     }`)

    // Create an index with non-default settings.
    res := luceniaapi.IndicesCreateRequest{
        Index: IndexName, 
        Body:  settings,
    }
    fmt.Println("Creating index")
    fmt.Println(res)

    // Add a document to the index.
    document := strings.NewReader(`{
        "title": "Moneyball",
        "director": "Bennett Miller",
        "year": "2011"
    }`)

    docId := "1"
    req := luceniaapi.IndexRequest{
        Index:      IndexName,
        DocumentID: docId,
        Body:       document,
    }
    insertResponse, err := req.Do(context.Background(), client)
    if err != nil {
        fmt.Println("failed to insert document ", err)
        os.Exit(1)
    }
    fmt.Println("Inserting a document")
    fmt.Println(insertResponse)
    defer insertResponse.Body.Close()
   
    // Perform bulk operations.
    blk, err := client.Bulk(
		strings.NewReader(`
    { "index" : { "_index" : "go-test-index1", "_id" : "2" } }
    { "title" : "Interstellar", "director" : "Christopher Nolan", "year" : "2014"}
    { "create" : { "_index" : "go-test-index1", "_id" : "3" } }
    { "title" : "Star Trek Beyond", "director" : "Justin Lin", "year" : "2015"}
    { "update" : {"_id" : "3", "_index" : "go-test-index1" } }
    { "doc" : {"year" : "2016"} }
`),
	)

    if err != nil {
        fmt.Println("failed to perform bulk operations", err)
        os.Exit(1)
    }
    fmt.Println("Performing bulk operations")
    fmt.Println(blk)

    // Search for the document.
    content := strings.NewReader(`{
       "size": 5,
       "query": {
           "multi_match": {
           "query": "miller",
           "fields": ["title^2", "director"]
           }
      }
    }`)

    search := luceniaapi.SearchRequest{
        Index: []string{IndexName},
        Body: content,
    }

    searchResponse, err := search.Do(context.Background(), client)
    if err != nil {
        fmt.Println("failed to search document ", err)
        os.Exit(1)
    }
    fmt.Println("Searching for a document")
    fmt.Println(searchResponse)
    defer searchResponse.Body.Close()

    // Delete the document.
    delete := luceniaapi.DeleteRequest{
        Index:      IndexName,
        DocumentID: docId,
    }

    deleteResponse, err := delete.Do(context.Background(), client)
    if err != nil {
        fmt.Println("failed to delete document ", err)
        os.Exit(1)
    }
    fmt.Println("Deleting a document")
    fmt.Println(deleteResponse)
    defer deleteResponse.Body.Close()

    // Delete the previously created index.
    deleteIndex := luceniaapi.IndicesDeleteRequest{
        Index: []string{IndexName},
    }

    deleteIndexResponse, err := deleteIndex.Do(context.Background(), client)
    if err != nil {
        fmt.Println("failed to delete index ", err)
        os.Exit(1)
    }
    fmt.Println("Deleting the index")
    fmt.Println(deleteIndexResponse)
    defer deleteIndexResponse.Body.Close()
}