1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-01-04 03:02:02 +02:00

Added LIKE operator (#591)

* Added LIKE operator
This commit is contained in:
Tim Voronov 2021-02-15 11:37:52 -05:00 committed by GitHub
parent 8d7f1dae23
commit f4876c05a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1203 additions and 908 deletions

1
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/corpix/uarand v0.1.1
github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9
github.com/gobwas/glob v0.2.3
github.com/gorilla/css v1.0.0
github.com/mafredri/cdp v0.30.0
github.com/natefinch/lumberjack v2.0.0+incompatible

20
go.sum
View File

@ -1,3 +1,4 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
@ -13,9 +14,11 @@ github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antlr/antlr4 v0.0.0-20200417160354-8c50731894e0 h1:j7MyDjg6pb7A2ziow17FDZ2Oj5vGnJsLyDmjpN4Jkcg=
github.com/antlr/antlr4 v0.0.0-20200417160354-8c50731894e0/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
@ -24,43 +27,55 @@ github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U=
github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9 h1:G765iDCq7bP5opdrPkXk+4V3yfkgV9iGFuheWZ/X/zY=
github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9/go.mod h1:D6ICZm05D9VN1n/8iOtBxLpXtoGp6HDFUJ1RNVieOSE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mafredri/cdp v0.30.0 h1:Lvcwjajq6wB6Uk8dYeCLrF26LG85rUdpMxgrwdEvU0o=
github.com/mafredri/cdp v0.30.0/go.mod h1:71D84qPmWUvBWYj24Zp+U69mrUof4o8qL2X1fQJ/lHc=
github.com/mafredri/go-lint v0.0.0-20180911205320-920981dfc79e/go.mod h1:k/zdyxI3q6dup24o8xpYjJKTCf2F7rfxLp6w/efTiWs=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/segmentio/encoding v0.1.10 h1:0b8dva47cSuNQR5ZcU3d0pfi9EnPpSK6q7y5ZGEW36Q=
github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/sethgrid/pester v1.1.0 h1:IyEAVvwSUPjs2ACFZkBe5N59BBUpSIkQ71Hr6cM5A+w=
github.com/sethgrid/pester v1.1.0/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns=
@ -70,6 +85,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/wI2L/jettison v0.7.1 h1:XNq/WvSOAiJhFww9F5JZZcBZtKFL2Y/9WHHEHLDq9TE=
github.com/wI2L/jettison v0.7.1/go.mod h1:dj49nOP41M7x6Jql62BqqF/+nW+XJgBaWzJR0hd6M84=
@ -104,9 +120,13 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,82 @@
package compiler_test
import (
"context"
"github.com/MontFerret/ferret/pkg/compiler"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestLikeOperator(t *testing.T) {
Convey("RETURN \"foo\" LIKE \"f*\" ", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
RETURN "foo" LIKE "f*"
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `true`)
})
Convey("RETURN LIKE('foo', 'f*')", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
RETURN LIKE('foo', 'f*')
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `true`)
})
Convey("RETURN \"foo\" NOT LIKE \"b*\" ", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
RETURN "foo" NOT LIKE "b*"
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `true`)
})
Convey("LET t = \"foo\" LIKE \"f*\" ", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
LET res = "foo" LIKE "f*"
RETURN res
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `true`)
})
Convey("FOR IN LIKE", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
FOR str IN ["foo", "bar", "qaz"]
FILTER str LIKE "*a*"
RETURN str
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `["bar","qaz"]`)
})
Convey("FOR IN LIKE 2", t, func() {
c := compiler.New()
out1, err := c.MustCompile(`
FOR str IN ["foo", "bar", "qaz"]
FILTER str LIKE "*a*"
RETURN str
`).Run(context.Background())
So(err, ShouldBeNil)
So(string(out1), ShouldEqual, `["bar","qaz"]`)
})
}

View File

