API Reference / API Methods / Upgrade guides for the API client

Upgrade guides for the API client

Upgrade from v2 to v3

The design of the latest version of the Go client is almost the same as the earlier version to make upgrading as smooth as possible.

This new version is compatible with the same Go versions as before (from 1.8 up to the most recent Go version).

Dependency upgrade

Because the package structure of the Go API client has changed between the v2 and v3, you can’t change the dependency version without updating your code. The major change is the removal of the algoliasearch/ sub-package in favor of algolia/search/ one. Hence, the first step before upgrading the package version is to replace all algoliasearch/ imports and algoliasearch. package prefix with their algolia/search/ and search. counterparts.

Since the introduction of versioned modules in Go 1.11, Go retrieves dependencies automatically every time you run go build or go test.

If you would like to import a specific version of the Go client, use the following:

$
go get github.com/algolia/algoliasearch-client-go/v3@v3.Y.Z

It’s recommended to migrate your project to use Go modules. If you’re still using dep as a dependency manager you can update the package as usual:

1
2
3
4
5
6
7
8
# First change the `version` field of the
# `github.com/algolia/algoliasearch-client-go` constraint of your `Gopkg.toml`
# file to `3.X.Y` with your editor of choice.
vim Gopkg.toml

# Then run `dep` as follows to automatically update the `Gopkg.lock` file with
# the most recent minor version of the Algolia Go client.
dep ensure

Client instantiation

Search client instantiation

Replace the instantiation of the search client as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Before
client := algoliasearch.NewClient("YourApplicationID", "YourAPIKey")
index := client.InitIndex("your_index_name")

// After
client := search.NewClient("YourApplicationID", "YourAPIKey")
index := client.InitIndex("your_index_name")

// Using configuration
client := search.NewClientWithConfig(search.Configuration{
    AppID: "YourApplicationID", // Mandatory
    APIKey: "YourAPIKey", // Mandatory
    Hosts: []string{ ... },            // Optional
    Requester: customRequester,        // Optional
    ReadTimeout: 5 * time.Second,      // Optional
    WriteTimeout: 30 * time.Second,    // Optional
    Headers: map[string]string{ ... }, // Optional
})

Analytics client instantiation

Replace the instantiation of the analytics client as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Before
client := algoliasearch.NewClient("YourApplicationID", "YourAPIKey").InitAnalytics()

// After
client := analytics.NewClient("YourApplicationID", "YourAPIKey")

// Using configuration
client := analytics.NewClientWithConfig(search.Configuration{
    AppID: "YourApplicationID",        // Mandatory
    APIKey: "YourAPIKey",              // Mandatory
    Hosts: []string{ ... },            // Optional
    Requester: customRequester,        // Optional
    ReadTimeout: 5 * time.Second,      // Optional
    WriteTimeout: 30 * time.Second,    // Optional
    Region: region.US                  // Optional
    Headers: map[string]string{ ... }, // Optional
})

Insights client instantiation

Replace the instantiation of the analytics client as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Before
client := algoliasearch.NewClient("YourApplicationID", "YourAPIKey").InitInsights()

// After
client := insights.NewClient("YourApplicationID", "YourAPIKey")
userClient := client.User("UserToken")

// Using configuration
client := insights.NewClientWithConfig(search.Configuration{
    AppID: "YourApplicationID",        // Mandatory
    APIKey: "YourAPIKey",              // Mandatory
    Hosts: []string{ ... },            // Optional
    Requester: customRequester,        // Optional
    ReadTimeout: 5 * time.Second,      // Optional
    WriteTimeout: 30 * time.Second,    // Optional
    Region: region.US                  // Optional
    Headers: map[string]string{ ... }, // Optional
})

Functional options as optional parameters

One of the biggest change is the addition of functional options, as coined by Dave Cheney to ease the manipulation of Algolia parameters.

For instance, when searching through some index records, instead of passing parameters via a custom algoliasearch.Map, you can now use the following syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Before
index.Search(
    "query",
    algoliasearch.Map{
        "attributesToRetrieve": []string{"objectID", "title"},
        "hitsPerPage": 3,
    },
})

// After
index.Search("query",
    opt.AttributesToRetrieve("objectID", "title"),
    opt.HitsPerPage(3),
)

This gives the advantage of better discoverability and improved type-safety, as all options are now typed.

algoliasearch.Object and algoliasearch.Map removed

