Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
de3e55c2
Commit
de3e55c2
authored
May 28, 2025
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support `now()` time functions
parent
f5ecb66f
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
149 additions
and
61 deletions
+149
-61
expr.go
plugin/filter/expr.go
+88
-0
filter.go
plugin/filter/filter.go
+16
-4
memo_filter.go
store/db/mysql/memo_filter.go
+10
-15
memo_filter_test.go
store/db/mysql/memo_filter_test.go
+6
-5
memo_filter.go
store/db/postgres/memo_filter.go
+9
-14
memo_filter_test.go
store/db/postgres/memo_filter_test.go
+6
-5
memo_filter.go
store/db/sqlite/memo_filter.go
+8
-13
memo_filter_test.go
store/db/sqlite/memo_filter_test.go
+6
-5
No files found.
plugin/filter/expr.go
View file @
de3e55c2
...
@@ -2,6 +2,7 @@ package filter
...
@@ -2,6 +2,7 @@ package filter
import
(
import
(
"errors"
"errors"
"time"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
)
...
@@ -37,3 +38,90 @@ func GetIdentExprName(expr *exprv1.Expr) (string, error) {
...
@@ -37,3 +38,90 @@ func GetIdentExprName(expr *exprv1.Expr) (string, error) {
}
}
return
expr
.
GetIdentExpr
()
.
GetName
(),
nil
return
expr
.
GetIdentExpr
()
.
GetName
(),
nil
}
}
// GetFunctionValue evaluates CEL function calls and returns their value.
// This is specifically for time functions like now().
func
GetFunctionValue
(
expr
*
exprv1
.
Expr
)
(
any
,
error
)
{
callExpr
,
ok
:=
expr
.
ExprKind
.
(
*
exprv1
.
Expr_CallExpr
)
if
!
ok
{
return
nil
,
errors
.
New
(
"invalid function call expression"
)
}
switch
callExpr
.
CallExpr
.
Function
{
case
"now"
:
if
len
(
callExpr
.
CallExpr
.
Args
)
!=
0
{
return
nil
,
errors
.
New
(
"now() function takes no arguments"
)
}
return
time
.
Now
()
.
Unix
(),
nil
case
"_-_"
:
// Handle subtraction for expressions like "now() - 60 * 60 * 24"
if
len
(
callExpr
.
CallExpr
.
Args
)
!=
2
{
return
nil
,
errors
.
New
(
"subtraction requires exactly two arguments"
)
}
left
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
0
])
if
err
!=
nil
{
return
nil
,
err
}
right
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
return
nil
,
err
}
leftInt
,
ok1
:=
left
.
(
int64
)
rightInt
,
ok2
:=
right
.
(
int64
)
if
!
ok1
||
!
ok2
{
return
nil
,
errors
.
New
(
"subtraction operands must be integers"
)
}
return
leftInt
-
rightInt
,
nil
case
"_*_"
:
// Handle multiplication for expressions like "60 * 60 * 24"
if
len
(
callExpr
.
CallExpr
.
Args
)
!=
2
{
return
nil
,
errors
.
New
(
"multiplication requires exactly two arguments"
)
}
left
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
0
])
if
err
!=
nil
{
return
nil
,
err
}
right
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
return
nil
,
err
}
leftInt
,
ok1
:=
left
.
(
int64
)
rightInt
,
ok2
:=
right
.
(
int64
)
if
!
ok1
||
!
ok2
{
return
nil
,
errors
.
New
(
"multiplication operands must be integers"
)
}
return
leftInt
*
rightInt
,
nil
case
"_+_"
:
// Handle addition
if
len
(
callExpr
.
CallExpr
.
Args
)
!=
2
{
return
nil
,
errors
.
New
(
"addition requires exactly two arguments"
)
}
left
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
0
])
if
err
!=
nil
{
return
nil
,
err
}
right
,
err
:=
GetExprValue
(
callExpr
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
return
nil
,
err
}
leftInt
,
ok1
:=
left
.
(
int64
)
rightInt
,
ok2
:=
right
.
(
int64
)
if
!
ok1
||
!
ok2
{
return
nil
,
errors
.
New
(
"addition operands must be integers"
)
}
return
leftInt
+
rightInt
,
nil
default
:
return
nil
,
errors
.
New
(
"unsupported function: "
+
callExpr
.
CallExpr
.
Function
)
}
}
// GetExprValue attempts to get a value from an expression, trying constants first, then functions.
func
GetExprValue
(
expr
*
exprv1
.
Expr
)
(
any
,
error
)
{
// Try to get constant value first
if
constValue
,
err
:=
GetConstValue
(
expr
);
err
==
nil
{
return
constValue
,
nil
}
// If not a constant, try to evaluate as a function
return
GetFunctionValue
(
expr
)
}
plugin/filter/filter.go
View file @
de3e55c2
package
filter
package
filter
import
(
import
(
"time"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/pkg/errors"
"github.com/pkg/errors"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
)
...
@@ -10,14 +14,22 @@ import (
...
@@ -10,14 +14,22 @@ import (
var
MemoFilterCELAttributes
=
[]
cel
.
EnvOption
{
var
MemoFilterCELAttributes
=
[]
cel
.
EnvOption
{
cel
.
Variable
(
"content"
,
cel
.
StringType
),
cel
.
Variable
(
"content"
,
cel
.
StringType
),
cel
.
Variable
(
"creator_id"
,
cel
.
IntType
),
cel
.
Variable
(
"creator_id"
,
cel
.
IntType
),
// As the built-in timestamp type is deprecated, we use string type for now.
cel
.
Variable
(
"created_ts"
,
cel
.
IntType
),
// e.g., "2021-01-01T00:00:00Z"
cel
.
Variable
(
"updated_ts"
,
cel
.
IntType
),
cel
.
Variable
(
"create_time"
,
cel
.
StringType
),
cel
.
Variable
(
"pinned"
,
cel
.
BoolType
),
cel
.
Variable
(
"pinned"
,
cel
.
BoolType
),
cel
.
Variable
(
"tag"
,
cel
.
StringType
),
cel
.
Variable
(
"tag"
,
cel
.
StringType
),
cel
.
Variable
(
"update_time"
,
cel
.
StringType
),
cel
.
Variable
(
"visibility"
,
cel
.
StringType
),
cel
.
Variable
(
"visibility"
,
cel
.
StringType
),
cel
.
Variable
(
"has_task_list"
,
cel
.
BoolType
),
cel
.
Variable
(
"has_task_list"
,
cel
.
BoolType
),
// Current timestamp function.
cel
.
Function
(
"now"
,
cel
.
Overload
(
"now"
,
[]
*
cel
.
Type
{},
cel
.
IntType
,
cel
.
FunctionBinding
(
func
(
args
...
ref
.
Val
)
ref
.
Val
{
return
types
.
Int
(
time
.
Now
()
.
Unix
())
}),
),
),
}
}
// Parse parses the filter string and returns the parsed expression.
// Parse parses the filter string and returns the parsed expression.
...
...
store/db/mysql/memo_filter.go
View file @
de3e55c2
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"fmt"
"fmt"
"slices"
"slices"
"strings"
"strings"
"time"
"github.com/pkg/errors"
"github.com/pkg/errors"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
_time"
,
"update_time
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
d_ts"
,
"updated_ts
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
}
}
value
,
err
:=
filter
.
Get
Const
Value
(
v
.
CallExpr
.
Args
[
1
])
value
,
err
:=
filter
.
Get
Expr
Value
(
v
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
operator
=
">="
operator
=
">="
}
}
if
identifier
==
"create
_time"
||
identifier
==
"update_time
"
{
if
identifier
==
"create
d_ts"
||
identifier
==
"updated_ts
"
{
timestamp
Str
,
ok
:=
value
.
(
string
)
timestamp
Int
,
ok
:=
value
.
(
int64
)
if
!
ok
{
if
!
ok
{
return
errors
.
New
(
"invalid timestamp value"
)
return
errors
.
New
(
"invalid timestamp value"
)
}
}
timestamp
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
timestampStr
)
if
err
!=
nil
{
return
errors
.
Wrap
(
err
,
"failed to parse timestamp"
)
}
var
factor
string
var
factor
string
if
identifier
==
"create
_time
"
{
if
identifier
==
"create
d_ts
"
{
factor
=
"
`memo`.`created_ts`
"
factor
=
"
UNIX_TIMESTAMP(`memo`.`created_ts`)
"
}
else
if
identifier
==
"update
_time
"
{
}
else
if
identifier
==
"update
d_ts
"
{
factor
=
"
`memo`.`updated_ts`
"
factor
=
"
UNIX_TIMESTAMP(`memo`.`updated_ts`)
"
}
}
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"
UNIX_TIMESTAMP(%s)
%s ?"
,
factor
,
operator
));
err
!=
nil
{
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"
%s
%s ?"
,
factor
,
operator
));
err
!=
nil
{
return
err
return
err
}
}
ctx
.
Args
=
append
(
ctx
.
Args
,
timestamp
.
Unix
()
)
ctx
.
Args
=
append
(
ctx
.
Args
,
timestamp
Int
)
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
if
operator
!=
"="
&&
operator
!=
"!="
{
if
operator
!=
"="
&&
operator
!=
"!="
{
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
...
...
store/db/mysql/memo_filter_test.go
View file @
de3e55c2
...
@@ -2,6 +2,7 @@ package mysql
...
@@ -2,6 +2,7 @@ package mysql
import
(
import
(
"testing"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -39,11 +40,6 @@ func TestConvertExprToSQL(t *testing.T) {
...
@@ -39,11 +40,6 @@ func TestConvertExprToSQL(t *testing.T) {
want
:
"`memo`.`visibility` IN (?,?)"
,
want
:
"`memo`.`visibility` IN (?,?)"
,
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
},
},
{
filter
:
`create_time == "2006-01-02T15:04:05+07:00"`
,
want
:
"UNIX_TIMESTAMP(`memo`.`created_ts`) = ?"
,
args
:
[]
any
{
int64
(
1136189045
)},
},
{
{
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
want
:
"(JSON_CONTAINS(JSON_EXTRACT(`memo`.`payload`, '$.tags'), ?) OR `memo`.`content` LIKE ?)"
,
want
:
"(JSON_CONTAINS(JSON_EXTRACT(`memo`.`payload`, '$.tags'), ?) OR `memo`.`content` LIKE ?)"
,
...
@@ -94,6 +90,11 @@ func TestConvertExprToSQL(t *testing.T) {
...
@@ -94,6 +90,11 @@ func TestConvertExprToSQL(t *testing.T) {
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.property.hasTaskList') = CAST('true' AS JSON) AND `memo`.`content` LIKE ?)"
,
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.property.hasTaskList') = CAST('true' AS JSON) AND `memo`.`content` LIKE ?)"
,
args
:
[]
any
{
"%todo%"
},
args
:
[]
any
{
"%todo%"
},
},
},
{
filter
:
`created_ts > now() - 60 * 60 * 24`
,
want
:
"UNIX_TIMESTAMP(`memo`.`created_ts`) > ?"
,
args
:
[]
any
{
time
.
Now
()
.
Unix
()
-
60
*
60
*
24
},
},
}
}
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
...
...
store/db/postgres/memo_filter.go
View file @
de3e55c2
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"fmt"
"fmt"
"slices"
"slices"
"strings"
"strings"
"time"
"github.com/pkg/errors"
"github.com/pkg/errors"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
_time"
,
"update_time
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
d_ts"
,
"updated_ts
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
}
}
value
,
err
:=
filter
.
Get
Const
Value
(
v
.
CallExpr
.
Args
[
1
])
value
,
err
:=
filter
.
Get
Expr
Value
(
v
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
operator
=
">="
operator
=
">="
}
}
if
identifier
==
"create
_time"
||
identifier
==
"update_time
"
{
if
identifier
==
"create
d_ts"
||
identifier
==
"updated_ts
"
{
timestamp
Str
,
ok
:=
value
.
(
string
)
timestamp
Int
,
ok
:=
value
.
(
int64
)
if
!
ok
{
if
!
ok
{
return
errors
.
New
(
"invalid timestamp value"
)
return
errors
.
New
(
"invalid timestamp value"
)
}
}
timestamp
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
timestampStr
)
if
err
!=
nil
{
return
errors
.
Wrap
(
err
,
"failed to parse timestamp"
)
}
var
factor
string
var
factor
string
if
identifier
==
"create
_time
"
{
if
identifier
==
"create
d_ts
"
{
factor
=
"
memo.created_ts
"
factor
=
"
EXTRACT(EPOCH FROM memo.created_ts)
"
}
else
if
identifier
==
"update
_time
"
{
}
else
if
identifier
==
"update
d_ts
"
{
factor
=
"
memo.updated_ts
"
factor
=
"
EXTRACT(EPOCH FROM memo.updated_ts)
"
}
}
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"%s %s %s"
,
factor
,
operator
,
placeholder
(
len
(
ctx
.
Args
)
+
ctx
.
ArgsOffset
+
1
)));
err
!=
nil
{
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"%s %s %s"
,
factor
,
operator
,
placeholder
(
len
(
ctx
.
Args
)
+
ctx
.
ArgsOffset
+
1
)));
err
!=
nil
{
return
err
return
err
}
}
ctx
.
Args
=
append
(
ctx
.
Args
,
timestamp
.
Unix
()
)
ctx
.
Args
=
append
(
ctx
.
Args
,
timestamp
Int
)
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
if
operator
!=
"="
&&
operator
!=
"!="
{
if
operator
!=
"="
&&
operator
!=
"!="
{
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
...
...
store/db/postgres/memo_filter_test.go
View file @
de3e55c2
...
@@ -2,6 +2,7 @@ package postgres
...
@@ -2,6 +2,7 @@ package postgres
import
(
import
(
"testing"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -39,11 +40,6 @@ func TestRestoreExprToSQL(t *testing.T) {
...
@@ -39,11 +40,6 @@ func TestRestoreExprToSQL(t *testing.T) {
want
:
"memo.visibility IN ($1,$2)"
,
want
:
"memo.visibility IN ($1,$2)"
,
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
},
},
{
filter
:
`create_time == "2006-01-02T15:04:05+07:00"`
,
want
:
"memo.created_ts = $1"
,
args
:
[]
any
{
int64
(
1136189045
)},
},
{
{
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
want
:
"(memo.payload->'tags' @> jsonb_build_array($1) OR memo.content ILIKE $2)"
,
want
:
"(memo.payload->'tags' @> jsonb_build_array($1) OR memo.content ILIKE $2)"
,
...
@@ -94,6 +90,11 @@ func TestRestoreExprToSQL(t *testing.T) {
...
@@ -94,6 +90,11 @@ func TestRestoreExprToSQL(t *testing.T) {
want
:
"((memo.payload->'property'->>'hasTaskList')::boolean IS TRUE AND memo.content ILIKE $1)"
,
want
:
"((memo.payload->'property'->>'hasTaskList')::boolean IS TRUE AND memo.content ILIKE $1)"
,
args
:
[]
any
{
"%todo%"
},
args
:
[]
any
{
"%todo%"
},
},
},
{
filter
:
`created_ts > now() - 60 * 60 * 24`
,
want
:
"EXTRACT(EPOCH FROM memo.created_ts) > $1"
,
args
:
[]
any
{
time
.
Now
()
.
Unix
()
-
60
*
60
*
24
},
},
}
}
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
...
...
store/db/sqlite/memo_filter.go
View file @
de3e55c2
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"fmt"
"fmt"
"slices"
"slices"
"strings"
"strings"
"time"
"github.com/pkg/errors"
"github.com/pkg/errors"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
exprv1
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -59,10 +58,10 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
_time"
,
"update_time
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
if
!
slices
.
Contains
([]
string
{
"creator_id"
,
"create
d_ts"
,
"updated_ts
"
,
"visibility"
,
"content"
,
"has_task_list"
},
identifier
)
{
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid identifier for %s"
,
v
.
CallExpr
.
Function
)
}
}
value
,
err
:=
filter
.
Get
Const
Value
(
v
.
CallExpr
.
Args
[
1
])
value
,
err
:=
filter
.
Get
Expr
Value
(
v
.
CallExpr
.
Args
[
1
])
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
...
@@ -82,26 +81,22 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
operator
=
">="
operator
=
">="
}
}
if
identifier
==
"create
_time"
||
identifier
==
"update_time
"
{
if
identifier
==
"create
d_ts"
||
identifier
==
"updated_ts
"
{
timestampStr
,
ok
:=
value
.
(
string
)
valueInt
,
ok
:=
value
.
(
int64
)
if
!
ok
{
if
!
ok
{
return
errors
.
New
(
"invalid timestamp value"
)
return
errors
.
New
(
"invalid integer timestamp value"
)
}
timestamp
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
timestampStr
)
if
err
!=
nil
{
return
errors
.
Wrap
(
err
,
"failed to parse timestamp"
)
}
}
var
factor
string
var
factor
string
if
identifier
==
"create
_time
"
{
if
identifier
==
"create
d_ts
"
{
factor
=
"`memo`.`created_ts`"
factor
=
"`memo`.`created_ts`"
}
else
if
identifier
==
"update
_time
"
{
}
else
if
identifier
==
"update
d_ts
"
{
factor
=
"`memo`.`updated_ts`"
factor
=
"`memo`.`updated_ts`"
}
}
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"%s %s ?"
,
factor
,
operator
));
err
!=
nil
{
if
_
,
err
:=
ctx
.
Buffer
.
WriteString
(
fmt
.
Sprintf
(
"%s %s ?"
,
factor
,
operator
));
err
!=
nil
{
return
err
return
err
}
}
ctx
.
Args
=
append
(
ctx
.
Args
,
timestamp
.
Unix
()
)
ctx
.
Args
=
append
(
ctx
.
Args
,
valueInt
)
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
}
else
if
identifier
==
"visibility"
||
identifier
==
"content"
{
if
operator
!=
"="
&&
operator
!=
"!="
{
if
operator
!=
"="
&&
operator
!=
"!="
{
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
return
errors
.
Errorf
(
"invalid operator for %s"
,
v
.
CallExpr
.
Function
)
...
...
store/db/sqlite/memo_filter_test.go
View file @
de3e55c2
...
@@ -2,6 +2,7 @@ package sqlite
...
@@ -2,6 +2,7 @@ package sqlite
import
(
import
(
"testing"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -44,11 +45,6 @@ func TestConvertExprToSQL(t *testing.T) {
...
@@ -44,11 +45,6 @@ func TestConvertExprToSQL(t *testing.T) {
want
:
"`memo`.`visibility` IN (?,?)"
,
want
:
"`memo`.`visibility` IN (?,?)"
,
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
args
:
[]
any
{
"PUBLIC"
,
"PRIVATE"
},
},
},
{
filter
:
`create_time == "2006-01-02T15:04:05+07:00"`
,
want
:
"`memo`.`created_ts` = ?"
,
args
:
[]
any
{
int64
(
1136189045
)},
},
{
{
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
filter
:
`tag in ['tag1'] || content.contains('hello')`
,
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.tags') LIKE ? OR `memo`.`content` LIKE ?)"
,
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.tags') LIKE ? OR `memo`.`content` LIKE ?)"
,
...
@@ -109,6 +105,11 @@ func TestConvertExprToSQL(t *testing.T) {
...
@@ -109,6 +105,11 @@ func TestConvertExprToSQL(t *testing.T) {
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.property.hasTaskList') IS TRUE AND `memo`.`content` LIKE ?)"
,
want
:
"(JSON_EXTRACT(`memo`.`payload`, '$.property.hasTaskList') IS TRUE AND `memo`.`content` LIKE ?)"
,
args
:
[]
any
{
"%todo%"
},
args
:
[]
any
{
"%todo%"
},
},
},
{
filter
:
`created_ts > now() - 60 * 60 * 24`
,
want
:
"`memo`.`created_ts` > ?"
,
args
:
[]
any
{
time
.
Now
()
.
Unix
()
-
60
*
60
*
24
},
},
}
}
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment