Skip to content

Commit afd5881

Browse files
committed
Fixes #116:
- Ensure that afterNodes matchOp will realize if certain slots are actually valid or not. If the slots are not valid, then don't run the actual matchOp, which is what the non after's case is supposed to be - Also fixed a bug where after's matchOp wasn't pulling out an object fastval correctly as the implementation was not there - Fix matcher Reset() where it wipes out old slots - Added new test data
1 parent e89cc82 commit afd5881

File tree

104 files changed

+72830
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+72830
-4
lines changed

fastMatcher.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ type slotData struct {
1111
size int
1212
}
1313

14+
var emptySlotData slotData
15+
1416
type FastMatcher struct {
1517
def MatchDef
1618
slots []slotData
@@ -27,7 +29,9 @@ func NewFastMatcher(def *MatchDef) *FastMatcher {
2729
}
2830

2931
func (m *FastMatcher) Reset() {
30-
m.slots = m.slots[:0]
32+
for i := 0; i < m.def.NumSlots; i++ {
33+
m.slots[i] = emptySlotData
34+
}
3135
m.buckets.Reset()
3236
}
3337

@@ -98,6 +102,12 @@ func (m *FastMatcher) literalFromSlot(slot SlotID) FastVal {
98102
if isLiteralToken(token) {
99103
var parser fastLitParser
100104
value = parser.Parse(token, tokenData)
105+
} else if slotInfo.size > 0 {
106+
if token == tknObjectStart {
107+
value = NewObjectFastVal(m.tokens.data[slotInfo.start : slotInfo.start+slotInfo.size])
108+
} else if token == tknArrayStart {
109+
value = NewArrayFastVal(m.tokens.data[slotInfo.start : slotInfo.start+slotInfo.size])
110+
}
101111
}
102112

103113
m.tokens.Seek(savePos)
@@ -222,20 +232,38 @@ func (m *FastMatcher) matchOp(op *OpNode, litVal *FastVal) error {
222232
return nil
223233
}
224234

235+
var slotNotFound bool
225236
lhsVal := NewMissingFastVal()
226237
if op.Lhs != nil {
227238
lhsVal = m.resolveParam(op.Lhs, litVal)
239+
if _, ok := op.Lhs.(SlotRef); ok && lhsVal.IsMissing() {
240+
slotNotFound = true
241+
}
228242
} else if litVal != nil {
229243
lhsVal = *litVal
230244
}
231245

232246
rhsVal := NewMissingFastVal()
233247
if op.Rhs != nil {
234248
rhsVal = m.resolveParam(op.Rhs, litVal)
249+
if _, ok := op.Rhs.(SlotRef); ok && rhsVal.IsMissing() {
250+
slotNotFound = true
251+
}
235252
} else if litVal != nil {
236253
rhsVal = *litVal
237254
}
238255

256+
if slotNotFound {
257+
// If references are for slots and at least one wasn't found
258+
// then the matchOp should not execute
259+
m.buckets.MarkNode(bucketIdx, false)
260+
261+
if m.buckets.IsResolved(0) {
262+
return nil
263+
}
264+
return nil
265+
}
266+
239267
var opRes bool
240268
switch op.Op {
241269
case OpTypeEquals:
@@ -548,7 +576,7 @@ func (m *FastMatcher) matchExec(token tokenType, tokenData []byte, tokenDataLen
548576
}
549577
}
550578
} else if token == tknObjectStart {
551-
objStartPos := m.tokens.Position()
579+
objStartPos := m.tokens.Position() - 1 /* to include the objStart itself*/
552580
if len(node.Elems) == 0 {
553581
// If we have no element handlers, we can just skip the whole thing...
554582
m.skipValue(token)
@@ -569,6 +597,7 @@ func (m *FastMatcher) matchExec(token tokenType, tokenData []byte, tokenDataLen
569597
objEndPos := m.tokens.Position()
570598

571599
objFastVal := NewObjectFastVal(m.tokens.data[objStartPos:objEndPos])
600+
572601
for _, op := range node.Ops {
573602
err := m.matchOp(&op, &objFastVal)
574603
if err != nil {
@@ -580,7 +609,7 @@ func (m *FastMatcher) matchExec(token tokenType, tokenData []byte, tokenDataLen
580609
}
581610
}
582611
} else if token == tknArrayStart {
583-
arrayStartPos := m.tokens.Position()
612+
arrayStartPos := m.tokens.Position() - 1 // Should be -1 to include the [
584613
if len(node.Loops) == 0 {
585614
err, shouldReturn := m.matchObjectOrArray(token, tokenData, node)
586615
if shouldReturn {
@@ -699,6 +728,7 @@ func (m *FastMatcher) matchObjectOrArray(token tokenType, tokenData []byte, node
699728
if err != nil {
700729
return err, true
701730
}
731+
702732
// Keep this here to catch any empty array or empty objs
703733
if token == endToken {
704734
return nil, true

fastval.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ func (val FastVal) compareObjArrData(other FastVal) int {
438438
}
439439
}
440440

441+
// Returns compared val and boolean indicating if the comparison is valid
441442
func (val FastVal) Compare(other FastVal) int {
442443
switch val.dataType {
443444
case IntValue:
@@ -480,7 +481,6 @@ func (val FastVal) Compare(other FastVal) int {
480481
}
481482

482483
func (val FastVal) Equals(other FastVal) bool {
483-
// TODO: I doubt this logic is correct...
484484
return val.Compare(other) == 0
485485
}
486486

filterExprParser_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ package gojsonsm
44

55
import (
66
"encoding/json"
7+
"fmt"
78
"github.com/stretchr/testify/assert"
9+
"io/ioutil"
10+
"os"
811
"testing"
912
)
1013

@@ -877,3 +880,52 @@ func TestFilterExpressionParser(t *testing.T) {
877880
_, err = fe.OutputExpression()
878881
assert.NotNil(err)
879882
}
883+
884+
func readJsonHelper(fileName string) (retMap map[string]interface{}, byteSlice []byte, err error) {
885+
byteSlice, err = ioutil.ReadFile(fileName)
886+
if err != nil {
887+
return
888+
}
889+
var unmarshalledIface interface{}
890+
err = json.Unmarshal(byteSlice, &unmarshalledIface)
891+
if err != nil {
892+
return
893+
}
894+
895+
retMap = unmarshalledIface.(map[string]interface{})
896+
return
897+
}
898+
899+
func TestEdgyJson(t *testing.T) {
900+
assert := assert.New(t)
901+
dirPath := "testdata/edgyJson"
902+
edgyJsonDir, err := os.Open(dirPath)
903+
if err != nil {
904+
fmt.Printf("Error: Unable to open edgyjson directory\n")
905+
return
906+
}
907+
defer edgyJsonDir.Close()
908+
909+
var trans Transformer
910+
_, fe, err := NewFilterExpressionParser("(int>equals10000) AND (int<>1000000) OR (float IS NOT NULL)")
911+
expr, err := fe.OutputExpression()
912+
assert.Nil(err)
913+
matchDef := trans.Transform([]Expression{expr})
914+
assert.NotNil(matchDef)
915+
m := NewFastMatcher(matchDef)
916+
917+
edgyJsonList, _ := edgyJsonDir.Readdirnames(0)
918+
for _, name := range edgyJsonList {
919+
fileName := fmt.Sprintf("%s/%s", dirPath, name)
920+
byteSlice, err := ioutil.ReadFile(fileName)
921+
if err != nil {
922+
fmt.Printf("Error: Unable to read %v\n", fileName)
923+
continue
924+
}
925+
926+
matched, err := m.Match(byteSlice)
927+
assert.Nil(err)
928+
assert.False(matched)
929+
m.Reset()
930+
}
931+
}

0 commit comments

Comments
 (0)