The tedious part of migrating from v2 to v3 is to remove all algoliasearch.Map and algoliasearch.Object references. Those objects are aliases on top of map[string]interface{}, which handled known types internally. From a developer perspective, this wasn’t great. You had to transform all user structures into those custom types.

Since this v3, user defined structures are now first-class citizens.

As an example, take this before/after snippet of code explaining how to save an object to an Algolia index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Book struct {
	Title    string `json:"title"`
	Year     string `json:"year"`
	ObjectID string `json:"objectID"`
}

book := Book{
	Title:    "It",
	Year:     1986,
	ObjectID: "12345",
}

// Before
object := algoliasearch.Object{
	"title":    book.Title,
	"year":     book.Year,
	"objectID": book.ObjectID,
}
index.AddObject(object)

// After
index.SaveObject(book)

Now, because objects are user-defined structures, the Go API client can’t return them from functions, due to the lack of generics. The approach taken is the same as the json.Unmarshal function from the standard library: any function which may return a user-defined object expects an extra parameter passed as a reference.

Again, as an example, take this before/after snippet of code explaining how to retrieve a deserialized object from an Algolia index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Book struct {
	Title    string `json:"title"`
	Year     string `json:"year"`
	ObjectID string `json:"objectID"`
}

// Before:
// the returned object is an `algoliasearch.Object`.
obj, err := index.GetObject(book.ObjectID)
fmt.Println(obj["title"])

// After:
// the object contained in the response payload is unmarshalled directly into
// the given reference, here an instance of a `Book` structure
var b Book
err := index.GetObject(book.ObjectID, &b)
fmt.Println(b.Title)

Note that you can actually use Go maps and any serializable object.

Setting and getting Settings

Using the v2, handling settings wasn’t convenient. This difficulty was mainly due to GetSettings returning a Settings structure whereas SetSettings was expecting an algoliasearch.Map (alias for map[string]interface{}).

Because of this, a Settings.ToMap function let users get a algoliasearch.Map, which SetSettings uses. This algoliasearch.Map couldn’t transform into Settings without lots of work. Also, it’s hard to compare both objects against each other.

The v3 gets rid of the algoliasearch.Map representation for the settings, which lets you now change GetSettings and SetSettings as such:

1
2
3
4
5
6
7
8
9
10
// Before
settings, err := index.GetSettings()
settingsAsMap := settings.ToMap()
settingsAsMap["replicas"] = []string{"replica1", "replica2"}
index.SetSettings(settingsAsMap)

// After
settings, err := index.GetSettings()
settings.Replicas = opt.Replicas("replica1", "replica2")
index.SetSettings(settings)

Waitable responses

To wait for indexing operation to complete, you had to explicitly call index.WaitTask() with the appropriate response’s TaskID field. You had to know about the existence of the TaskID field and to learn how it works with index.WaitTask.

More importantly, it was difficult to wait concurrently on the different tasks.

Most of other methods weren’t waitable. For instance, there was no solution to wait for the completion of key or analytics-related operations.

Since v3, all response objects triggering asynchronous operations on Algolia’ side now have a Wait() method. Each method hides its own logic on how to wait for its own underlying operation to complete.

To address the last issue, being able to wait on multiple tasks concurrently, v3 introduces a new object: wait.Group. You can use it as follows:

1
2
3
4
5
6
7
8
9
10
11
12
g := wait.Group()

res1, err := index.SaveObjects(...)
g.Collect(res1)

res2, err := index.SetSettings(...)
res3, err := index.SaveRules(...)
g.Collect(res2, res3)

// `g.Wait()` call will block until both the `SaveObjects`, `SetSettings` and
// `SaveRules` operations terminates.
err = g.Wait()

Whenever operations are close to each other, wait.Wait(...) is also provided as a shortcut.

1
2
3
4
5
res1, err := index.SaveObjects(...)
res2, err := index.SetSettings(...)
res3, err := index.SaveRules(...)

err = wait.Wait(res1, res2, res3)

New debug package

This release also replaces the debug approach, which was using ALGOLIA_DEBUG environment variable values to control global level of debug messages.

Since v3, you can guard specific places of the code by debug.Enable()/debug.Disable() calls, to pretty-print raw JSON request and response payloads and other specific debug information to the user. You can use it as follows:

1
2
3
debug.Enable()
res, err := index.SaveObject(map[string]string{"objectID": "one"})
debug.Disable()

Which would print on the standard output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> ALGOLIA DEBUG request:
        method="POST"
        url="https://YourApplicationID.algolia.net/1/indexes/test"
        body=
        {
          "objectID": "one"
        }
