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. PreviouslyClient.Batch
Client.MultipleGetObjects
: retrieve objects by objectID across different indicesIndex.BrowseObjects
: produce an iterator over all objects matching the query parametersIndex.BrowseRules
: produce an iterator over all rules matching the query parametersIndex.BrowseSynonyms
: produce an iterator over all synonyms matching the query parameters
Removed methods
*
: replace withopt.ExtraHeader(...)
oropt.ExtraURLParam(...)
as functional optionsAnalyticsClient.WaitTask
: replace by invoking response objects’.Wait
method directly (see the Waitable responses section for more information)Client.AddUserKey
: replace withClient.AddAPIKey
Client.Batch
: replace withClient.MultipleBatch
Client.ClearIndex
: replace withIndex.Clear
Client.DeleteIndex
: replace withIndex.Delete
Client.DeleteUserKey
: replace withClient.DeleteAPIKey
Client.GetStatus
: replace withIndex.GetStatus
Client.GetUserKey
: replace withClient.GetAPIKey
Client.InitAnalytics
: replace withanalytics.NewClient
Client.InitInsights
: replace withinsights.NewClient
Client.ListKeys
: replace withClient.ListAPIKeys
Client.SetAnalyticsTimeout
: replace withcontext.WithTimeout
as a functional optionClient.SetExtraHeader
: replace withopt.ExtraHeader
as a functional optionClient.SetHTTPClient
: replace with properRequester
insearch.NewClientWithConfig
Client.SetMaxIdleConnsPerHosts
: replace with properRequester
insearch.NewClientWithConfig
Client.SetReadTimeout
: replace withcontext.WithTimeout
as a functional optionClient.SetTimeout
: replace withcontext.WithTimeout
as a functional optionClient.SetWriteTimeout
: replace withcontext.WithTimeout
as a functional optionClient.UpdateUserKey
: replace withClient.AddAPIKey
Client.WaitTask
: replace by invoking response objects’.Wait
method directly (see the Waitable responses section for more information)Index.AddAPIKey
: replace withClient.AddAPIKey
Index.AddObject
: replace withIndex.SaveObject
Index.AddObject
: replace withIndex.SaveObject
Index.AddObjects
: replace withIndex.SaveObjects(..., opt.AutoGenerateObjectIDIfNotExist(true))
Index.AddSynonym
: replace withIndex.SaveSynonym
Index.AddUserKey
: replace withClient.AddAPIKey
Index.BatchRules
: replace withIndex.SaveRules
Index.BatchSynonyms
: replace withIndex.SaveSynonyms
Index.BrowseAll
: replace withIndex.BrowseObjects
Index.Browse
: replace withIndex.BrowseObjects
Index.Copy
: replace withClient.Copy
Index.DeleteAPIKey
: replace withClient.DeleteAPIKey
Index.DeleteByQuery
: replace withIndex.DeleteBy
Index.DeleteUserKey
: replace withClient.DeleteAPIKey
Index.GetAPIKey
: replace withClient.GetAPIKey
Index.GetObjectsAttrs
: replace withIndex.GetObjects(..., opt.AttributesToRetrieve(...))
Index.GetUserKey
: replace withClient.GetAPIKey
Index.ListKeys
: replace withClient.ListAPIKeys
Index.MoveTo
: replace withClient.Move
Index.Move
: replace withClient.Move
Index.PartialUpdateObjectNoCreate
: replace withIndex.PartialUpdateObject(..., opt.CreateIfNotExists(false))
Index.PartialUpdateObjectsNoCreate
: replace withIndex.PartialUpdateObjects(..., opt.CreateIfNotExists(false))
Index.ScopedCopy
: replace withClient.Copy(..., opt.Scopes(...))
Index.SearchFacet
: replace withIndex.SearchForFacetValues
Index.UpdateAPIKey
: replace withClient.UpdateAPIKey
Index.UpdateObject
: replace withIndex.SaveObject
Index.UpdateObjects
: replace withIndex.SaveObjects
Index.UpdateUserKey
: replace withClient.UpdateAPIKey