Repository: algolia/algoliasearch-client-go
Pull request #500 (2019-06-25 08:36:35)

perf: benchmarks for getObjectID/hasObjectID functions
**Summary**

As suggested by @pavel-popov on Github in
https://github.com/algolia/algoliasearch-client-go/pull/482
we should reconsider the implementation of `hasObjectID`/`getObjectID`
functions as the first implementation most probably behaves very poorly
in term of performances.

This commit contains 3 different implementations with their associated
tests and benchmarks to help us take a decision.

**Proposed implementations**

1. Marshaling-based implementation (original implemantion)

The original implementation relies on calls to
`json.Marshal`/`json.Unmarshal` to detect which fields is serialized to
`objectID` once encoded into JSON by the standard library. This method
has the advantage of respecting any custom `json.MarshalJSON` function
the user would have provided. However, we expect it to have poor
performances, each object needing to be encoded/decoded.

2. Interface-based implementation

As suggested by @pavel-popov in the previously mentioned Github PR, we
could ask users to implement a specific interface, such as:

```go
type ObjectIDProvider interface {
    ObjectID() string
}
```

This would ease the implementation of `getObjectID` where we would only
need an explicit cast from the `interface{}` to this custom interface
and a call to the `ObjectID()` method to retrieve the objectID. The
disadvantage of this method being that users would need to add
Algolia-related code (implementation of our interface) to improve
internal performances of our library.

3. Reflection-based implementation

The last implementation we suggest is a reflection-based one where the
objectID is retrieved from the `objectID` JSON tag, if any. By using
reflection, we would be able to inspect each object at runtime without
the need of Algolia-related code into the user-code (the use of JSON
tags is very Go idiomatic).

**Results**

```
BenchmarkHasObjectID/hasObjectID-8                300000      4791 ns/op
BenchmarkHasObjectID/hasObjectIDWithProvider-8  20000000      78.8 ns/op
BenchmarkHasObjectID/hasObjectIDWithReflect-8    5000000       316 ns/op
```

Results clearly shows that the interface-based implementation is the
fastest whereas the original implementation is confirmed to be the
slowest one (with a 61x slowdown factor).

Also, the reflection-based implementation is only 4x slower than the
interface-based one, while providing a 15x speedup compared our base
implementation.

**Suggestion**

Following what the results showed us, and as an effort to keep the
backward-compatibility with previous versions, I'd recommend to use the
reflection-based implementation as a first-intent in `getObjectID`
function to retrieve the value of the `objectID` JSON tag and then
fallback to the original implementation if it has not been found. This
way, we ensure that our implementation will continue to work with users
who implemented `json.MarshalJSON` on their Go structures while those
who directly used JSON tags will get the performance speedup for free
(i.e. without any implementation of Algolia-related interfaces).


