mirror of
https://github.com/timshannon/bolthold.git
synced 2026-04-23 00:27:06 +08:00
Added Not() criterion modifier
examples
```go
bolthold.Where("Category").Not().In("food", "animal")
bolthold.Where("Tags").Not().IsNil()
bolthold.Where("Tags").Not().IsNil().And("Name").Not().RegExp(regexp.MustCompile("ea"))
bolthold.Where(bolthold.Key).Not().Eq(testData[4].Key)
bolthold.Where("Category").Not().Eq("food").And("Category").Not().Eq("animal").Index("Category")
```
We now unfortunately have an overlap with the `Ne()` and `Not().Eq()`,
but it's not worth dropping the `Ne` operator and breaking backwards
compatibility.
This commit is contained in:
@@ -22,3 +22,4 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.swp
|
||||
|
||||
+36
-5
@@ -303,7 +303,7 @@ var testResults = []test{
|
||||
field := ra.Field()
|
||||
_, ok := field.(string)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Field not a string, it's a %T!", field)
|
||||
return false, fmt.Errorf("field not a string, it's a %T", field)
|
||||
}
|
||||
|
||||
return strings.HasPrefix(field.(string), "oat"), nil
|
||||
@@ -316,7 +316,7 @@ var testResults = []test{
|
||||
record := ra.Record()
|
||||
_, ok := record.(*ItemTest)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Record not an ItemTest, it's a %T!", record)
|
||||
return false, fmt.Errorf("record not an ItemTest, it's a %T", record)
|
||||
}
|
||||
|
||||
return strings.HasPrefix(record.(*ItemTest).Name, "oat"), nil
|
||||
@@ -435,7 +435,7 @@ var testResults = []test{
|
||||
field := ra.Field()
|
||||
_, ok := field.(string)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Field not a string, it's a %T!", field)
|
||||
return false, fmt.Errorf("field not a string, it's a %T", field)
|
||||
}
|
||||
|
||||
return !strings.HasPrefix(field.(string), "veh"), nil
|
||||
@@ -448,7 +448,7 @@ var testResults = []test{
|
||||
field := ra.Field()
|
||||
_, ok := field.(string)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Field not a string, it's a %T!", field)
|
||||
return false, fmt.Errorf("field not a string, it's a %T", field)
|
||||
}
|
||||
|
||||
return !strings.HasPrefix(field.(string), "veh"), nil
|
||||
@@ -491,6 +491,37 @@ var testResults = []test{
|
||||
query: bolthold.Where("Category").Eq("food").Index("Category").And(bolthold.Key).Gt(testData[10].Key),
|
||||
result: []int{12, 15},
|
||||
},
|
||||
test{
|
||||
name: "Not In",
|
||||
query: bolthold.Where("Category").Not().In("food", "animal"),
|
||||
result: []int{0, 1, 3, 6, 11},
|
||||
},
|
||||
test{
|
||||
name: "Not IsNil",
|
||||
query: bolthold.Where("Tags").Not().IsNil(),
|
||||
result: []int{4, 7, 10, 12, 15},
|
||||
},
|
||||
test{
|
||||
name: "Multiple Not criteria",
|
||||
query: bolthold.Where("Tags").Not().IsNil().And("Name").Not().RegExp(regexp.MustCompile("ea")),
|
||||
result: []int{4, 7, 10, 15},
|
||||
},
|
||||
test{
|
||||
name: "Not Equal Key with not modifier",
|
||||
query: bolthold.Where(bolthold.Key).Not().Eq(testData[4].Key),
|
||||
result: []int{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
},
|
||||
test{
|
||||
name: "Double Negative", // don't do this, it's confusing
|
||||
query: bolthold.Where(bolthold.Key).Not().Not().Eq(testData[4].Key),
|
||||
result: []int{4},
|
||||
},
|
||||
test{
|
||||
name: "Not on Index",
|
||||
query: bolthold.Where("Category").Not().Eq("food").And("Category").Not().Eq("animal").
|
||||
Index("Category"),
|
||||
result: []int{0, 1, 3, 6, 11},
|
||||
},
|
||||
}
|
||||
|
||||
func insertTestData(t *testing.T, store *bolthold.Store) {
|
||||
@@ -861,7 +892,7 @@ func TestKeyMatchFunc(t *testing.T) {
|
||||
field := ra.Field()
|
||||
_, ok := field.(string)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Field not a string, it's a %T!", field)
|
||||
return false, fmt.Errorf("field not a string, it's a %T", field)
|
||||
}
|
||||
|
||||
return strings.HasPrefix(field.(string), "oat"), nil
|
||||
|
||||
@@ -288,7 +288,7 @@ func newIterator(tx *bolt.Tx, typeName string, query *Query) *iterator {
|
||||
// however if there is only one critrion and it is either > = or >= then we can seek to the value and
|
||||
// save reads
|
||||
func seekCursor(cursor *bolt.Cursor, criteria []*Criterion) (key, value []byte) {
|
||||
if len(criteria) != 1 {
|
||||
if len(criteria) != 1 || criteria[0].negate {
|
||||
return cursor.First()
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ type Criterion struct {
|
||||
operator int
|
||||
value interface{}
|
||||
inValues []interface{}
|
||||
negate bool
|
||||
}
|
||||
|
||||
func hasMatchFunc(criteria []*Criterion) bool {
|
||||
@@ -324,6 +325,12 @@ func (c *Criterion) IsNil() *Query {
|
||||
return c.op(isnil, nil)
|
||||
}
|
||||
|
||||
// Not will negate the following critierion
|
||||
func (c *Criterion) Not() *Criterion {
|
||||
c.negate = !c.negate
|
||||
return c
|
||||
}
|
||||
|
||||
// MatchFunc is a function used to test an arbitrary matching value in a query
|
||||
type MatchFunc func(ra *RecordAccess) (bool, error)
|
||||
|
||||
@@ -449,7 +456,8 @@ func matchesAllCriteria(criteria []*Criterion, value interface{}, encoded bool,
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !ok {
|
||||
|
||||
if criteria[i].negate == ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
@@ -496,6 +504,9 @@ func (q *Query) String() string {
|
||||
|
||||
func (c *Criterion) String() string {
|
||||
s := ""
|
||||
if c.negate {
|
||||
s += "NOT "
|
||||
}
|
||||
switch c.operator {
|
||||
case eq:
|
||||
s += "=="
|
||||
|
||||
Reference in New Issue
Block a user