This is an automated email from the ASF dual-hosted git repository.
hanahmily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git
The following commit(s) were added to refs/heads/main by this push:
new 214265832 Fix tagFamilyFilters.range method about encode and logic
problem (#976)
214265832 is described below
commit 21426583251c95711ec2b165f7f069f0cab6f29d
Author: eye-gu <[email protected]>
AuthorDate: Sun Feb 22 21:24:04 2026 +0800
Fix tagFamilyFilters.range method about encode and logic problem (#976)
* Fix tagFamilyFilters.range method about encode and logic problem
* Add boundary case tests for Range method in tag filters
---------
Co-authored-by: Gao Hongtao <[email protected]>
---
banyand/stream/tag_filter.go | 15 ++--
banyand/stream/tag_filter_test.go | 169 +++++++++++++++++++++++++++++++++++---
2 files changed, 167 insertions(+), 17 deletions(-)
diff --git a/banyand/stream/tag_filter.go b/banyand/stream/tag_filter.go
index d83729548..4498777a6 100644
--- a/banyand/stream/tag_filter.go
+++ b/banyand/stream/tag_filter.go
@@ -21,7 +21,10 @@ import (
"bytes"
"fmt"
+ "github.com/blugelabs/bluge/numeric"
+
pkgbytes "github.com/apache/skywalking-banyandb/pkg/bytes"
+ "github.com/apache/skywalking-banyandb/pkg/convert"
"github.com/apache/skywalking-banyandb/pkg/encoding"
"github.com/apache/skywalking-banyandb/pkg/filter"
"github.com/apache/skywalking-banyandb/pkg/fs"
@@ -233,10 +236,9 @@ func (tfs *tagFamilyFilters) Range(tagName string,
rangeOpts index.RangeOpts) (b
if !ok {
return false, fmt.Errorf("lower is not
a float value: %v", rangeOpts.Lower)
}
- value := make([]byte, 0)
- value = encoding.Int64ToBytes(value,
int64(lower.Value))
+ value :=
convert.Int64ToBytes(numeric.Float64ToInt64(lower.Value))
if bytes.Compare(tf.max, value) == -1 ||
!rangeOpts.IncludesLower && bytes.Equal(tf.max, value) {
- return false, nil
+ return true, nil
}
}
if rangeOpts.Upper != nil {
@@ -244,15 +246,14 @@ func (tfs *tagFamilyFilters) Range(tagName string,
rangeOpts index.RangeOpts) (b
if !ok {
return false, fmt.Errorf("upper is not
a float value: %v", rangeOpts.Upper)
}
- value := make([]byte, 0)
- value = encoding.Int64ToBytes(value,
int64(upper.Value))
+ value :=
convert.Int64ToBytes(numeric.Float64ToInt64(upper.Value))
if bytes.Compare(tf.min, value) == 1 ||
!rangeOpts.IncludesUpper && bytes.Equal(tf.min, value) {
- return false, nil
+ return true, nil
}
}
}
}
- return true, nil
+ return false, nil
}
// Having checks if any of the provided tag values might exist in the bloom
filter.
diff --git a/banyand/stream/tag_filter_test.go
b/banyand/stream/tag_filter_test.go
index ad5fafccd..363b1c621 100644
--- a/banyand/stream/tag_filter_test.go
+++ b/banyand/stream/tag_filter_test.go
@@ -25,6 +25,7 @@ import (
"github.com/stretchr/testify/assert"
+ "github.com/apache/skywalking-banyandb/pkg/convert"
"github.com/apache/skywalking-banyandb/pkg/encoding"
"github.com/apache/skywalking-banyandb/pkg/filter"
"github.com/apache/skywalking-banyandb/pkg/fs"
@@ -491,10 +492,8 @@ func generateMetaAndFilterWithScenarios(scenarios []struct
{
}
if scenario.hasMinMax && scenario.valueType ==
pbv1.ValueTypeInt64 {
- tm.min = make([]byte, 8)
- tm.max = make([]byte, 8)
- binary.BigEndian.PutUint64(tm.min, 100)
- binary.BigEndian.PutUint64(tm.max, 200)
+ tm.min = convert.Int64ToBytes(100)
+ tm.max = convert.Int64ToBytes(200)
}
if scenario.hasFilter {
@@ -600,6 +599,8 @@ func TestTagFamilyFiltersNPEAndUnmarshalBehavior(t
*testing.T) {
tff := tfs.tagFamilyFilters[0]
assert.NotNil(t, tff)
+ ops := index.NewIntRangeOpts(150, 180, true, true)
+
tests := []struct {
rangeOpts *index.RangeOpts
name string
@@ -662,12 +663,7 @@ func TestTagFamilyFiltersNPEAndUnmarshalBehavior(t
*testing.T) {
eqExpected: true,
eqDescription: "Eq should return true (conservative)
when filter is nil",
testRange: true,
- rangeOpts: &index.RangeOpts{
- Lower: &index.FloatTermValue{Value:
150.0},
- Upper: &index.FloatTermValue{Value:
180.0},
- IncludesLower: true,
- IncludesUpper: true,
- },
+ rangeOpts: &ops,
rangeExpected: false, // should not skip (value is
within range)
rangeErr: false,
},
@@ -722,3 +718,156 @@ func TestTagFamilyFiltersNPEAndUnmarshalBehavior(t
*testing.T) {
})
}
}
+
+func TestTagFamilyFiltersRangeBoundaryCases(t *testing.T) {
+ scenarios := []struct {
+ name string
+ valueType pbv1.ValueType
+ hasFilter bool
+ hasMinMax bool
+ encodeType encoding.EncodeType
+ }{
+ {
+ name: "numeric-tag",
+ valueType: pbv1.ValueTypeInt64,
+ hasFilter: false,
+ hasMinMax: true,
+ encodeType: encoding.EncodeTypePlain,
+ },
+ }
+
+ metaBuf, filterBuf, tagValueBuf :=
generateMetaAndFilterWithScenarios(scenarios)
+
+ tagFamilies := map[string]*dataBlock{
+ "default": {
+ offset: 0,
+ size: uint64(len(metaBuf)),
+ },
+ }
+ metaReaders := map[string]fs.Reader{
+ "default": &mockReader{data: metaBuf},
+ }
+ filterReaders := map[string]fs.Reader{
+ "default": &mockReader{data: filterBuf},
+ }
+ tagValueReaders := map[string]fs.Reader{
+ "default": &mockReader{data: tagValueBuf},
+ }
+
+ tfs := generateTagFamilyFilters()
+ defer releaseTagFamilyFilters(tfs)
+ tfs.unmarshal(tagFamilies, metaReaders, filterReaders, tagValueReaders)
+
+ tests := []struct {
+ name string
+ description string
+ lower int64
+ upper int64
+ includesLower bool
+ includesUpper bool
+ expectedSkip bool
+ }{
+ {
+ name: "query above block max - inclusive",
+ lower: 300,
+ upper: 400,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: true,
+ description: "query [300, 400] vs block [100, 200]
should skip",
+ },
+ {
+ name: "query above block max - adjacent",
+ lower: 200,
+ upper: 300,
+ includesLower: false,
+ includesUpper: true,
+ expectedSkip: true,
+ description: "query (200, 300] vs block [100, 200]
should skip since lower is exclusive",
+ },
+ {
+ name: "query below block min - inclusive",
+ lower: 10,
+ upper: 50,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: true,
+ description: "query [10, 50] vs block [100, 200]
should skip",
+ },
+ {
+ name: "query below block min - adjacent",
+ lower: 50,
+ upper: 100,
+ includesLower: true,
+ includesUpper: false,
+ expectedSkip: true,
+ description: "query [50, 100) vs block [100, 200]
should skip since upper is exclusive",
+ },
+ {
+ name: "query overlaps block lower",
+ lower: 50,
+ upper: 150,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [50, 150] vs block [100, 200]
should not skip",
+ },
+ {
+ name: "query overlaps block upper",
+ lower: 150,
+ upper: 250,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [150, 250] vs block [100, 200]
should not skip",
+ },
+ {
+ name: "query contains block",
+ lower: 50,
+ upper: 250,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [50, 250] vs block [100, 200]
should not skip",
+ },
+ {
+ name: "query within block",
+ lower: 120,
+ upper: 180,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [120, 180] vs block [100, 200]
should not skip",
+ },
+ {
+ name: "inclusive lower equals block max",
+ lower: 200,
+ upper: 300,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [200, 300] vs block [100, 200]
should not skip since lower is inclusive",
+ },
+ {
+ name: "inclusive upper equals block min",
+ lower: 50,
+ upper: 100,
+ includesLower: true,
+ includesUpper: true,
+ expectedSkip: false,
+ description: "query [50, 100] vs block [100, 200]
should not skip since upper is inclusive",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tester := assert.New(t)
+
+ rangeOpts := index.NewIntRangeOpts(tt.lower, tt.upper,
tt.includesLower, tt.includesUpper)
+ shouldSkip, err := tfs.Range("numeric-tag", rangeOpts)
+
+ tester.NoError(err)
+ tester.Equal(tt.expectedSkip, shouldSkip,
tt.description)
+ })
+ }
+}