> ALGOLIA DEBUG response:
        body=
        {
          "createdAt": "2019-04-12T15:57:21.669Z",
          "taskID": 11054870692,
          "objectID": "one"
        }

New methods

  • Client.MultipleBatch: perform batches across different indices. Previously Client.Batch
  • Client.MultipleGetObjects: retrieve objects by objectID across different indices
  • Index.BrowseObjects: produce an iterator over all objects matching the query parameters
  • Index.BrowseRules: produce an iterator over all rules matching the query parameters
  • Index.BrowseSynonyms: produce an iterator over all synonyms matching the query parameters

Removed methods

  • *: replace with opt.ExtraHeader(...) or opt.ExtraURLParam(...) as functional options
  • AnalyticsClient.WaitTask: replace by invoking response objects’ .Wait method directly (see the Waitable responses section for more information)
  • Client.AddUserKey: replace with Client.AddAPIKey
  • Client.Batch: replace with Client.MultipleBatch
  • Client.ClearIndex: replace with Index.Clear
  • Client.DeleteIndex: replace with Index.Delete
  • Client.DeleteUserKey: replace with Client.DeleteAPIKey
  • Client.GetStatus: replace with Index.GetStatus
  • Client.GetUserKey: replace with Client.GetAPIKey
  • Client.InitAnalytics: replace with analytics.NewClient
  • Client.InitInsights: replace with insights.NewClient
  • Client.ListKeys: replace with Client.ListAPIKeys
  • Client.SetAnalyticsTimeout: replace with context.WithTimeout as a functional option
  • Client.SetExtraHeader: replace with opt.ExtraHeader as a functional option
  • Client.SetHTTPClient: replace with proper Requester in search.NewClientWithConfig
  • Client.SetMaxIdleConnsPerHosts: replace with proper Requester in search.NewClientWithConfig
  • Client.SetReadTimeout: replace with context.WithTimeout as a functional option
  • Client.SetTimeout: replace with context.WithTimeout as a functional option
  • Client.SetWriteTimeout: replace with context.WithTimeout as a functional option
  • Client.UpdateUserKey: replace with Client.AddAPIKey
  • Client.WaitTask: replace by invoking response objects’ .Wait method directly (see the Waitable responses section for more information)
  • Index.AddAPIKey: replace with Client.AddAPIKey
  • Index.AddObject: replace with Index.SaveObject
  • Index.AddObject: replace with Index.SaveObject
  • Index.AddObjects: replace with Index.SaveObjects(..., opt.AutoGenerateObjectIDIfNotExist(true))
  • Index.AddSynonym: replace with Index.SaveSynonym
  • Index.AddUserKey: replace with Client.AddAPIKey
  • Index.BatchRules: replace with Index.SaveRules
  • Index.BatchSynonyms: replace with Index.SaveSynonyms
  • Index.BrowseAll: replace with Index.BrowseObjects
  • Index.Browse: replace with Index.BrowseObjects
  • Index.Copy: replace with Client.Copy
  • Index.DeleteAPIKey: replace with Client.DeleteAPIKey
  • Index.DeleteByQuery: replace with Index.DeleteBy
  • Index.DeleteUserKey: replace with Client.DeleteAPIKey
  • Index.GetAPIKey: replace with Client.GetAPIKey
  • Index.GetObjectsAttrs: replace with Index.GetObjects(..., opt.AttributesToRetrieve(...))
  • Index.GetUserKey: replace with Client.GetAPIKey
  • Index.ListKeys: replace with Client.ListAPIKeys
  • Index.MoveTo: replace with Client.Move
  • Index.Move: replace with Client.Move
  • Index.PartialUpdateObjectNoCreate: replace with Index.PartialUpdateObject(..., opt.CreateIfNotExists(false))
  • Index.PartialUpdateObjectsNoCreate: replace with Index.PartialUpdateObjects(..., opt.CreateIfNotExists(false))
  • Index.ScopedCopy: replace with Client.Copy(..., opt.Scopes(...))
  • Index.SearchFacet: replace with Index.SearchForFacetValues
  • Index.UpdateAPIKey: replace with Client.UpdateAPIKey
  • Index.UpdateObject: replace with Index.SaveObject
  • Index.UpdateObjects: replace with Index.SaveObjects
  • Index.UpdateUserKey: replace with Client.UpdateAPIKey

Did you find this page helpful?