Patch:
diff --git a/algolia/internal/gen/options.go b/algolia/internal/gen/options.go
index 8edb0af2..fb249087 100644
--- a/algolia/internal/gen/options.go
+++ b/algolia/internal/gen/options.go
@@ -102,6 +102,7 @@ var options = []Option{
 	{"exactOnSingleWordQuery", Settings | Search, "attribute", ""},
 	{"alternativesAsExact", Settings | Search, []string{"ignorePlurals", "singleWordSynonym"}, ""},
 	{"advancedSyntaxFeatures", Settings | Search, []string{"exactPhrase", "excludeWords"}, ""},
+	{"similarQuery", Search, "", ""},
 
 	// Query rules
 	{"enableRules", Settings | Search, true, ""},
@@ -132,6 +133,7 @@ var options = []Option{
 	{"percentileComputation", Search, true, ""},
 	{"explain", Search, []string{}, ""},
 	{"advanced", Settings, 0, ""},
+	{"attributeCriteriaComputedByMinProximity", Settings | Search, false, ""},
 
 	// Secured API key
 	{"restrictIndices", Other, []string{}, ""},
diff --git a/algolia/internal/opt/attribute_criteria_computed_by_min_proximity.go b/algolia/internal/opt/attribute_criteria_computed_by_min_proximity.go
new file mode 100644
index 00000000..6a6270c1
--- /dev/null
+++ b/algolia/internal/opt/attribute_criteria_computed_by_min_proximity.go
@@ -0,0 +1,18 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import (
+	"github.com/algolia/algoliasearch-client-go/algolia/opt"
+)
+
+// ExtractAttributeCriteriaComputedByMinProximity returns the first found AttributeCriteriaComputedByMinProximityOption from the
+// given variadic arguments or nil otherwise.
+func ExtractAttributeCriteriaComputedByMinProximity(opts ...interface{}) *opt.AttributeCriteriaComputedByMinProximityOption {
+	for _, o := range opts {
+		if v, ok := o.(*opt.AttributeCriteriaComputedByMinProximityOption); ok {
+			return v
+		}
+	}
+	return nil
+}
diff --git a/algolia/internal/opt/similar_query.go b/algolia/internal/opt/similar_query.go
new file mode 100644
index 00000000..17401af9
--- /dev/null
+++ b/algolia/internal/opt/similar_query.go
@@ -0,0 +1,18 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import (
+	"github.com/algolia/algoliasearch-client-go/algolia/opt"
+)
+
+// ExtractSimilarQuery returns the first found SimilarQueryOption from the
+// given variadic arguments or nil otherwise.
+func ExtractSimilarQuery(opts ...interface{}) *opt.SimilarQueryOption {
+	for _, o := range opts {
+		if v, ok := o.(*opt.SimilarQueryOption); ok {
+			return v
+		}
+	}
+	return nil
+}
diff --git a/algolia/opt/attribute_criteria_computed_by_min_proximity.go b/algolia/opt/attribute_criteria_computed_by_min_proximity.go
new file mode 100644
index 00000000..93682f86
--- /dev/null
+++ b/algolia/opt/attribute_criteria_computed_by_min_proximity.go
@@ -0,0 +1,63 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import "encoding/json"
+
+// AttributeCriteriaComputedByMinProximityOption is a wrapper for an AttributeCriteriaComputedByMinProximity option parameter. It holds
+// the actual value of the option that can be accessed by calling Get.
+type AttributeCriteriaComputedByMinProximityOption struct {
+	value bool
+}
+
+// AttributeCriteriaComputedByMinProximity wraps the given value into a AttributeCriteriaComputedByMinProximityOption.
+func AttributeCriteriaComputedByMinProximity(v bool) *AttributeCriteriaComputedByMinProximityOption {
+	return &AttributeCriteriaComputedByMinProximityOption{v}
+}
+
+// Get retrieves the actual value of the option parameter.
+func (o *AttributeCriteriaComputedByMinProximityOption) Get() bool {
+	if o == nil {
+		return false
+	}
+	return o.value
+}
+
+// MarshalJSON implements the json.Marshaler interface for
+// AttributeCriteriaComputedByMinProximityOption.
+func (o AttributeCriteriaComputedByMinProximityOption) MarshalJSON() ([]byte, error) {
+	return json.Marshal(o.value)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface for
+// AttributeCriteriaComputedByMinProximityOption.
+func (o *AttributeCriteriaComputedByMinProximityOption) UnmarshalJSON(data []byte) error {
+	if string(data) == "null" {
+		o.value = false
+		return nil
+	}
+	return json.Unmarshal(data, &o.value)
+}
+
+// Equal returns true if the given option is equal to the instance one. In case
+// the given option is nil, we checked the instance one is set to the default
+// value of the option.
+func (o *AttributeCriteriaComputedByMinProximityOption) Equal(o2 *AttributeCriteriaComputedByMinProximityOption) bool {
+	if o2 == nil {
+		return o.value == false
+	}
+	return o.value == o2.value
+}
+
+// AttributeCriteriaComputedByMinProximityEqual returns true if the two options are equal.
+// In case of one option being nil, the value of the other must be nil as well
+// or be set to the default value of this option.
+func AttributeCriteriaComputedByMinProximityEqual(o1, o2 *AttributeCriteriaComputedByMinProximityOption) bool {
+	if o1 != nil {
+		return o1.Equal(o2)
+	}
+	if o2 != nil {
+		return o2.Equal(o1)
+	}
+	return true
+}
diff --git a/algolia/opt/similar_query.go b/algolia/opt/similar_query.go
new file mode 100644
index 00000000..94e9beaf
--- /dev/null
+++ b/algolia/opt/similar_query.go
@@ -0,0 +1,63 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import "encoding/json"
+
+// SimilarQueryOption is a wrapper for an SimilarQuery option parameter. It holds
+// the actual value of the option that can be accessed by calling Get.
+type SimilarQueryOption struct {
+	value string
+}
+
+// SimilarQuery wraps the given value into a SimilarQueryOption.
+func SimilarQuery(v string) *SimilarQueryOption {
+	return &SimilarQueryOption{v}
+}
+
+// Get retrieves the actual value of the option parameter.
+func (o *SimilarQueryOption) Get() string {
+	if o == nil {
+		return ""
+	}
+	return o.value
+}
+
+// MarshalJSON implements the json.Marshaler interface for
+// SimilarQueryOption.
+func (o SimilarQueryOption) MarshalJSON() ([]byte, error) {
+	return json.Marshal(o.value)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface for
+// SimilarQueryOption.
+func (o *SimilarQueryOption) UnmarshalJSON(data []byte) error {
+	if string(data) == "null" {
+		o.value = ""
+		return nil
+	}
+	return json.Unmarshal(data, &o.value)
+}
+
+// Equal returns true if the given option is equal to the instance one. In case
+// the given option is nil, we checked the instance one is set to the default
+// value of the option.
+func (o *SimilarQueryOption) Equal(o2 *SimilarQueryOption) bool {
+	if o2 == nil {
+		return o.value == ""
+	}
+	return o.value == o2.value
+}
+
+// SimilarQueryEqual returns true if the two options are equal.
+// In case of one option being nil, the value of the other must be nil as well
+// or be set to the default value of this option.
+func SimilarQueryEqual(o1, o2 *SimilarQueryOption) bool {
+	if o1 != nil {
+		return o1.Equal(o2)
+	}
+	if o2 != nil {
+		return o2.Equal(o1)
+	}
+	return true
+}
diff --git a/algolia/search/query_params.go b/algolia/search/query_params.go
index 665f744f..0fbea79d 100644
--- a/algolia/search/query_params.go
+++ b/algolia/search/query_params.go
@@ -9,133 +9,137 @@ import (
 
 // QueryParams represents all the parameters that can be passed at query time.
 type QueryParams struct {
-	AttributesToRetrieve              *opt.AttributesToRetrieveOption              `json:"attributesToRetrieve,omitempty"`
-	RestrictSearchableAttributes      *opt.RestrictSearchableAttributesOption      `json:"restrictSearchableAttributes,omitempty"`
-	Filters                           *opt.FiltersOption                           `json:"filters,omitempty"`
-	FacetFilters                      *opt.FacetFiltersOption                      `json:"facetFilters,omitempty"`
-	OptionalFilters                   *opt.OptionalFiltersOption                   `json:"optionalFilters,omitempty"`
-	NumericFilters                    *opt.NumericFiltersOption                    `json:"numericFilters,omitempty"`
-	TagFilters                        *opt.TagFiltersOption                        `json:"tagFilters,omitempty"`
-	SumOrFiltersScores                *opt.SumOrFiltersScoresOption                `json:"sumOrFiltersScores,omitempty"`
-	Facets                            *opt.FacetsOption                            `json:"facets,omitempty"`
-	MaxValuesPerFacet                 *opt.MaxValuesPerFacetOption                 `json:"maxValuesPerFacet,omitempty"`
-	FacetingAfterDistinct             *opt.FacetingAfterDistinctOption             `json:"facetingAfterDistinct,omitempty"`
-	SortFacetValuesBy                 *opt.SortFacetValuesByOption                 `json:"sortFacetValuesBy,omitempty"`
-	AttributesToHighlight             *opt.AttributesToHighlightOption             `json:"attributesToHighlight,omitempty"`
-	AttributesToSnippet               *opt.AttributesToSnippetOption               `json:"attributesToSnippet,omitempty"`
-	HighlightPreTag                   *opt.HighlightPreTagOption                   `json:"highlightPreTag,omitempty"`
-	HighlightPostTag                  *opt.HighlightPostTagOption                  `json:"highlightPostTag,omitempty"`
-	SnippetEllipsisText               *opt.SnippetEllipsisTextOption               `json:"snippetEllipsisText,omitempty"`
-	RestrictHighlightAndSnippetArrays *opt.RestrictHighlightAndSnippetArraysOption `json:"restrictHighlightAndSnippetArrays,omitempty"`
-	Page                              *opt.PageOption                              `json:"page,omitempty"`
-	HitsPerPage                       *opt.HitsPerPageOption                       `json:"hitsPerPage,omitempty"`
-	Offset                            *opt.OffsetOption                            `json:"offset,omitempty"`
-	Length                            *opt.LengthOption                            `json:"length,omitempty"`
-	MinWordSizefor1Typo               *opt.MinWordSizefor1TypoOption               `json:"minWordSizefor1Typo,omitempty"`
-	MinWordSizefor2Typos              *opt.MinWordSizefor2TyposOption              `json:"minWordSizefor2Typos,omitempty"`
-	TypoTolerance                     *opt.TypoToleranceOption                     `json:"typoTolerance,omitempty"`
-	AllowTyposOnNumericTokens         *opt.AllowTyposOnNumericTokensOption         `json:"allowTyposOnNumericTokens,omitempty"`
-	DisableTypoToleranceOnAttributes  *opt.DisableTypoToleranceOnAttributesOption  `json:"disableTypoToleranceOnAttributes,omitempty"`
-	AroundLatLng                      *opt.AroundLatLngOption                      `json:"aroundLatLng,omitempty"`
-	AroundLatLngViaIP                 *opt.AroundLatLngViaIPOption                 `json:"aroundLatLngViaIP,omitempty"`
-	AroundRadius                      *opt.AroundRadiusOption                      `json:"aroundRadius,omitempty"`
-	AroundPrecision                   *opt.AroundPrecisionOption                   `json:"aroundPrecision,omitempty"`
-	MinimumAroundRadius               *opt.MinimumAroundRadiusOption               `json:"minimumAroundRadius,omitempty"`
-	InsideBoundingBox                 *opt.InsideBoundingBoxOption                 `json:"insideBoundingBox,omitempty"`
-	InsidePolygon                     *opt.InsidePolygonOption                     `json:"insidePolygon,omitempty"`
-	IgnorePlurals                     *opt.IgnorePluralsOption                     `json:"ignorePlurals,omitempty"`
-	RemoveStopWords                   *opt.RemoveStopWordsOption                   `json:"removeStopWords,omitempty"`
-	QueryLanguages                    *opt.QueryLanguagesOption                    `json:"queryLanguages,omitempty"`
-	QueryType                         *opt.QueryTypeOption                         `json:"queryType,omitempty"`
-	RemoveWordsIfNoResults            *opt.RemoveWordsIfNoResultsOption            `json:"removeWordsIfNoResults,omitempty"`
-	AdvancedSyntax                    *opt.AdvancedSyntaxOption                    `json:"advancedSyntax,omitempty"`
-	OptionalWords                     *opt.OptionalWordsOption                     `json:"optionalWords,omitempty"`
-	DisableExactOnAttributes          *opt.DisableExactOnAttributesOption          `json:"disableExactOnAttributes,omitempty"`
-	ExactOnSingleWordQuery            *opt.ExactOnSingleWordQueryOption            `json:"exactOnSingleWordQuery,omitempty"`
-	AlternativesAsExact               *opt.AlternativesAsExactOption               `json:"alternativesAsExact,omitempty"`
-	AdvancedSyntaxFeatures            *opt.AdvancedSyntaxFeaturesOption            `json:"advancedSyntaxFeatures,omitempty"`
-	EnableRules                       *opt.EnableRulesOption                       `json:"enableRules,omitempty"`
-	RuleContexts                      *opt.RuleContextsOption                      `json:"ruleContexts,omitempty"`
-	EnablePersonalization             *opt.EnablePersonalizationOption             `json:"enablePersonalization,omitempty"`
-	PersonalizationImpact             *opt.PersonalizationImpactOption             `json:"personalizationImpact,omitempty"`
-	UserToken                         *opt.UserTokenOption                         `json:"userToken,omitempty"`
-	Distinct                          *opt.DistinctOption                          `json:"distinct,omitempty"`
-	GetRankingInfo                    *opt.GetRankingInfoOption                    `json:"getRankingInfo,omitempty"`
-	ClickAnalytics                    *opt.ClickAnalyticsOption                    `json:"clickAnalytics,omitempty"`
-	Analytics                         *opt.AnalyticsOption                         `json:"analytics,omitempty"`
-	AnalyticsTags                     *opt.AnalyticsTagsOption                     `json:"analyticsTags,omitempty"`
-	Synonyms                          *opt.SynonymsOption                          `json:"synonyms,omitempty"`
-	ReplaceSynonymsInHighlight        *opt.ReplaceSynonymsInHighlightOption        `json:"replaceSynonymsInHighlight,omitempty"`
-	MinProximity                      *opt.MinProximityOption                      `json:"minProximity,omitempty"`
-	ResponseFields                    *opt.ResponseFieldsOption                    `json:"responseFields,omitempty"`
-	MaxFacetHits                      *opt.MaxFacetHitsOption                      `json:"maxFacetHits,omitempty"`
-	PercentileComputation             *opt.PercentileComputationOption             `json:"percentileComputation,omitempty"`
-	Explain                           *opt.ExplainOption                           `json:"explain,omitempty"`
+	AttributesToRetrieve                    *opt.AttributesToRetrieveOption                    `json:"attributesToRetrieve,omitempty"`
+	RestrictSearchableAttributes            *opt.RestrictSearchableAttributesOption            `json:"restrictSearchableAttributes,omitempty"`
+	Filters                                 *opt.FiltersOption                                 `json:"filters,omitempty"`
+	FacetFilters                            *opt.FacetFiltersOption                            `json:"facetFilters,omitempty"`
+	OptionalFilters                         *opt.OptionalFiltersOption                         `json:"optionalFilters,omitempty"`
+	NumericFilters                          *opt.NumericFiltersOption                          `json:"numericFilters,omitempty"`
+	TagFilters                              *opt.TagFiltersOption                              `json:"tagFilters,omitempty"`
+	SumOrFiltersScores                      *opt.SumOrFiltersScoresOption                      `json:"sumOrFiltersScores,omitempty"`
+	Facets                                  *opt.FacetsOption                                  `json:"facets,omitempty"`
+	MaxValuesPerFacet                       *opt.MaxValuesPerFacetOption                       `json:"maxValuesPerFacet,omitempty"`
+	FacetingAfterDistinct                   *opt.FacetingAfterDistinctOption                   `json:"facetingAfterDistinct,omitempty"`
+	SortFacetValuesBy                       *opt.SortFacetValuesByOption                       `json:"sortFacetValuesBy,omitempty"`
+	AttributesToHighlight                   *opt.AttributesToHighlightOption                   `json:"attributesToHighlight,omitempty"`
+	AttributesToSnippet                     *opt.AttributesToSnippetOption                     `json:"attributesToSnippet,omitempty"`
+	HighlightPreTag                         *opt.HighlightPreTagOption                         `json:"highlightPreTag,omitempty"`
+	HighlightPostTag                        *opt.HighlightPostTagOption                        `json:"highlightPostTag,omitempty"`
+	SnippetEllipsisText                     *opt.SnippetEllipsisTextOption                     `json:"snippetEllipsisText,omitempty"`
+	RestrictHighlightAndSnippetArrays       *opt.RestrictHighlightAndSnippetArraysOption       `json:"restrictHighlightAndSnippetArrays,omitempty"`
+	Page                                    *opt.PageOption                                    `json:"page,omitempty"`
+	HitsPerPage                             *opt.HitsPerPageOption                             `json:"hitsPerPage,omitempty"`
+	Offset                                  *opt.OffsetOption                                  `json:"offset,omitempty"`
+	Length                                  *opt.LengthOption                                  `json:"length,omitempty"`
+	MinWordSizefor1Typo                     *opt.MinWordSizefor1TypoOption                     `json:"minWordSizefor1Typo,omitempty"`
+	MinWordSizefor2Typos                    *opt.MinWordSizefor2TyposOption                    `json:"minWordSizefor2Typos,omitempty"`
+	TypoTolerance                           *opt.TypoToleranceOption                           `json:"typoTolerance,omitempty"`
+	AllowTyposOnNumericTokens               *opt.AllowTyposOnNumericTokensOption               `json:"allowTyposOnNumericTokens,omitempty"`
+	DisableTypoToleranceOnAttributes        *opt.DisableTypoToleranceOnAttributesOption        `json:"disableTypoToleranceOnAttributes,omitempty"`
+	AroundLatLng                            *opt.AroundLatLngOption                            `json:"aroundLatLng,omitempty"`
+	AroundLatLngViaIP                       *opt.AroundLatLngViaIPOption                       `json:"aroundLatLngViaIP,omitempty"`
+	AroundRadius                            *opt.AroundRadiusOption                            `json:"aroundRadius,omitempty"`
+	AroundPrecision                         *opt.AroundPrecisionOption                         `json:"aroundPrecision,omitempty"`
+	MinimumAroundRadius                     *opt.MinimumAroundRadiusOption                     `json:"minimumAroundRadius,omitempty"`
+	InsideBoundingBox                       *opt.InsideBoundingBoxOption                       `json:"insideBoundingBox,omitempty"`
+	InsidePolygon                           *opt.InsidePolygonOption                           `json:"insidePolygon,omitempty"`
+	IgnorePlurals                           *opt.IgnorePluralsOption                           `json:"ignorePlurals,omitempty"`
+	RemoveStopWords                         *opt.RemoveStopWordsOption                         `json:"removeStopWords,omitempty"`
+	QueryLanguages                          *opt.QueryLanguagesOption                          `json:"queryLanguages,omitempty"`
+	QueryType                               *opt.QueryTypeOption                               `json:"queryType,omitempty"`
+	RemoveWordsIfNoResults                  *opt.RemoveWordsIfNoResultsOption                  `json:"removeWordsIfNoResults,omitempty"`
+	AdvancedSyntax                          *opt.AdvancedSyntaxOption                          `json:"advancedSyntax,omitempty"`
+	OptionalWords                           *opt.OptionalWordsOption                           `json:"optionalWords,omitempty"`
+	DisableExactOnAttributes                *opt.DisableExactOnAttributesOption                `json:"disableExactOnAttributes,omitempty"`
+	ExactOnSingleWordQuery                  *opt.ExactOnSingleWordQueryOption                  `json:"exactOnSingleWordQuery,omitempty"`
+	AlternativesAsExact                     *opt.AlternativesAsExactOption                     `json:"alternativesAsExact,omitempty"`
+	AdvancedSyntaxFeatures                  *opt.AdvancedSyntaxFeaturesOption                  `json:"advancedSyntaxFeatures,omitempty"`
+	SimilarQuery                            *opt.SimilarQueryOption                            `json:"similarQuery,omitempty"`
+	EnableRules                             *opt.EnableRulesOption                             `json:"enableRules,omitempty"`
+	RuleContexts                            *opt.RuleContextsOption                            `json:"ruleContexts,omitempty"`
+	EnablePersonalization                   *opt.EnablePersonalizationOption                   `json:"enablePersonalization,omitempty"`
+	PersonalizationImpact                   *opt.PersonalizationImpactOption                   `json:"personalizationImpact,omitempty"`
+	UserToken                               *opt.UserTokenOption                               `json:"userToken,omitempty"`
+	Distinct                                *opt.DistinctOption                                `json:"distinct,omitempty"`
+	GetRankingInfo                          *opt.GetRankingInfoOption                          `json:"getRankingInfo,omitempty"`
+	ClickAnalytics                          *opt.ClickAnalyticsOption                          `json:"clickAnalytics,omitempty"`
+	Analytics                               *opt.AnalyticsOption                               `json:"analytics,omitempty"`
+	AnalyticsTags                           *opt.AnalyticsTagsOption                           `json:"analyticsTags,omitempty"`
+	Synonyms                                *opt.SynonymsOption                                `json:"synonyms,omitempty"`
+	ReplaceSynonymsInHighlight              *opt.ReplaceSynonymsInHighlightOption              `json:"replaceSynonymsInHighlight,omitempty"`
+	MinProximity                            *opt.MinProximityOption                            `json:"minProximity,omitempty"`
+	ResponseFields                          *opt.ResponseFieldsOption                          `json:"responseFields,omitempty"`
+	MaxFacetHits                            *opt.MaxFacetHitsOption                            `json:"maxFacetHits,omitempty"`
+	PercentileComputation                   *opt.PercentileComputationOption                   `json:"percentileComputation,omitempty"`
+	Explain                                 *opt.ExplainOption                                 `json:"explain,omitempty"`
+	AttributeCriteriaComputedByMinProximity *opt.AttributeCriteriaComputedByMinProximityOption `json:"attributeCriteriaComputedByMinProximity,omitempty"`
 }
 
 func newQueryParams(opts ...interface{}) QueryParams {
 	return QueryParams{
-		AttributesToRetrieve:              iopt.ExtractAttributesToRetrieve(opts...),
-		RestrictSearchableAttributes:      iopt.ExtractRestrictSearchableAttributes(opts...),
-		Filters:                           iopt.ExtractFilters(opts...),
-		FacetFilters:                      iopt.ExtractFacetFilters(opts...),
-		OptionalFilters:                   iopt.ExtractOptionalFilters(opts...),
-		NumericFilters:                    iopt.ExtractNumericFilters(opts...),
-		TagFilters:                        iopt.ExtractTagFilters(opts...),
-		SumOrFiltersScores:                iopt.ExtractSumOrFiltersScores(opts...),
-		Facets:                            iopt.ExtractFacets(opts...),
-		MaxValuesPerFacet:                 iopt.ExtractMaxValuesPerFacet(opts...),
-		FacetingAfterDistinct:             iopt.ExtractFacetingAfterDistinct(opts...),
-		SortFacetValuesBy:                 iopt.ExtractSortFacetValuesBy(opts...),
-		AttributesToHighlight:             iopt.ExtractAttributesToHighlight(opts...),
-		AttributesToSnippet:               iopt.ExtractAttributesToSnippet(opts...),
-		HighlightPreTag:                   iopt.ExtractHighlightPreTag(opts...),
-		HighlightPostTag:                  iopt.ExtractHighlightPostTag(opts...),
-		SnippetEllipsisText:               iopt.ExtractSnippetEllipsisText(opts...),
-		RestrictHighlightAndSnippetArrays: iopt.ExtractRestrictHighlightAndSnippetArrays(opts...),
-		Page:                              iopt.ExtractPage(opts...),
-		HitsPerPage:                       iopt.ExtractHitsPerPage(opts...),
-		Offset:                            iopt.ExtractOffset(opts...),
-		Length:                            iopt.ExtractLength(opts...),
-		MinWordSizefor1Typo:               iopt.ExtractMinWordSizefor1Typo(opts...),
-		MinWordSizefor2Typos:              iopt.ExtractMinWordSizefor2Typos(opts...),
-		TypoTolerance:                     iopt.ExtractTypoTolerance(opts...),
-		AllowTyposOnNumericTokens:         iopt.ExtractAllowTyposOnNumericTokens(opts...),
-		DisableTypoToleranceOnAttributes:  iopt.ExtractDisableTypoToleranceOnAttributes(opts...),
-		AroundLatLng:                      iopt.ExtractAroundLatLng(opts...),
-		AroundLatLngViaIP:                 iopt.ExtractAroundLatLngViaIP(opts...),
-		AroundRadius:                      iopt.ExtractAroundRadius(opts...),
-		AroundPrecision:                   iopt.ExtractAroundPrecision(opts...),
-		MinimumAroundRadius:               iopt.ExtractMinimumAroundRadius(opts...),
-		InsideBoundingBox:                 iopt.ExtractInsideBoundingBox(opts...),
-		InsidePolygon:                     iopt.ExtractInsidePolygon(opts...),
-		IgnorePlurals:                     iopt.ExtractIgnorePlurals(opts...),
-		RemoveStopWords:                   iopt.ExtractRemoveStopWords(opts...),
-		QueryLanguages:                    iopt.ExtractQueryLanguages(opts...),
-		QueryType:                         iopt.ExtractQueryType(opts...),
-		RemoveWordsIfNoResults:            iopt.ExtractRemoveWordsIfNoResults(opts...),
-		AdvancedSyntax:                    iopt.ExtractAdvancedSyntax(opts...),
-		OptionalWords:                     iopt.ExtractOptionalWords(opts...),
-		DisableExactOnAttributes:          iopt.ExtractDisableExactOnAttributes(opts...),
-		ExactOnSingleWordQuery:            iopt.ExtractExactOnSingleWordQuery(opts...),
-		AlternativesAsExact:               iopt.ExtractAlternativesAsExact(opts...),
-		AdvancedSyntaxFeatures:            iopt.ExtractAdvancedSyntaxFeatures(opts...),
-		EnableRules:                       iopt.ExtractEnableRules(opts...),
-		RuleContexts:                      iopt.ExtractRuleContexts(opts...),
-		EnablePersonalization:             iopt.ExtractEnablePersonalization(opts...),
-		PersonalizationImpact:             iopt.ExtractPersonalizationImpact(opts...),
-		UserToken:                         iopt.ExtractUserToken(opts...),
-		Distinct:                          iopt.ExtractDistinct(opts...),
-		GetRankingInfo:                    iopt.ExtractGetRankingInfo(opts...),
-		ClickAnalytics:                    iopt.ExtractClickAnalytics(opts...),
-		Analytics:                         iopt.ExtractAnalytics(opts...),
-		AnalyticsTags:                     iopt.ExtractAnalyticsTags(opts...),
-		Synonyms:                          iopt.ExtractSynonyms(opts...),
-		ReplaceSynonymsInHighlight:        iopt.ExtractReplaceSynonymsInHighlight(opts...),
-		MinProximity:                      iopt.ExtractMinProximity(opts...),
-		ResponseFields:                    iopt.ExtractResponseFields(opts...),
-		MaxFacetHits:                      iopt.ExtractMaxFacetHits(opts...),
-		PercentileComputation:             iopt.ExtractPercentileComputation(opts...),
-		Explain:                           iopt.ExtractExplain(opts...),
+		AttributesToRetrieve:                    iopt.ExtractAttributesToRetrieve(opts...),
+		RestrictSearchableAttributes:            iopt.ExtractRestrictSearchableAttributes(opts...),
+		Filters:                                 iopt.ExtractFilters(opts...),
+		FacetFilters:                            iopt.ExtractFacetFilters(opts...),
+		OptionalFilters:                         iopt.ExtractOptionalFilters(opts...),
+		NumericFilters:                          iopt.ExtractNumericFilters(opts...),
+		TagFilters:                              iopt.ExtractTagFilters(opts...),
+		SumOrFiltersScores:                      iopt.ExtractSumOrFiltersScores(opts...),
+		Facets:                                  iopt.ExtractFacets(opts...),
+		MaxValuesPerFacet:                       iopt.ExtractMaxValuesPerFacet(opts...),
+		FacetingAfterDistinct:                   iopt.ExtractFacetingAfterDistinct(opts...),
+		SortFacetValuesBy:                       iopt.ExtractSortFacetValuesBy(opts...),
+		AttributesToHighlight:                   iopt.ExtractAttributesToHighlight(opts...),
+		AttributesToSnippet:                     iopt.ExtractAttributesToSnippet(opts...),
+		HighlightPreTag:                         iopt.ExtractHighlightPreTag(opts...),
+		HighlightPostTag:                        iopt.ExtractHighlightPostTag(opts...),
+		SnippetEllipsisText:                     iopt.ExtractSnippetEllipsisText(opts...),
+		RestrictHighlightAndSnippetArrays:       iopt.ExtractRestrictHighlightAndSnippetArrays(opts...),
+		Page:                                    iopt.ExtractPage(opts...),
+		HitsPerPage:                             iopt.ExtractHitsPerPage(opts...),
+		Offset:                                  iopt.ExtractOffset(opts...),
+		Length:                                  iopt.ExtractLength(opts...),
+		MinWordSizefor1Typo:                     iopt.ExtractMinWordSizefor1Typo(opts...),
+		MinWordSizefor2Typos:                    iopt.ExtractMinWordSizefor2Typos(opts...),
+		TypoTolerance:                           iopt.ExtractTypoTolerance(opts...),
+		AllowTyposOnNumericTokens:               iopt.ExtractAllowTyposOnNumericTokens(opts...),
+		DisableTypoToleranceOnAttributes:        iopt.ExtractDisableTypoToleranceOnAttributes(opts...),
+		AroundLatLng:                            iopt.ExtractAroundLatLng(opts...),
+		AroundLatLngViaIP:                       iopt.ExtractAroundLatLngViaIP(opts...),
+		AroundRadius:                            iopt.ExtractAroundRadius(opts...),
+		AroundPrecision:                         iopt.ExtractAroundPrecision(opts...),
+		MinimumAroundRadius:                     iopt.ExtractMinimumAroundRadius(opts...),
+		InsideBoundingBox:                       iopt.ExtractInsideBoundingBox(opts...),
+		InsidePolygon:                           iopt.ExtractInsidePolygon(opts...),
+		IgnorePlurals:                           iopt.ExtractIgnorePlurals(opts...),
+		RemoveStopWords:                         iopt.ExtractRemoveStopWords(opts...),
+		QueryLanguages:                          iopt.ExtractQueryLanguages(opts...),
+		QueryType:                               iopt.ExtractQueryType(opts...),
+		RemoveWordsIfNoResults:                  iopt.ExtractRemoveWordsIfNoResults(opts...),
+		AdvancedSyntax:                          iopt.ExtractAdvancedSyntax(opts...),
+		OptionalWords:                           iopt.ExtractOptionalWords(opts...),
+		DisableExactOnAttributes:                iopt.ExtractDisableExactOnAttributes(opts...),
+		ExactOnSingleWordQuery:                  iopt.ExtractExactOnSingleWordQuery(opts...),
+		AlternativesAsExact:                     iopt.ExtractAlternativesAsExact(opts...),
+		AdvancedSyntaxFeatures:                  iopt.ExtractAdvancedSyntaxFeatures(opts...),
+		SimilarQuery:                            iopt.ExtractSimilarQuery(opts...),
+		EnableRules:                             iopt.ExtractEnableRules(opts...),
+		RuleContexts:                            iopt.ExtractRuleContexts(opts...),
+		EnablePersonalization:                   iopt.ExtractEnablePersonalization(opts...),
+		PersonalizationImpact:                   iopt.ExtractPersonalizationImpact(opts...),
+		UserToken:                               iopt.ExtractUserToken(opts...),
+		Distinct:                                iopt.ExtractDistinct(opts...),
+		GetRankingInfo:                          iopt.ExtractGetRankingInfo(opts...),
+		ClickAnalytics:                          iopt.ExtractClickAnalytics(opts...),
+		Analytics:                               iopt.ExtractAnalytics(opts...),
+		AnalyticsTags:                           iopt.ExtractAnalyticsTags(opts...),
+		Synonyms:                                iopt.ExtractSynonyms(opts...),
+		ReplaceSynonymsInHighlight:              iopt.ExtractReplaceSynonymsInHighlight(opts...),
+		MinProximity:                            iopt.ExtractMinProximity(opts...),
+		ResponseFields:                          iopt.ExtractResponseFields(opts...),
+		MaxFacetHits:                            iopt.ExtractMaxFacetHits(opts...),
+		PercentileComputation:                   iopt.ExtractPercentileComputation(opts...),
+		Explain:                                 iopt.ExtractExplain(opts...),
+		AttributeCriteriaComputedByMinProximity: iopt.ExtractAttributeCriteriaComputedByMinProximity(opts...),
 	}
 }
diff --git a/algolia/search/settings.go b/algolia/search/settings.go
index 10683543..3dcb1883 100644
--- a/algolia/search/settings.go
+++ b/algolia/search/settings.go
@@ -13,57 +13,58 @@ import (
 
 // Settings represents an index settings configuration.
 type Settings struct {
-	SearchableAttributes              *opt.SearchableAttributesOption              `json:"searchableAttributes,omitempty"`
-	AttributesForFaceting             *opt.AttributesForFacetingOption             `json:"attributesForFaceting,omitempty"`
-	UnretrievableAttributes           *opt.UnretrievableAttributesOption           `json:"unretrievableAttributes,omitempty"`
-	AttributesToRetrieve              *opt.AttributesToRetrieveOption              `json:"attributesToRetrieve,omitempty"`
-	Ranking                           *opt.RankingOption                           `json:"ranking,omitempty"`
-	CustomRanking                     *opt.CustomRankingOption                     `json:"customRanking,omitempty"`
-	Replicas                          *opt.ReplicasOption                          `json:"replicas,omitempty"`
-	Primary                           *opt.PrimaryOption                           `json:"primary,omitempty"`
-	MaxValuesPerFacet                 *opt.MaxValuesPerFacetOption                 `json:"maxValuesPerFacet,omitempty"`
-	SortFacetValuesBy                 *opt.SortFacetValuesByOption                 `json:"sortFacetValuesBy,omitempty"`
-	AttributesToHighlight             *opt.AttributesToHighlightOption             `json:"attributesToHighlight,omitempty"`
-	AttributesToSnippet               *opt.AttributesToSnippetOption               `json:"attributesToSnippet,omitempty"`
-	HighlightPreTag                   *opt.HighlightPreTagOption                   `json:"highlightPreTag,omitempty"`
-	HighlightPostTag                  *opt.HighlightPostTagOption                  `json:"highlightPostTag,omitempty"`
-	SnippetEllipsisText               *opt.SnippetEllipsisTextOption               `json:"snippetEllipsisText,omitempty"`
-	RestrictHighlightAndSnippetArrays *opt.RestrictHighlightAndSnippetArraysOption `json:"restrictHighlightAndSnippetArrays,omitempty"`
-	HitsPerPage                       *opt.HitsPerPageOption                       `json:"hitsPerPage,omitempty"`
-	PaginationLimitedTo               *opt.PaginationLimitedToOption               `json:"paginationLimitedTo,omitempty"`
-	MinWordSizefor1Typo               *opt.MinWordSizefor1TypoOption               `json:"minWordSizefor1Typo,omitempty"`
-	MinWordSizefor2Typos              *opt.MinWordSizefor2TyposOption              `json:"minWordSizefor2Typos,omitempty"`
-	TypoTolerance                     *opt.TypoToleranceOption                     `json:"typoTolerance,omitempty"`
-	AllowTyposOnNumericTokens         *opt.AllowTyposOnNumericTokensOption         `json:"allowTyposOnNumericTokens,omitempty"`
-	DisableTypoToleranceOnAttributes  *opt.DisableTypoToleranceOnAttributesOption  `json:"disableTypoToleranceOnAttributes,omitempty"`
-	DisableTypoToleranceOnWords       *opt.DisableTypoToleranceOnWordsOption       `json:"disableTypoToleranceOnWords,omitempty"`
-	SeparatorsToIndex                 *opt.SeparatorsToIndexOption                 `json:"separatorsToIndex,omitempty"`
-	IgnorePlurals                     *opt.IgnorePluralsOption                     `json:"ignorePlurals,omitempty"`
-	RemoveStopWords                   *opt.RemoveStopWordsOption                   `json:"removeStopWords,omitempty"`
-	CamelCaseAttributes               *opt.CamelCaseAttributesOption               `json:"camelCaseAttributes,omitempty"`
-	DecompoundedAttributes            *opt.DecompoundedAttributesOption            `json:"decompoundedAttributes,omitempty"`
-	KeepDiacriticsOnCharacters        *opt.KeepDiacriticsOnCharactersOption        `json:"keepDiacriticsOnCharacters,omitempty"`
-	QueryLanguages                    *opt.QueryLanguagesOption                    `json:"queryLanguages,omitempty"`
-	QueryType                         *opt.QueryTypeOption                         `json:"queryType,omitempty"`
-	RemoveWordsIfNoResults            *opt.RemoveWordsIfNoResultsOption            `json:"removeWordsIfNoResults,omitempty"`
-	AdvancedSyntax                    *opt.AdvancedSyntaxOption                    `json:"advancedSyntax,omitempty"`
-	OptionalWords                     *opt.OptionalWordsOption                     `json:"optionalWords,omitempty"`
-	DisablePrefixOnAttributes         *opt.DisablePrefixOnAttributesOption         `json:"disablePrefixOnAttributes,omitempty"`
-	DisableExactOnAttributes          *opt.DisableExactOnAttributesOption          `json:"disableExactOnAttributes,omitempty"`
-	ExactOnSingleWordQuery            *opt.ExactOnSingleWordQueryOption            `json:"exactOnSingleWordQuery,omitempty"`
-	AlternativesAsExact               *opt.AlternativesAsExactOption               `json:"alternativesAsExact,omitempty"`
-	AdvancedSyntaxFeatures            *opt.AdvancedSyntaxFeaturesOption            `json:"advancedSyntaxFeatures,omitempty"`
-	EnableRules                       *opt.EnableRulesOption                       `json:"enableRules,omitempty"`
-	NumericAttributesForFiltering     *opt.NumericAttributesForFilteringOption     `json:"numericAttributesForFiltering,omitempty"`
-	AllowCompressionOfIntegerArray    *opt.AllowCompressionOfIntegerArrayOption    `json:"allowCompressionOfIntegerArray,omitempty"`
-	AttributeForDistinct              *opt.AttributeForDistinctOption              `json:"attributeForDistinct,omitempty"`
-	Distinct                          *opt.DistinctOption                          `json:"distinct,omitempty"`
-	ReplaceSynonymsInHighlight        *opt.ReplaceSynonymsInHighlightOption        `json:"replaceSynonymsInHighlight,omitempty"`
-	MinProximity                      *opt.MinProximityOption                      `json:"minProximity,omitempty"`
-	ResponseFields                    *opt.ResponseFieldsOption                    `json:"responseFields,omitempty"`
-	MaxFacetHits                      *opt.MaxFacetHitsOption                      `json:"maxFacetHits,omitempty"`
-	Advanced                          *opt.AdvancedOption                          `json:"advanced,omitempty"`
-	CustomSettings                    map[string]interface{}                       `json:"-"`
+	SearchableAttributes                    *opt.SearchableAttributesOption                    `json:"searchableAttributes,omitempty"`
+	AttributesForFaceting                   *opt.AttributesForFacetingOption                   `json:"attributesForFaceting,omitempty"`
+	UnretrievableAttributes                 *opt.UnretrievableAttributesOption                 `json:"unretrievableAttributes,omitempty"`
+	AttributesToRetrieve                    *opt.AttributesToRetrieveOption                    `json:"attributesToRetrieve,omitempty"`
+	Ranking                                 *opt.RankingOption                                 `json:"ranking,omitempty"`
+	CustomRanking                           *opt.CustomRankingOption                           `json:"customRanking,omitempty"`
+	Replicas                                *opt.ReplicasOption                                `json:"replicas,omitempty"`
+	Primary                                 *opt.PrimaryOption                                 `json:"primary,omitempty"`
+	MaxValuesPerFacet                       *opt.MaxValuesPerFacetOption                       `json:"maxValuesPerFacet,omitempty"`
+	SortFacetValuesBy                       *opt.SortFacetValuesByOption                       `json:"sortFacetValuesBy,omitempty"`
+	AttributesToHighlight                   *opt.AttributesToHighlightOption                   `json:"attributesToHighlight,omitempty"`
+	AttributesToSnippet                     *opt.AttributesToSnippetOption                     `json:"attributesToSnippet,omitempty"`
+	HighlightPreTag                         *opt.HighlightPreTagOption                         `json:"highlightPreTag,omitempty"`
+	HighlightPostTag                        *opt.HighlightPostTagOption                        `json:"highlightPostTag,omitempty"`
+	SnippetEllipsisText                     *opt.SnippetEllipsisTextOption                     `json:"snippetEllipsisText,omitempty"`
+	RestrictHighlightAndSnippetArrays       *opt.RestrictHighlightAndSnippetArraysOption       `json:"restrictHighlightAndSnippetArrays,omitempty"`
+	HitsPerPage                             *opt.HitsPerPageOption                             `json:"hitsPerPage,omitempty"`
+	PaginationLimitedTo                     *opt.PaginationLimitedToOption                     `json:"paginationLimitedTo,omitempty"`
+	MinWordSizefor1Typo                     *opt.MinWordSizefor1TypoOption                     `json:"minWordSizefor1Typo,omitempty"`
+	MinWordSizefor2Typos                    *opt.MinWordSizefor2TyposOption                    `json:"minWordSizefor2Typos,omitempty"`
+	TypoTolerance                           *opt.TypoToleranceOption                           `json:"typoTolerance,omitempty"`
+	AllowTyposOnNumericTokens               *opt.AllowTyposOnNumericTokensOption               `json:"allowTyposOnNumericTokens,omitempty"`
+	DisableTypoToleranceOnAttributes        *opt.DisableTypoToleranceOnAttributesOption        `json:"disableTypoToleranceOnAttributes,omitempty"`
+	DisableTypoToleranceOnWords             *opt.DisableTypoToleranceOnWordsOption             `json:"disableTypoToleranceOnWords,omitempty"`
+	SeparatorsToIndex                       *opt.SeparatorsToIndexOption                       `json:"separatorsToIndex,omitempty"`
+	IgnorePlurals                           *opt.IgnorePluralsOption                           `json:"ignorePlurals,omitempty"`
+	RemoveStopWords                         *opt.RemoveStopWordsOption                         `json:"removeStopWords,omitempty"`
+	CamelCaseAttributes                     *opt.CamelCaseAttributesOption                     `json:"camelCaseAttributes,omitempty"`
+	DecompoundedAttributes                  *opt.DecompoundedAttributesOption                  `json:"decompoundedAttributes,omitempty"`
+	KeepDiacriticsOnCharacters              *opt.KeepDiacriticsOnCharactersOption              `json:"keepDiacriticsOnCharacters,omitempty"`
+	QueryLanguages                          *opt.QueryLanguagesOption                          `json:"queryLanguages,omitempty"`
+	QueryType                               *opt.QueryTypeOption                               `json:"queryType,omitempty"`
+	RemoveWordsIfNoResults                  *opt.RemoveWordsIfNoResultsOption                  `json:"removeWordsIfNoResults,omitempty"`
+	AdvancedSyntax                          *opt.AdvancedSyntaxOption                          `json:"advancedSyntax,omitempty"`
+	OptionalWords                           *opt.OptionalWordsOption                           `json:"optionalWords,omitempty"`
+	DisablePrefixOnAttributes               *opt.DisablePrefixOnAttributesOption               `json:"disablePrefixOnAttributes,omitempty"`
+	DisableExactOnAttributes                *opt.DisableExactOnAttributesOption                `json:"disableExactOnAttributes,omitempty"`
+	ExactOnSingleWordQuery                  *opt.ExactOnSingleWordQueryOption                  `json:"exactOnSingleWordQuery,omitempty"`
+	AlternativesAsExact                     *opt.AlternativesAsExactOption                     `json:"alternativesAsExact,omitempty"`
+	AdvancedSyntaxFeatures                  *opt.AdvancedSyntaxFeaturesOption                  `json:"advancedSyntaxFeatures,omitempty"`
+	EnableRules                             *opt.EnableRulesOption                             `json:"enableRules,omitempty"`
+	NumericAttributesForFiltering           *opt.NumericAttributesForFilteringOption           `json:"numericAttributesForFiltering,omitempty"`
+	AllowCompressionOfIntegerArray          *opt.AllowCompressionOfIntegerArrayOption          `json:"allowCompressionOfIntegerArray,omitempty"`
+	AttributeForDistinct                    *opt.AttributeForDistinctOption                    `json:"attributeForDistinct,omitempty"`
+	Distinct                                *opt.DistinctOption                                `json:"distinct,omitempty"`
+	ReplaceSynonymsInHighlight              *opt.ReplaceSynonymsInHighlightOption              `json:"replaceSynonymsInHighlight,omitempty"`
+	MinProximity                            *opt.MinProximityOption                            `json:"minProximity,omitempty"`
+	ResponseFields                          *opt.ResponseFieldsOption                          `json:"responseFields,omitempty"`
+	MaxFacetHits                            *opt.MaxFacetHitsOption                            `json:"maxFacetHits,omitempty"`
+	Advanced                                *opt.AdvancedOption                                `json:"advanced,omitempty"`
+	AttributeCriteriaComputedByMinProximity *opt.AttributeCriteriaComputedByMinProximityOption `json:"attributeCriteriaComputedByMinProximity,omitempty"`
+	CustomSettings                          map[string]interface{}                             `json:"-"`
 }
 
 type settings Settings
@@ -156,6 +157,7 @@ func (s *Settings) UnmarshalJSON(data []byte) error {
 		"responseFields",
 		"maxFacetHits",
 		"advanced",
+		"attributeCriteriaComputedByMinProximity",
 	} {
 		delete(s.CustomSettings, knownSetting)
 	}
@@ -386,6 +388,10 @@ func (s Settings) Equal(s2 Settings) bool {
 		debug.Printf("Settings.Advanced are not equal: %#v != %#v\n", s.Advanced, s2.Advanced)
 		return false
 	}
+	if !opt.AttributeCriteriaComputedByMinProximityEqual(s.AttributeCriteriaComputedByMinProximity, s2.AttributeCriteriaComputedByMinProximity) {
+		debug.Printf("Settings.AttributeCriteriaComputedByMinProximity are not equal: %#v != %#v\n", s.AttributeCriteriaComputedByMinProximity, s2.AttributeCriteriaComputedByMinProximity)
+		return false
+	}
 	return true
 }
 
@@ -441,6 +447,7 @@ func (s Settings) String() string {
 	settingsStr += fmt.Sprintf("\tResponseFields: %v,\n", stringifyReturnValues(s.ResponseFields.Get()))
 	settingsStr += fmt.Sprintf("\tMaxFacetHits: %v,\n", stringifyReturnValues(s.MaxFacetHits.Get()))
 	settingsStr += fmt.Sprintf("\tAdvanced: %v,\n", stringifyReturnValues(s.Advanced.Get()))
+	settingsStr += fmt.Sprintf("\tAttributeCriteriaComputedByMinProximity: %v,\n", stringifyReturnValues(s.AttributeCriteriaComputedByMinProximity.Get()))
 
 	settingsStr += "\tCustomSettings{\n"
 	for k, v := range s.CustomSettings {
diff --git a/algolia/search/utils.go b/algolia/search/utils.go
index 6b99b059..fcb2282c 100644
--- a/algolia/search/utils.go
+++ b/algolia/search/utils.go
@@ -3,6 +3,8 @@ package search
 import (
 	"encoding/json"
 	"fmt"
+	"reflect"
+	"strings"
 	"time"
 
 	"github.com/algolia/algoliasearch-client-go/algolia/call"
@@ -53,7 +55,7 @@ func waitWithRetry(f func() (bool, error)) error {
 	}
 }
 
-func getObjectID(object interface{}) (string, bool) {
+func getObjectIDWithMarshal(object interface{}) (string, bool) {
 	data, err := json.Marshal(object)
 	if err != nil {
 		return "", false
@@ -78,6 +80,30 @@ func getObjectID(object interface{}) (string, bool) {
 	}
 }
 
+func getObjectIDWithReflect(object interface{}) (string, bool) {
+	t := reflect.TypeOf(object)
+	if t == nil || t.Kind() != reflect.Struct {
+		return "", false
+	}
+
+	ve := reflect.ValueOf(object)
+	for i := 0; i < ve.NumField(); i++ {
+		jsonTagValue, ok := t.Field(i).Tag.Lookup("json")
+		if ok && strings.Contains(jsonTagValue, "objectID") {
+			return fmt.Sprintf("%v", ve.Field(i).Interface()), true
+		}
+	}
+	return "", false
+}
+
+func getObjectID(object interface{}) (string, bool) {
+	objectID, ok := getObjectIDWithReflect(object)
+	if !ok {
+		objectID, ok = getObjectIDWithMarshal(object)
+	}
+	return objectID, ok
+}
+
 func hasObjectID(object interface{}) bool {
 	_, ok := getObjectID(object)
 	return ok


Test patch:
diff --git a/algolia/internal/opt/attribute_criteria_computed_by_min_proximity_test.go b/algolia/internal/opt/attribute_criteria_computed_by_min_proximity_test.go
new file mode 100644
index 00000000..56640fe5
--- /dev/null
+++ b/algolia/internal/opt/attribute_criteria_computed_by_min_proximity_test.go
@@ -0,0 +1,41 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/algolia/algoliasearch-client-go/algolia/opt"
+	"github.com/stretchr/testify/require"
+)
+
+func TestAttributeCriteriaComputedByMinProximity(t *testing.T) {
+	for _, c := range []struct {
+		opts     []interface{}
+		expected *opt.AttributeCriteriaComputedByMinProximityOption
+	}{
+		{
+			opts:     []interface{}{nil},
+			expected: opt.AttributeCriteriaComputedByMinProximity(false),
+		},
+		{
+			opts:     []interface{}{opt.AttributeCriteriaComputedByMinProximity(true)},
+			expected: opt.AttributeCriteriaComputedByMinProximity(true),
+		},
+		{
+			opts:     []interface{}{opt.AttributeCriteriaComputedByMinProximity(false)},
+			expected: opt.AttributeCriteriaComputedByMinProximity(false),
+		},
+	} {
+		var (
+			in  = ExtractAttributeCriteriaComputedByMinProximity(c.opts...)
+			out opt.AttributeCriteriaComputedByMinProximityOption
+		)
+		data, err := json.Marshal(&in)
+		require.NoError(t, err)
+		err = json.Unmarshal(data, &out)
+		require.NoError(t, err)
+		require.Equal(t, *c.expected, out)
+	}
+}
diff --git a/algolia/internal/opt/similar_query_test.go b/algolia/internal/opt/similar_query_test.go
new file mode 100644
index 00000000..51238470
--- /dev/null
+++ b/algolia/internal/opt/similar_query_test.go
@@ -0,0 +1,41 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package opt
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/algolia/algoliasearch-client-go/algolia/opt"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSimilarQuery(t *testing.T) {
+	for _, c := range []struct {
+		opts     []interface{}
+		expected *opt.SimilarQueryOption
+	}{
+		{
+			opts:     []interface{}{nil},
+			expected: opt.SimilarQuery(""),
+		},
+		{
+			opts:     []interface{}{opt.SimilarQuery("")},
+			expected: opt.SimilarQuery(""),
+		},
+		{
+			opts:     []interface{}{opt.SimilarQuery("content of the string value")},
+			expected: opt.SimilarQuery("content of the string value"),
+		},
+	} {
+		var (
+			in  = ExtractSimilarQuery(c.opts...)
+			out opt.SimilarQueryOption
+		)
+		data, err := json.Marshal(&in)
+		require.NoError(t, err)
+		err = json.Unmarshal(data, &out)
+		require.NoError(t, err)
+		require.Equal(t, *c.expected, out)
+	}
+}
diff --git a/algolia/opt/option_getters_test.go b/algolia/opt/option_getters_test.go
index 345437c7..88277af2 100644
--- a/algolia/opt/option_getters_test.go
+++ b/algolia/opt/option_getters_test.go
@@ -182,6 +182,9 @@ func TestOptionGetters(t *testing.T) {
 	var advancedSyntaxFeatures *AdvancedSyntaxFeaturesOption = nil
 	advancedSyntaxFeatures.Get()
 
+	var similarQuery *SimilarQueryOption = nil
+	similarQuery.Get()
+
 	var enableRules *EnableRulesOption = nil
 	enableRules.Get()
 
@@ -248,6 +251,9 @@ func TestOptionGetters(t *testing.T) {
 	var advanced *AdvancedOption = nil
 	advanced.Get()
 
+	var attributeCriteriaComputedByMinProximity *AttributeCriteriaComputedByMinProximityOption = nil
+	attributeCriteriaComputedByMinProximity.Get()
+
 	var restrictIndices *RestrictIndicesOption = nil
 	restrictIndices.Get()
 
diff --git a/algolia/search/requests_multiple_test.go b/algolia/search/requests_multiple_test.go
new file mode 100644
index 00000000..eb051346
--- /dev/null
+++ b/algolia/search/requests_multiple_test.go
@@ -0,0 +1,75 @@
+package search
+
+import (
+	"encoding/json"
+	"net/url"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/algolia/algoliasearch-client-go/algolia/opt"
+)
+
+func TestSerializationNestedArraysInMultipleQueries(t *testing.T) {
+	queries := []IndexedQuery{
+		NewIndexedQuery("test",
+			opt.FacetFilterAnd(
+				opt.FacetFilterOr(
+					opt.FacetFilter("facet1:true"),
+					opt.FacetFilter("facet2:true"),
+				),
+				opt.FacetFilter("facet3:true"),
+			),
+			opt.InsideBoundingBox([][4]float64{
+				{1.0, 2.0, 3.0, 4.0},
+				{5.0, 6.0, 7.0, 8.0},
+			}),
+		),
+	}
+
+	reqIn := newMultipleQueriesReq(queries, "none")
+
+	data, err := json.Marshal(reqIn)
+	require.NoError(t, err)
+
+	var reqOut multipleQueriesReq
+	err = json.Unmarshal(data, &reqOut)
+	require.NoError(t, err)
+
+	require.Len(t, reqOut.Requests, 1)
+	params := reqOut.Requests[0].Params
+
+	values, err := url.ParseQuery(params)
+	require.NoError(t, err)
+	require.Len(t, values, 3)
+
+	// Check facetFilters
+	{
+		var (
+			got      [][]string
+			expected = [][]string{
+				{"facet1:true", "facet2:true"},
+				{"facet3:true"},
+			}
+		)
+
+		err = json.Unmarshal([]byte(values.Get("facetFilters")), &got)
+		require.NoError(t, err)
+		require.Equal(t, expected, got)
+	}
+
+	// Check insideBoundingBox
+	{
+		var (
+			got      [][]float64
+			expected = [][]float64{
+				{1.0, 2.0, 3.0, 4.0},
+				{5.0, 6.0, 7.0, 8.0},
+			}
+		)
+
+		err = json.Unmarshal([]byte(values.Get("insideBoundingBox")), &got)
+		require.NoError(t, err)
+		require.Equal(t, expected, got)
+	}
+}