Commit 925e9788 authored by Johnny's avatar Johnny

feat: support pinned factor

parent 90119c1a
......@@ -12,6 +12,7 @@ var MemoFilterCELAttributes = []cel.EnvOption{
// As the built-in timestamp type is deprecated, we use string type for now.
// e.g., "2021-01-01T00:00:00Z"
cel.Variable("create_time", cel.StringType),
cel.Variable("pinned", cel.BoolType),
cel.Variable("tag", cel.StringType),
cel.Variable("update_time", cel.StringType),
cel.Variable("visibility", cel.StringType),
......
......@@ -211,6 +211,9 @@ message UserStats {
// Format: "tag1": 1, "tag2": 2
map<string, int32> tag_count = 4;
// The pinned memos of the user.
repeated string pinned_memos = 5;
message MemoTypeStats {
int32 link_count = 1;
int32 code_count = 2;
......
This diff is collapsed.
......@@ -3272,6 +3272,11 @@ definitions:
title: |-
The count of tags.
Format: "tag1": 1, "tag2": 2
pinnedMemos:
type: array
items:
type: string
description: The pinned memos of the user.
v1Visibility:
type: string
enum:
......
......@@ -108,7 +108,6 @@ func (s *APIV1Service) ListMemos(ctx context.Context, request *v1pb.ListMemosReq
return nil, status.Errorf(codes.InvalidArgument, "invalid parent: %v", err)
}
memoFind.CreatorID = &userID
memoFind.OrderByPinned = true
}
if request.State == v1pb.State_ARCHIVED {
state := store.Archived
......
......@@ -62,6 +62,9 @@ func (s *APIV1Service) ListAllUserStats(ctx context.Context, _ *v1pb.ListAllUser
for _, tag := range memo.Payload.Tags {
userStats.TagCount[tag]++
}
if memo.Pinned {
userStats.PinnedMemos = append(userStats.PinnedMemos, fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID))
}
if memo.Payload.Property.GetHasLink() {
userStats.MemoTypeStats.LinkCount++
}
......@@ -140,6 +143,9 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
for _, tag := range memo.Payload.Tags {
userStats.TagCount[tag]++
}
if memo.Pinned {
userStats.PinnedMemos = append(userStats.PinnedMemos, fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID))
}
if memo.Payload.Property.GetHasLink() {
userStats.MemoTypeStats.LinkCount++
}
......
......@@ -87,6 +87,9 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
}
where = append(where, fmt.Sprintf("`memo`.`visibility` in (%s)", strings.Join(placeholder, ",")))
}
if v := find.Pinned; v != nil {
where, args = append(where, "`memo`.`pinned` = ?"), append(args, *v)
}
if v := find.PayloadFind; v != nil {
if v.Raw != nil {
where, args = append(where, "`memo`.`payload` = ?"), append(args, *v.Raw)
......@@ -131,14 +134,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
having = append(having, "`parent_id` IS NULL")
}
orders := []string{}
if find.OrderByPinned {
orders = append(orders, "`pinned` DESC")
}
order := "DESC"
if find.OrderByTimeAsc {
order = "ASC"
}
orders := []string{}
if find.OrderByUpdatedTs {
orders = append(orders, "`updated_ts` "+order)
} else {
......
......@@ -188,6 +188,16 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
}
ctx.Args = append(ctx.Args, fmt.Sprintf("%%%s%%", arg))
}
} else if v, ok := expr.ExprKind.(*exprv1.Expr_IdentExpr); ok {
identifier := v.IdentExpr.GetName()
if !slices.Contains([]string{"pinned"}, identifier) {
return errors.Errorf("invalid identifier for %s", identifier)
}
if identifier == "pinned" {
if _, err := ctx.Buffer.WriteString("`memo`.`pinned` IS TRUE"); err != nil {
return err
}
}
}
return nil
}
......@@ -49,6 +49,16 @@ func TestConvertExprToSQL(t *testing.T) {
want: "(JSON_CONTAINS(JSON_EXTRACT(`memo`.`payload`, '$.tags'), ?) OR `memo`.`content` LIKE ?)",
args: []any{"tag1", "%hello%"},
},
{
filter: `1`,
want: "",
args: []any{},
},
{
filter: `pinned`,
want: "`memo`.`pinned` IS TRUE",
args: []any{},
},
}
for _, tt := range tests {
......
......@@ -78,6 +78,9 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
}
where = append(where, fmt.Sprintf("memo.visibility in (%s)", strings.Join(holders, ", ")))
}
if v := find.Pinned; v != nil {
where, args = append(where, "memo.pinned = "+placeholder(len(args)+1)), append(args, *v)
}
if v := find.PayloadFind; v != nil {
if v.Raw != nil {
where, args = append(where, "memo.payload = "+placeholder(len(args)+1)), append(args, *v.Raw)
......@@ -123,14 +126,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
where = append(where, "memo_relation.related_memo_id IS NULL")
}
orders := []string{}
if find.OrderByPinned {
orders = append(orders, "pinned DESC")
}
order := "DESC"
if find.OrderByTimeAsc {
order = "ASC"
}
orders := []string{}
if find.OrderByUpdatedTs {
orders = append(orders, "updated_ts "+order)
} else {
......
......@@ -188,6 +188,16 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
}
ctx.Args = append(ctx.Args, fmt.Sprintf("%%%s%%", arg))
}
} else if v, ok := expr.ExprKind.(*exprv1.Expr_IdentExpr); ok {
identifier := v.IdentExpr.GetName()
if !slices.Contains([]string{"pinned"}, identifier) {
return errors.Errorf("invalid identifier %s", identifier)
}
if identifier == "pinned" {
if _, err := ctx.Buffer.WriteString("memo.pinned IS TRUE"); err != nil {
return err
}
}
}
return nil
}
......@@ -49,6 +49,16 @@ func TestRestoreExprToSQL(t *testing.T) {
want: "(memo.payload->'tags' @> $1::jsonb OR memo.content ILIKE $2)",
args: []any{[]any{"tag1"}, "%hello%"},
},
{
filter: `1`,
want: "",
args: []any{},
},
{
filter: `pinned`,
want: "memo.pinned IS TRUE",
args: []any{},
},
}
for _, tt := range tests {
......
......@@ -79,6 +79,9 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
}
where = append(where, fmt.Sprintf("`memo`.`visibility` IN (%s)", strings.Join(placeholder, ",")))
}
if v := find.Pinned; v != nil {
where, args = append(where, "`memo`.`pinned` = ?"), append(args, *v)
}
if v := find.PayloadFind; v != nil {
if v.Raw != nil {
where, args = append(where, "`memo`.`payload` = ?"), append(args, *v.Raw)
......@@ -123,14 +126,11 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
where = append(where, "`parent_id` IS NULL")
}
orderBy := []string{}
if find.OrderByPinned {
orderBy = append(orderBy, "`pinned` DESC")
}
order := "DESC"
if find.OrderByTimeAsc {
order = "ASC"
}
orderBy := []string{}
if find.OrderByUpdatedTs {
orderBy = append(orderBy, "`updated_ts` "+order)
} else {
......
......@@ -188,6 +188,16 @@ func (d *DB) ConvertExprToSQL(ctx *filter.ConvertContext, expr *exprv1.Expr) err
}
ctx.Args = append(ctx.Args, fmt.Sprintf("%%%s%%", arg))
}
} else if v, ok := expr.ExprKind.(*exprv1.Expr_IdentExpr); ok {
identifier := v.IdentExpr.GetName()
if !slices.Contains([]string{"pinned"}, identifier) {
return errors.Errorf("invalid identifier %s", identifier)
}
if identifier == "pinned" {
if _, err := ctx.Buffer.WriteString("`memo`.`pinned` IS TRUE"); err != nil {
return err
}
}
}
return nil
}
......@@ -59,6 +59,16 @@ func TestConvertExprToSQL(t *testing.T) {
want: "",
args: []any{},
},
{
filter: `pinned`,
want: "`memo`.`pinned` IS TRUE",
args: []any{},
},
{
filter: `!pinned`,
want: "NOT (`memo`.`pinned` IS TRUE)",
args: []any{},
},
}
for _, tt := range tests {
......
......@@ -70,6 +70,7 @@ type FindMemo struct {
// Domain specific fields
ContentSearch []string
VisibilityList []Visibility
Pinned *bool
PayloadFind *FindMemoPayload
ExcludeContent bool
ExcludeComments bool
......@@ -81,7 +82,6 @@ type FindMemo struct {
// Ordering
OrderByUpdatedTs bool
OrderByPinned bool
OrderByTimeAsc bool
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment