Repository: timshannon/bolthold
Pull request #138 (2023-11-29 17:53:01)

Fixes Issue #137


Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index a4021c0..acda157 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -16,7 +16,7 @@ jobs:
     - name: Set up Go
       uses: actions/setup-go@v2
       with:
-        go-version: 1.16
+        go-version: 1.21.1
 
     - name: Build
       run: go build -v ./...
diff --git a/compare.go b/compare.go
index abe1dd8..af18572 100644
--- a/compare.go
+++ b/compare.go
@@ -32,6 +32,10 @@ type Comparer interface {
 }
 
 func (c *Criterion) compare(rowValue, criterionValue interface{}, currentRow interface{}) (int, error) {
+	if reflect.TypeOf(rowValue) == reflect.TypeOf(reflect.Value{}) {
+		rowValue = rowValue.(reflect.Value).Interface()
+	}
+
 	if rowValue == nil || criterionValue == nil {
 		if rowValue == criterionValue {
 			return 0, nil
diff --git a/criteria.go b/criteria.go
index b0f2510..4088a40 100644
--- a/criteria.go
+++ b/criteria.go
@@ -81,13 +81,15 @@ type Criterion struct {
 	negate   bool
 }
 
-func hasMatchFunc(criteria []*Criterion) bool {
+// some operators can't function against an indexed value, they need to look at
+// the entire record
+func canUseIndex(criteria []*Criterion) bool {
 	for _, c := range criteria {
-		if c.operator == fn {
-			return true
+		if c.operator == fn || c.operator == all {
+			return false
 		}
 	}
-	return false
+	return true
 }
 
 // Slice turns a slice of any time into []interface{} by copying the slice values so it can be easily passed
diff --git a/go.mod b/go.mod
index cc77273..239ba64 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,7 @@ module github.com/timshannon/bolthold
 
 go 1.16
 
-require go.etcd.io/bbolt v1.3.6
+require (
+	go.etcd.io/bbolt v1.3.8
+	golang.org/x/sys v0.15.0 // indirect
+)
diff --git a/go.sum b/go.sum
index 3bc6bff..d623abb 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,22 @@
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
-go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d h1:L/IKR6COd7ubZrs2oTnTi73IhgqJ71c9s80WsQnh0Es=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
+go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
+go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/index.go b/index.go
index a53a000..82d78a6 100644
--- a/index.go
+++ b/index.go
@@ -265,8 +265,8 @@ func (s *Store) newIterator(source BucketSource, typeName string, query *Query)
 		iBucket = source.Bucket(indexBucketName(typeName, query.index))
 	}
 
-	if iBucket == nil || hasMatchFunc(criteria) {
-		// bad index or matches Function on indexed field, filter through entire store
+	if iBucket == nil || !canUseIndex(criteria) {
+		// bad index or criteria that can't use indexes, filter through entire store
 		query.badIndex = true
 
 		iter.indexCursor = source.Bucket([]byte(typeName)).Cursor()


Test patch:
diff --git a/find_test.go b/find_test.go
index 2efc45e..b06c689 100644
--- a/find_test.go
+++ b/find_test.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/timshannon/bolthold"
+	bh "github.com/timshannon/bolthold"
 )
 
 type ItemTest struct {
@@ -1227,3 +1228,29 @@ func Test67SeekCursor(t *testing.T) {
 		equals(t, 0, len(result))
 	})
 }
+
+func TestIssue137SliceIndexWithPointers(t *testing.T) {
+
+	type Event struct {
+		Id         uint64
+		Type       string   `boltholdIndex:"Type"`
+		Categories []string `boltholdSliceIndex:"Categories"`
+	}
+
+	testWrap(t, func(store *bh.Store, t *testing.T) {
+		cat1 := "Cat 1"
+		cat2 := "Cat 2"
+		cat3 := "Cat 3"
+
+		e1 := &Event{Id: 1, Type: "Type1", Categories: []string{cat1, cat2}}
+		e2 := &Event{Id: 2, Type: "Type1", Categories: []string{cat3}}
+
+		ok(t, store.Insert(e1.Id, e1))
+		ok(t, store.Insert(e2.Id, e2))
+
+		var es []*Event
+		query := bh.Where("Categories").ContainsAll(cat1, cat2).Index("Categories")
+		ok(t, store.Find(&es, query))
+		equals(t, 1, len(es))
+	})
+}