Skip to content

Commit

Permalink
added: Implement AccountClient.CopyIndex
Browse files Browse the repository at this point in the history
  • Loading branch information
aseure committed Nov 27, 2018
1 parent ddc2dbd commit ebe51f5
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 0 deletions.
135 changes: 135 additions & 0 deletions algoliasearch/account_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package algoliasearch

import "fmt"

type accountClient struct{}

func NewAccountClient() AccountClient {
return &accountClient{}
}

func (a *accountClient) CopyIndex(src, dst Index) ([]int, error) {
return a.CopyIndexWithRequestOptions(src, dst, nil)
}

func (a *accountClient) CopyIndexWithRequestOptions(src, dst Index, opts *RequestOptions) ([]int, error) {
if src.GetAppID() == dst.GetAppID() {
return nil, SameAppIDErr
}

if _, err := dst.GetSettingsWithRequestOptions(opts); err == nil {
return nil, IndexAlreadyExistsErr
}

var taskIDs []int

// Copy synonyms
{
it := NewSynonymIterator(src)

var synonyms []Synonym

for {
synonym, err := it.Next()
if err != nil {
if err == NoMoreSynonymsErr {
break
} else {
return nil, fmt.Errorf("error while iterating source index synonyms: %v", err)
}
}
synonyms = append(synonyms, *synonym)
}

if synonyms != nil {
res, err := dst.ReplaceAllSynonymsWithRequestOptions(synonyms, opts)
if err != nil {
return nil, fmt.Errorf("error while replacing destination index synonyms: %v", err)
}
taskIDs = append(taskIDs, res.TaskID)
}
}

// Copy rules
{
it := NewRuleIterator(src)

var rules []Rule

for {
rule, err := it.Next()
if err != nil {
if err == NoMoreRulesErr {
break
} else {
return nil, fmt.Errorf("error while iterating source index rules: %v", err)
}
}
rules = append(rules, *rule)
}
if rules != nil {
res, err := dst.ReplaceAllRulesWithRequestOptions(rules, opts)
if err != nil {
return nil, fmt.Errorf("error while replacing destination index rules: %v", err)
}
taskIDs = append(taskIDs, res.TaskID)
}
}

// Copy settings
{
settings, err := src.GetSettingsWithRequestOptions(opts)
if err != nil {
return nil, fmt.Errorf("cannot retrieve source index settings: %v", err)
}

res, err := dst.SetSettingsWithRequestOptions(settings.ToMap(), opts)
if err != nil {
return nil, fmt.Errorf("cannot set destination index settings: %v", err)
}
taskIDs = append(taskIDs, res.TaskID)

}

// Copy objects
{
it, err := src.BrowseAllWithRequestOptions(nil, opts)
if err != nil {
return nil, fmt.Errorf("cannot browse source index objects: %v", err)
}

var objects []Object
batchSize := 1000

for {
res, err := it.Next()
if err != nil {
if err == NoMoreHitsErr {
break
} else {
return nil, fmt.Errorf("error while iterating source index objects: %v", err)
}
}
objects = append(objects, Object(res))

if len(objects) >= batchSize {
res, err := dst.AddObjectsWithRequestOptions(objects, opts)
if err != nil {
return nil, fmt.Errorf("error while saving batch of objects: %v", err)
}
taskIDs = append(taskIDs, res.TaskID)
objects = []Object{}
}
}

// Send the last batch
res, err := dst.AddObjectsWithRequestOptions(objects, opts)
if err != nil {
return nil, fmt.Errorf("error while saving batch of objects: %v", err)
}
taskIDs = append(taskIDs, res.TaskID)
objects = []Object{}
}

return taskIDs, nil
}
93 changes: 93 additions & 0 deletions algoliasearch/account_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package algoliasearch

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestAccountClient(t *testing.T) {
client1, index1 := initClientAndIndex(t, "TestAccountClient")

index2 := client1.InitIndex("go-TestAccountClient")
{
account := NewAccountClient()
_, err := account.CopyIndex(index1, index2)
require.Equal(t, SameAppIDErr, err)
}

client2 := initClient2(t)
index2 = client2.InitIndex("go-TestAccountClient")

{
_, err := index2.Delete()
require.NoError(t, err)
}

var taskIDs []int

{
res, err := index1.AddObject(Object{"objectID": "one"})
require.NoError(t, err)
taskIDs = append(taskIDs, res.TaskID)
}

{
res, err := index1.SaveRule(Rule{
ObjectID: "one",
Condition: NewSimpleRuleCondition(Contains, "pattern"),
Consequence: RuleConsequence{
Params: Map{
"query": Map{
"edits": []Edit{DeleteEdit("pattern")},
},
},
},
}, false)
require.NoError(t, err)
taskIDs = append(taskIDs, res.TaskID)
}

{
res, err := index1.SaveSynonym(NewSynonym("one", []string{"one", "two"}), false)
require.NoError(t, err)
taskIDs = append(taskIDs, res.TaskID)
}

{
res, err := index1.SetSettings(Map{"searchableAttributes": []string{"objectID"}})
require.NoError(t, err)
taskIDs = append(taskIDs, res.TaskID)
}

waitTasksAsync(t, index1, taskIDs)
taskIDs = []int{}

{
account := NewAccountClient()
taskIDs, err := account.CopyIndex(index1, index2)
require.NoError(t, err)
waitTasksAsync(t, index2, taskIDs)
}

{
_, err := index2.GetObject("one", nil)
require.NoError(t, err)

_, err = index2.GetRule("one")
require.NoError(t, err)

_, err = index2.GetSynonym("one")
require.NoError(t, err)

settings, err := index2.GetSettings()
require.NoError(t, err)
require.Equal(t, []string{"objectID"}, settings.SearchableAttributes)
}

{
account := NewAccountClient()
_, err := account.CopyIndex(index1, index2)
require.Equal(t, IndexAlreadyExistsErr, err)
}
}
11 changes: 11 additions & 0 deletions algoliasearch/algoliasearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -883,3 +883,14 @@ type Analytics interface {
// goes wrong or if the task did not succeed, a non-nil error is returned.
WaitTask(task ABTestTaskRes) (err error)
}

// AccountClient is responsible for handling cross-application operations.
type AccountClient interface {
// CopyIndex copies the content of the entire source index to the destination index. Indices from the same
// application cannot be copied. To do so, use Client.CopyIndex instead.
CopyIndex(src, dst Index) (taskIDs []int, err error)

// CopyIndexWithRequestOptions is the same as CopyIndex but it also
// accepts extra RequestOptions.
CopyIndexWithRequestOptions(src, dst Index, opts *RequestOptions) (taskIDs []int, err error)
}
2 changes: 2 additions & 0 deletions algoliasearch/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ var (
NoMoreSynonymsErr error = errors.New("No more synonyms")
NoMoreRulesErr error = errors.New("No more rules")
ExhaustionOfTryableHostsErr error = errors.New("All hosts have been contacted unsuccessfully")
SameAppIDErr error = errors.New("Indices cannot target the same application ID. Please use Client.CopyIndex for same-app index copy instead.")
IndexAlreadyExistsErr error = errors.New("Destination index already exists. Please delete it first as the CopyIndex cannot hold the responsibility of modifying the destination index.")
)

// NetError is used internally to differente regular error from errors
Expand Down

0 comments on commit ebe51f5

Please sign in to comment.