Skip to content

Commit

Permalink
Merge pull request jtlabsio#2 from jtlabsio/v1.3.0
Browse files Browse the repository at this point in the history
V1.3.0
  • Loading branch information
brozeph authored May 8, 2021
2 parents e80771c + 158ffe2 commit 5df990f
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 30 deletions.
121 changes: 93 additions & 28 deletions bsonTypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)

var reWord = regexp.MustCompile(`\w+`)
var (
reNull = regexp.MustCompile(`null`)
reWord = regexp.MustCompile(`\w+`)
)

func detectDateComparisonOperator(field string, values []string) bson.M {
if len(values) == 0 {
Expand Down Expand Up @@ -86,28 +89,45 @@ func detectDateComparisonOperator(field string, values []string) bson.M {
uv = value[1:]
}

// ne
if value[0:1] == "-" {
oper = "$ne"
uv = value[1:]
}

// update value to remove the prefix
if uv != "" {
value = uv
}
}

// detect usage of keyword "null"
if reNull.MatchString(value) {
// check if there is an lt, lte, gt or gte key
if oper != "" {
return bson.M{field: bson.D{primitive.E{
Key: oper,
Value: nil,
}}}
}

// return the filter
return bson.M{field: nil}
}

// parse the date value
dv, _ := time.Parse(time.RFC3339, value)
var f interface{}

// check if there is an lt, lte, gt or gte key
if oper != "" {
f = bson.D{primitive.E{
return bson.M{field: bson.D{primitive.E{
Key: oper,
Value: dv,
}}
} else {
f = dv
}}}
}

// return the filter
return bson.M{field: f}
return bson.M{field: dv}
}

func detectNumericComparisonOperator(field string, values []string, numericType string) bson.M {
Expand Down Expand Up @@ -218,6 +238,24 @@ func detectNumericComparisonOperator(field string, values []string, numericType
}
}

if reNull.MatchString(value) {
// detect $ne operator (note use of - shorthand here which is not
// processed on numeric values that are not "null")
if value[0:1] == "-" || value[0:2] == "!=" {
oper = "$ne"
}

if oper != "" {
// return with the specified operator
return bson.M{field: bson.D{primitive.E{
Key: oper,
Value: nil,
}}}
}

return bson.M{field: nil}
}