@ -448,47 +448,7 @@ func (v *visitor) doVisitLimitClauseValue(ctx *fql.LimitClauseValueContext, scop
}
func (v *visitor) doVisitFilterClause(ctx *fql.FilterClauseContext, scope *scope) (core.Expression, error) {
exp := ctx.Expression().(*fql.ExpressionContext)
exps, err := v.doVisitAllExpressions(exp.AllExpression(), scope)
if err != nil {
return nil, err
}
if len(exps) == 2 {
left := exps[0]
right := exps[1]
equalityOp := exp.EqualityOperator()
if equalityOp != nil {
return operators.NewEqualityOperator(v.getSourceMap(ctx), left, right, equalityOp.GetText())
}
regexpOp := exp.RegexpOperator()
if regexpOp != nil {
return operators.NewRegexpOperator(v.getSourceMap(ctx), left, right, regexpOp.GetText())
}
logicalAndOp := exp.LogicalAndOperator()
if logicalAndOp != nil {
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, logicalAndOp.GetText())
}
logicalOrOp := exp.LogicalOrOperator()
if logicalOrOp != nil {
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, logicalOrOp.GetText())
}
} else {
// should be unary operator
return v.doVisitExpression(exp, scope)
}
return nil, core.Error(ErrInvalidToken, ctx.GetText())
return v.doVisitExpression(ctx.Expression().(*fql.ExpressionContext), scope)
}
func (v *visitor) doVisitSortClause(ctx *fql.SortClauseContext, scope *scope) ([]*clauses.SorterExpression, error) {
@ -1288,9 +1248,7 @@ func (v *visitor) doVisitMathOperator(ctx *fql.ExpressionContext, scope *scope)
)
}
func (v *visitor) doVisitUnaryOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
op := ctx.UnaryOperator().(*fql.UnaryOperatorContext)
func (v *visitor) doVisitUnaryOperator(ctx *fql.ExpressionContext, op *fql.UnaryOperatorContext, scope *scope) (core.OperatorExpression, error) {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
@ -1335,8 +1293,7 @@ func (v *visitor) doVisitLogicalOperator(ctx *fql.ExpressionContext, scope *scop
return operators.NewLogicalOperator(v.getSourceMap(ctx), left, right, operator)
}
func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
equalityOp := ctx.EqualityOperator().(*fql.EqualityOperatorContext)
func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, op *fql.EqualityOperatorContext, scope *scope) (core.OperatorExpression, error) {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
@ -1346,11 +1303,10 @@ func (v *visitor) doVisitEqualityOperator(ctx *fql.ExpressionContext, scope *sco
left := exps[0]
right := exps[1]
return operators.NewEqualityOperator(v.getSourceMap(equalityOp), left, right, equalityOp.GetText())
return operators.NewEqualityOperator(v.getSourceMap(op), left, right, op.GetText())
}
func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
regexpOp := ctx.RegexpOperator().(*fql.RegexpOperatorContext)
func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, op *fql.RegexpOperatorContext, scope *scope) (core.Expression, error) {
rawExps := ctx.AllExpression()
exps, err := v.doVisitAllExpressions(rawExps, scope)
@ -1376,7 +1332,7 @@ func (v *visitor) doVisitRegexpOperator(ctx *fql.ExpressionContext, scope *scope
return nil, errors.Wrap(errors.New("expected a string literal or a function call"), src.String())
}
return operators.NewRegexpOperator(v.getSourceMap(regexpOp), left, right, regexpOp.GetText())
return operators.NewRegexpOperator(v.getSourceMap(op), left, right, op.GetText())
}
func (v *visitor) doVisitInOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
@ -1403,16 +1359,28 @@ func (v *visitor) doVisitInOperator(ctx *fql.ExpressionContext, scope *scope) (c
)
}
func (v *visitor) doVisitLikeOperator(ctx *fql.ExpressionContext, op *fql.LikeOperatorContext, s *scope) (core.Expression, error) {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), s)
if err != nil {
return nil, err
}
left := exps[0]
right := exps[1]
return operators.NewLikeOperator(v.getSourceMap(op), left, right, op.Not() != nil)
}
func (v *visitor) doVisitArrayOperator(ctx *fql.ExpressionContext, scope *scope) (core.OperatorExpression, error) {
var comparator core.OperatorExpression
var err error
switch {
case ctx.InOperator() != nil:
if op := ctx.InOperator(); op != nil {
comparator, err = v.doVisitInOperator(ctx, scope)
case ctx.EqualityOperator() != nil:
comparator, err = v.doVisitEqualityOperator(ctx, scope)
default:
} else if op := ctx.EqualityOperator(); op != nil {
comparator, err = v.doVisitEqualityOperator(ctx, op.(*fql.EqualityOperatorContext), scope)
} else {
return nil, v.unexpectedToken(ctx)
}
@ -1459,129 +1427,91 @@ func (v *visitor) doVisitExpressionGroup(ctx *fql.ExpressionGroupContext, scope
}
func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (core.Expression, error) {
seq := ctx.ExpressionGroup()
if seq != nil {
return v.doVisitExpressionGroup(seq.(*fql.ExpressionGroupContext), scope)
if exp := ctx.ExpressionGroup(); exp != nil {
return v.doVisitExpressionGroup(exp.(*fql.ExpressionGroupContext), scope)
}
member := ctx.MemberExpression()
if member != nil {
return v.doVisitMemberExpression(member.(*fql.MemberExpressionContext), scope)
if exp := ctx.MemberExpression(); exp != nil {
return v.doVisitMemberExpression(exp.(*fql.MemberExpressionContext), scope)
}
funCall := ctx.FunctionCallExpression()
if funCall != nil {
return v.doVisitFunctionCallExpression(funCall.(*fql.FunctionCallExpressionContext), scope)
if exp := ctx.FunctionCallExpression(); exp != nil {
return v.doVisitFunctionCallExpression(exp.(*fql.FunctionCallExpressionContext), scope)
}
notOp := ctx.UnaryOperator()
if notOp != nil {
return v.doVisitUnaryOperator(ctx, scope)
if exp := ctx.UnaryOperator(); exp != nil {
return v.doVisitUnaryOperator(ctx, exp.(*fql.UnaryOperatorContext), scope)
}
multiOp := ctx.MultiplicativeOperator()
if multiOp != nil {
if exp := ctx.MultiplicativeOperator(); exp != nil {
return v.doVisitMathOperator(ctx, scope)
}
addOp := ctx.AdditiveOperator()
if addOp != nil {
if exp := ctx.AdditiveOperator(); exp != nil {
return v.doVisitMathOperator(ctx, scope)
}
arrOp := ctx.ArrayOperator()
if arrOp != nil {
if exp := ctx.ArrayOperator(); exp != nil {
return v.doVisitArrayOperator(ctx, scope)
}
equalityOp := ctx.EqualityOperator()
if equalityOp != nil {
return v.doVisitEqualityOperator(ctx, scope)
if exp := ctx.EqualityOperator(); exp != nil {
return v.doVisitEqualityOperator(ctx, exp.(*fql.EqualityOperatorContext), scope)
}
inOp := ctx.InOperator()
if inOp != nil {
if exp := ctx.InOperator(); exp != nil {
return v.doVisitInOperator(ctx, scope)
}
logicalAndOp := ctx.LogicalAndOperator()
if exp := ctx.LikeOperator(); exp != nil {
return v.doVisitLikeOperator(ctx, exp.(*fql.LikeOperatorContext), scope)
}
if logicalAndOp != nil {
if exp := ctx.LogicalAndOperator(); exp != nil {
return v.doVisitLogicalOperator(ctx, scope)
}
logicalOrOp := ctx.LogicalOrOperator()
if logicalOrOp != nil {
if exp := ctx.LogicalOrOperator(); exp != nil {
return v.doVisitLogicalOperator(ctx, scope)
}
regexpOp := ctx.RegexpOperator()
if regexpOp != nil {
return v.doVisitRegexpOperator(ctx, scope)
if exp := ctx.RegexpOperator(); exp != nil {
return v.doVisitRegexpOperator(ctx, exp.(*fql.RegexpOperatorContext), scope)
}
variable := ctx.Variable()
if variable != nil {
return v.doVisitVariable(variable.(*fql.VariableContext), scope)
if exp := ctx.Variable(); exp != nil {
return v.doVisitVariable(exp.(*fql.VariableContext), scope)
}
str := ctx.StringLiteral()
if str != nil {
return v.doVisitStringLiteral(str.(*fql.StringLiteralContext))
if exp := ctx.StringLiteral(); exp != nil {
return v.doVisitStringLiteral(exp.(*fql.StringLiteralContext))
}
integ := ctx.IntegerLiteral()
if integ != nil {
return v.doVisitIntegerLiteral(integ.(*fql.IntegerLiteralContext))
if exp := ctx.IntegerLiteral(); exp != nil {
return v.doVisitIntegerLiteral(exp.(*fql.IntegerLiteralContext))
}
float := ctx.FloatLiteral()
if float != nil {
return v.doVisitFloatLiteral(float.(*fql.FloatLiteralContext))
if exp := ctx.FloatLiteral(); exp != nil {
return v.doVisitFloatLiteral(exp.(*fql.FloatLiteralContext))
}
boolean := ctx.BooleanLiteral()
if boolean != nil {
return v.doVisitBooleanLiteral(boolean.(*fql.BooleanLiteralContext))
if exp := ctx.BooleanLiteral(); exp != nil {
return v.doVisitBooleanLiteral(exp.(*fql.BooleanLiteralContext))
}
arr := ctx.ArrayLiteral()
if arr != nil {
return v.doVisitArrayLiteral(arr.(*fql.ArrayLiteralContext), scope)
if exp := ctx.ArrayLiteral(); exp != nil {
return v.doVisitArrayLiteral(exp.(*fql.ArrayLiteralContext), scope)
}
obj := ctx.ObjectLiteral()
if obj != nil {
return v.doVisitObjectLiteral(obj.(*fql.ObjectLiteralContext), scope)
if exp := ctx.ObjectLiteral(); exp != nil {
return v.doVisitObjectLiteral(exp.(*fql.ObjectLiteralContext), scope)
}
none := ctx.NoneLiteral()
if none != nil {
return v.doVisitNoneLiteral(none.(*fql.NoneLiteralContext))
if exp := ctx.NoneLiteral(); exp != nil {
return v.doVisitNoneLiteral(exp.(*fql.NoneLiteralContext))
}
questionCtx := ctx.QuestionMark()
if questionCtx != nil {
if exp := ctx.QuestionMark(); exp != nil {
exps, err := v.doVisitAllExpressions(ctx.AllExpression(), scope)
if err != nil {
@ -1595,19 +1525,14 @@ func (v *visitor) doVisitExpression(ctx *fql.ExpressionContext, scope *scope) (c
)
}
rangeOp := ctx.RangeOperator()
if rangeOp != nil {
return v.doVisitRangeOperator(rangeOp.(*fql.RangeOperatorContext), scope)
if exp := ctx.RangeOperator(); exp != nil {
return v.doVisitRangeOperator(exp.(*fql.RangeOperatorContext), scope)
}
param := ctx.Param()
if param != nil {
if param := ctx.Param(); param != nil {
return v.doVisitParamContext(param.(*fql.ParamContext), scope)
}
// TODO: Complete it
return nil, ErrNotImplemented
}

View File

@ -55,13 +55,14 @@ Aggregate=54
Like=55
Not=56
In=57
While=58
Param=59
Identifier=60
StringLiteral=61
IntegerLiteral=62
FloatLiteral=63
NamespaceSegment=64
Do=58
While=59
Param=60
Identifier=61
StringLiteral=62
IntegerLiteral=63
FloatLiteral=64
NamespaceSegment=65
':'=5
';'=6
'.'=7
@ -109,5 +110,6 @@ NamespaceSegment=64
'AGGREGATE'=54
'LIKE'=55
'IN'=57
'WHILE'=58
'@'=59
'DO'=58
'WHILE'=59
'@'=60

View File

@ -285,6 +285,7 @@ expression
| expressionGroup
| expression arrayOperator (inOperator | equalityOperator) expression
| expression inOperator expression
| expression likeOperator expression
| expression equalityOperator expression
| expression regexpOperator expression
| expression logicalAndOperator expression
@ -320,6 +321,11 @@ inOperator
| Not In
;
likeOperator
: Like
| Not Like
;
equalityOperator
: Gt
| Lt
@ -357,5 +363,4 @@ unaryOperator
: Not
| Plus
| Minus
| Like
;

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.8. DO NOT EDIT.
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.9.1. DO NOT EDIT.
package fql
@ -268,9 +268,6 @@ var serializedLexerAtn = []uint16{
2, 3, 2,
}
var lexerDeserializer = antlr.NewATNDeserializer(nil)
var lexerAtn = lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
var lexerChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
@ -324,18 +321,20 @@ type FqlLexer struct {
// TODO: EOF string
}
var lexerDecisionToDFA = make([]*antlr.DFA, len(lexerAtn.DecisionToState))
func init() {
// NewFqlLexer produces a new lexer instance for the optional input antlr.CharStream.
//
// The *FqlLexer instance produced may be reused by calling the SetInputStream method.
// The initial lexer configuration is expensive to construct, and the object is not thread-safe;
// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
// objects can be used in a thread-safe manner.
func NewFqlLexer(input antlr.CharStream) *FqlLexer {
l := new(FqlLexer)
lexerDeserializer := antlr.NewATNDeserializer(nil)
lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState))
for index, ds := range lexerAtn.DecisionToState {
lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
}
}
func NewFqlLexer(input antlr.CharStream) *FqlLexer {
l := new(FqlLexer)
l.BaseLexer = antlr.NewBaseLexer(input)
l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
package fql // FqlParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
@ -364,6 +364,12 @@ func (s *BaseFqlParserListener) EnterInOperator(ctx *InOperatorContext) {}
// ExitInOperator is called when production inOperator is exited.
func (s *BaseFqlParserListener) ExitInOperator(ctx *InOperatorContext) {}
// EnterLikeOperator is called when production likeOperator is entered.
func (s *BaseFqlParserListener) EnterLikeOperator(ctx *LikeOperatorContext) {}
// ExitLikeOperator is called when production likeOperator is exited.
func (s *BaseFqlParserListener) ExitLikeOperator(ctx *LikeOperatorContext) {}
// EnterEqualityOperator is called when production equalityOperator is entered.
func (s *BaseFqlParserListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}

View File

@ -1,4 +1,4 @@
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
package fql // FqlParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
@ -235,6 +235,10 @@ func (v *BaseFqlParserVisitor) VisitInOperator(ctx *InOperatorContext) interface
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitLikeOperator(ctx *LikeOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFqlParserVisitor) VisitEqualityOperator(ctx *EqualityOperatorContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -1,4 +1,4 @@
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
package fql // FqlParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
@ -178,6 +178,9 @@ type FqlParserListener interface {
// EnterInOperator is called when entering the inOperator production.
EnterInOperator(c *InOperatorContext)
// EnterLikeOperator is called when entering the likeOperator production.
EnterLikeOperator(c *LikeOperatorContext)
// EnterEqualityOperator is called when entering the equalityOperator production.
EnterEqualityOperator(c *EqualityOperatorContext)
@ -370,6 +373,9 @@ type FqlParserListener interface {
// ExitInOperator is called when exiting the inOperator production.
ExitInOperator(c *InOperatorContext)
// ExitLikeOperator is called when exiting the likeOperator production.
ExitLikeOperator(c *LikeOperatorContext)
// ExitEqualityOperator is called when exiting the equalityOperator production.
ExitEqualityOperator(c *EqualityOperatorContext)

View File

@ -1,4 +1,4 @@
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
// Code generated from antlr/FqlParser.g4 by ANTLR 4.9.1. DO NOT EDIT.
package fql // FqlParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
@ -178,6 +178,9 @@ type FqlParserVisitor interface {
// Visit a parse tree produced by FqlParser#inOperator.
VisitInOperator(ctx *InOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#likeOperator.
VisitLikeOperator(ctx *LikeOperatorContext) interface{}
// Visit a parse tree produced by FqlParser#equalityOperator.
VisitEqualityOperator(ctx *EqualityOperatorContext) interface{}

View File

@ -0,0 +1,80 @@
package operators
import (
"context"
"github.com/gobwas/glob"
"github.com/pkg/errors"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
)
type LikeOperator struct {
*baseOperator
not bool
}
func NewLikeOperator(
src core.SourceMap,
left core.Expression,
right core.Expression,
not bool,
) (*LikeOperator, error) {
if left == nil {
return nil, core.Error(core.ErrMissedArgument, "left expression")
}
if right == nil {
return nil, core.Error(core.ErrMissedArgument, "right expression")
}
return &LikeOperator{&baseOperator{src, left, right}, not}, nil
}
func (operator *LikeOperator) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
left, err := operator.left.Exec(ctx, scope)
if err != nil {
return values.False, core.SourceError(operator.src, err)
}
right, err := operator.right.Exec(ctx, scope)
if err != nil {
return values.False, core.SourceError(operator.src, err)
}
return operator.Eval(ctx, left, right)
}
func (operator *LikeOperator) Eval(_ context.Context, left, right core.Value) (core.Value, error) {
err := core.ValidateType(right, types.String)
if err != nil {
// TODO: Return the error? AQL just returns false
return values.False, nil
}
err = core.ValidateType(left, types.String)
if err != nil {
// TODO: Return the error? AQL just returns false
return values.False, nil
}
r, err := glob.Compile(right.String())
if err != nil {
return nil, errors.Wrap(err, "invalid glob pattern")
}
result := r.Match(left.String())
if operator.not {
return values.NewBoolean(!result), nil
}
return values.NewBoolean(result), nil
}

View File

@ -2,12 +2,19 @@ package strings
import (
"context"
"unicode"
"regexp"
"strings"
"github.com/gobwas/glob"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
var (
deprecatedLikeSyntax = regexp.MustCompile("[%_]")
)
// LIKE checks whether the pattern search is contained in the string text, using wildcard matching.
// @param {String} str - The string to search in.
// @param {String} search - A search pattern that can contain the wildcard characters.
@ -20,46 +27,41 @@ func Like(_ context.Context, args ...core.Value) (core.Value, error) {
return values.False, err
}
str := []rune(args[0].String())
pattern := []rune(args[1].String())
str := args[0].String()
pattern := args[1].String()
if len(pattern) == 0 {
return values.NewBoolean(len(str) == 0), nil
}
lookup := make([][]bool, len(str)+1)
// TODO: Remove me in next releases
replaced := deprecatedLikeSyntax.ReplaceAllFunc([]byte(pattern), func(b []byte) []byte {
str := string(b)
for i := range lookup {
lookup[i] = make([]bool, len(pattern)+1)
}
switch str {
case "%":
return []byte("*")
case "_":
return []byte("?")
default:
return b
}
})
lookup[0][0] = true
pattern = string(replaced)
for j := 1; j < len(pattern)+1; j++ {
if pattern[j-1] == '%' {
lookup[0][j] = lookup[0][j-1]
if len(args) > 2 {
if values.ToBoolean(args[2]) {
str = strings.ToLower(str)
pattern = strings.ToLower(pattern)
}
}
for i := 1; i < len(str)+1; i++ {
for j := 1; j < len(pattern)+1; j++ {
switch {
case pattern[j-1] == '%':
lookup[i][j] = lookup[i][j-1] || lookup[i-1][j]
case pattern[j-1] == '_' || str[i-1] == pattern[j-1]:
lookup[i][j] = lookup[i-1][j-1]
case len(args) > 2:
isEq := unicode.ToLower(str[i-1]) == unicode.ToLower(pattern[j-1])
if args[2] == values.True && isEq {
lookup[i][j] = lookup[i-1][j-1]
}
default:
lookup[i][j] = false
}
}
g, err := glob.Compile(pattern)
if err != nil {
return nil, err
}
matched := lookup[len(str)][len(pattern)]
return values.NewBoolean(matched), nil
return values.NewBoolean(g.Match(str)), nil
}