From ac8108fb36dbd1d4c60620c80b9f5c3d549b7976 Mon Sep 17 00:00:00 2001 From: usama khan Date: Mon, 12 Oct 2026 13:32:32 +0530 Subject: [PATCH] search: fix inverted error check in facet range decoding protoToRange decodes the facet range refinements returned by the search service. Its two ParseFloat checks are inverted: each bound is assigned only when parsing fails, so a valid bound is discarded and left at the infinite default, while an empty or malformed bound is kept as 0. A refinement that should decode to 1.5..3 comes back as -Inf..+Inf, and a half-open range picks up a spurious 0 endpoint. Use err == nil so finite bounds are decoded and absent bounds keep their infinity sentinel. The v2 copy has the same bug. --- search/search.go | 4 ++-- search/search_test.go | 31 +++++++++++++++++++++++++++++++ v2/search/search.go | 4 ++-- v2/search/search_test.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/search/search.go b/search/search.go index fd768084..8485433c 100644 --- a/search/search.go +++ b/search/search.go @@ -768,10 +768,10 @@ func rangeToProto(r Range) (*pb.FacetRange, error) { func protoToRange(rng *pb.FacetRefinement_Range) Range { r := Range{Start: negInf, End: posInf} - if x, err := strconv.ParseFloat(rng.GetStart(), 64); err != nil { + if x, err := strconv.ParseFloat(rng.GetStart(), 64); err == nil { r.Start = x } - if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err != nil { + if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err == nil { r.End = x } return r diff --git a/search/search_test.go b/search/search_test.go index 0459cd74..519dfb63 100644 --- a/search/search_test.go +++ b/search/search_test.go @@ -7,6 +7,7 @@ package search import ( "errors" "fmt" + "math" "reflect" "strings" "testing" @@ -1268,3 +1269,33 @@ func TestDeleteMultiError(t *testing.T) { t.Fatalf("got nil, want not-nill MultiError[1]") } } + +func TestProtoToRange(t *testing.T) { + testCases := []struct { + desc string + rng *pb.FacetRefinement_Range + want Range + }{ + { + desc: "both bounds finite", + rng: &pb.FacetRefinement_Range{Start: proto.String("1.5e+00"), End: proto.String("3e+00")}, + want: Range{Start: 1.5, End: 3}, + }, + { + desc: "no lower bound", + rng: &pb.FacetRefinement_Range{End: proto.String("3e+00")}, + want: Range{Start: math.Inf(-1), End: 3}, + }, + { + desc: "no upper bound", + rng: &pb.FacetRefinement_Range{Start: proto.String("1.5e+00")}, + want: Range{Start: 1.5, End: math.Inf(1)}, + }, + } + for _, tc := range testCases { + got := protoToRange(tc.rng) + if got != tc.want { + t.Errorf("%s: protoToRange(%v) = %v, want %v", tc.desc, tc.rng, got, tc.want) + } + } +} diff --git a/v2/search/search.go b/v2/search/search.go index bd79d0e3..11e9cd24 100644 --- a/v2/search/search.go +++ b/v2/search/search.go @@ -768,10 +768,10 @@ func rangeToProto(r Range) (*pb.FacetRange, error) { func protoToRange(rng *pb.FacetRefinement_Range) Range { r := Range{Start: negInf, End: posInf} - if x, err := strconv.ParseFloat(rng.GetStart(), 64); err != nil { + if x, err := strconv.ParseFloat(rng.GetStart(), 64); err == nil { r.Start = x } - if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err != nil { + if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err == nil { r.End = x } return r diff --git a/v2/search/search_test.go b/v2/search/search_test.go index e757ed69..66cd69b3 100644 --- a/v2/search/search_test.go +++ b/v2/search/search_test.go @@ -7,6 +7,7 @@ package search import ( "errors" "fmt" + "math" "reflect" "strings" "testing" @@ -1268,3 +1269,33 @@ func TestDeleteMultiError(t *testing.T) { t.Fatalf("got nil, want not-nill MultiError[1]") } } + +func TestProtoToRange(t *testing.T) { + testCases := []struct { + desc string + rng *pb.FacetRefinement_Range + want Range + }{ + { + desc: "both bounds finite", + rng: &pb.FacetRefinement_Range{Start: proto.String("1.5e+00"), End: proto.String("3e+00")}, + want: Range{Start: 1.5, End: 3}, + }, + { + desc: "no lower bound", + rng: &pb.FacetRefinement_Range{End: proto.String("3e+00")}, + want: Range{Start: math.Inf(-1), End: 3}, + }, + { + desc: "no upper bound", + rng: &pb.FacetRefinement_Range{Start: proto.String("1.5e+00")}, + want: Range{Start: 1.5, End: math.Inf(1)}, + }, + } + for _, tc := range testCases { + got := protoToRange(tc.rng) + if got != tc.want { + t.Errorf("%s: protoToRange(%v) = %v, want %v", tc.desc, tc.rng, got, tc.want) + } + } +}