// parse the numeric value appropriately
var parsedValue interface{}
if numericType == "decimal" || numericType == "double" {
Expand All @@ -228,7 +266,9 @@ func detectNumericComparisonOperator(field string, values []string, numericType
if bitSize == 32 {
parsedValue = float32(v)
}
} else {
}

if parsedValue == nil {
v, _ := strconv.ParseInt(value, 0, bitSize)
parsedValue = v

Expand All @@ -240,21 +280,11 @@ func detectNumericComparisonOperator(field string, values []string, numericType

// check if there is an lt, lte, gt or gte key
if oper != "" {
var clause bson.D
if numericType == "decimal" || numericType == "double" {
clause = bson.D{primitive.E{
Key: oper,
Value: parsedValue,
}}
} else {
clause = bson.D{primitive.E{
Key: oper,
Value: parsedValue,
}}
}

// return with the specified operator
return bson.M{field: clause}
return bson.M{field: bson.D{primitive.E{
Key: oper,
Value: parsedValue,
}}}
}

// no operator... just the value
Expand Down Expand Up @@ -336,51 +366,86 @@ func detectStringComparisonOperator(field string, values []string, bsonType stri
bw = value[len(value)-1:] == "*"
ew = value[0:1] == "*"
c = bw && ew
ne = value[0:1] == "-"

// adjust value when not equal...
if ne || ew {
value = value[1:]
}

if bw {
value = value[0 : len(value)-1]
}

if c {
bw = false
ew = false
}
}

// check for != or string in quotes
if len(value) > 2 {
if len(value) > 2 && !ne {
ne = value[0:2] == "!="
em = value[0:1] == "\"" &&
value[len(value)-1:] == "\""

if ne {
value = value[2:]
}

if em {
value = value[1 : len(value)-1]
}
}

// handle null keyword
if reNull.MatchString(value) {
if ne {
return bson.M{field: bson.D{primitive.E{
Key: "$ne",
Value: nil,
}}}
}

return bson.M{field: nil}
}

// not equal...
if ne {
return bson.M{field: bson.D{primitive.E{
Key: "$ne",
Value: value[2:],
Value: value,
}}}
}

// contains...
if c {
return bson.M{field: primitive.Regex{
Pattern: value[1 : len(value)-1],
Pattern: value,
Options: "i",
}}
}

// begins with...
if bw {
return bson.M{field: primitive.Regex{
Pattern: fmt.Sprintf("^%s", value[0:len(value)-1]),
Pattern: fmt.Sprintf("^%s", value),
Options: "i",
}}
}

// ends with...
if ew {
return bson.M{field: primitive.Regex{
Pattern: fmt.Sprintf("%s$", value[1:]),
Pattern: fmt.Sprintf("%s$", value),
Options: "i",
}}
}

// exact match...
if em {
return bson.M{field: primitive.Regex{
Pattern: fmt.Sprintf("^%s$", value[1:len(value)-1]),
Pattern: fmt.Sprintf("^%s$", value),
Options: "",
}}
}
Expand Down
43 changes: 41 additions & 2 deletions querybuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,12 @@ func TestQueryBuilder_Filter(t *testing.T) {
"dVal3": "date",
"dVal4": "date",
"dVal5": "date",
"dVal6": "date",
},
strictValidation: false,
},
args: args{
qs: "filter[dVal1]=<2020-01-01T12:00:00.000Z&filter[dVal2]=<=2021-02-16T02:04:05.000Z&filter[dVal3]=>2021-02-16T02:04:05.000Z&filter[dVal4]=>=2021-02-16T02:04:05.000Z&filter[dVal5]=!=2020-01-01T12:00:00.000Z",
qs: "filter[dVal1]=<2020-01-01T12:00:00.000Z&filter[dVal2]=<=2021-02-16T02:04:05.000Z&filter[dVal3]=>2021-02-16T02:04:05.000Z&filter[dVal4]=>=2021-02-16T02:04:05.000Z&filter[dVal5]=!=2020-01-01T12:00:00.000Z&filter[dVal6]=-2020-01-01T12:00:00.000Z",
},
want: bson.M{
"dVal1": bson.D{primitive.E{
Expand All @@ -358,6 +359,10 @@ func TestQueryBuilder_Filter(t *testing.T) {
Key: "$ne",
Value: time.Date(2020, time.January, 1, 12, 0, 0, 0, time.UTC),
}},
"dVal6": bson.D{primitive.E{
Key: "$ne",
Value: time.Date(2020, time.January, 1, 12, 0, 0, 0, time.UTC),
}},
},
wantErr: false,
},
Expand Down Expand Up @@ -440,11 +445,12 @@ func TestQueryBuilder_Filter(t *testing.T) {
"sVal4": "string",
"sVal5": "string",
"sVal6": "string",
"sVal7": "string",
},
strictValidation: false,
},
args: args{
qs: "filter[sVal1]=*value&filter[sVal2]=value*&filter[sVal3]=*value*&filter[sVal4]=value&filter[sVal5]=!=value&filter[sVal6]=\"value\"",
qs: "filter[sVal1]=*value&filter[sVal2]=value*&filter[sVal3]=*value*&filter[sVal4]=value&filter[sVal5]=!=value&filter[sVal6]=\"value\"&filter[sVal7]=-value",
},
want: bson.M{
"sVal1": primitive.Regex{
Expand All @@ -468,6 +474,39 @@ func TestQueryBuilder_Filter(t *testing.T) {
Pattern: "^value$",
Options: "",
},
"sVal7": bson.D{primitive.E{
Key: "$ne",
Value: "value",
}},
},
wantErr: false,
},
{
name: "should properly handle null keyword in searches",
fields: fields{
collection: "test",
fieldTypes: map[string]string{
"sVal1": "string",
"nVal1": "int",
"dVal1": "date",
"sVal2": "string",
},
strictValidation: false,
},
args: args{
qs: "filter[sVal1]=null&filter[nVal1]=-null&filter[dVal1]=null&filter[sVal2]=-null",
},
want: bson.M{
"sVal1": nil,
"nVal1": bson.D{primitive.E{
Key: "$ne",
Value: nil,
}},
"dVal1": nil,
"sVal2": bson.D{primitive.E{
Key: "$ne",
Value: nil,
}},
},
wantErr: false,
},
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ For `string` bsonType fields in the schema, the following operators can be lever
* `not equal` (i.e. `{ "name": { "$ne": "term" } }`): `?filter[name]=!=term`
* `in` (i.e. `{ "name": { "$in": [ ... ] } }`): `?filter[name]=term1,term2,term3,term4`
* standard comparison (i.e. `{ "name": "term" }`): `?filter[name]=term`
* `null` is translated to `null` in the query (i.e. `{ 'name': null }`): `?filter[name]=null`

*numeric bsonType*

Expand Down

0 comments on commit 5df990f

Please sign in to comment.