From 988568695132ababf6de85257cd3c43f4d2b04b3 Mon Sep 17 00:00:00 2001 From: KaWaite Date: Mon, 22 Nov 2021 18:39:22 +0900 Subject: [PATCH 01/18] wip: asset filtering --- internal/adapter/gql/generated.go | 67 ++++++++++++++----- internal/adapter/gql/gqlmodel/models_gen.go | 49 ++++++++++++++ internal/adapter/gql/loader_asset.go | 10 ++- internal/adapter/gql/resolver_query.go | 4 +- internal/adapter/gql/resolver_team.go | 2 +- internal/infrastructure/memory/asset.go | 3 +- internal/infrastructure/mongo/asset.go | 28 ++++++-- internal/infrastructure/mongo/dataset.go | 2 +- .../infrastructure/mongo/dataset_schema.go | 2 +- .../infrastructure/mongo/mongodoc/client.go | 5 +- .../mongo/mongodoc/clientcol.go | 5 +- internal/infrastructure/mongo/project.go | 2 +- internal/usecase/interactor/asset.go | 4 +- internal/usecase/interfaces/asset.go | 13 +++- internal/usecase/repo/asset.go | 3 +- schema.graphql | 10 +++ 16 files changed, 172 insertions(+), 37 deletions(-) diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 5247cea3..bb4f5031 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -752,7 +752,7 @@ type ComplexityRoot struct { } Query struct { - Assets func(childComplexity int, teamID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int + Assets func(childComplexity int, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int CheckProjectAlias func(childComplexity int, alias string) int DatasetSchemas func(childComplexity int, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int Datasets func(childComplexity int, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int @@ -1248,7 +1248,7 @@ type QueryResolver interface { Plugins(ctx context.Context, id []*id.PluginID) ([]*gqlmodel.Plugin, error) Layer(ctx context.Context, id id.ID) (gqlmodel.Layer, error) Scene(ctx context.Context, projectID id.ID) (*gqlmodel.Scene, error) - Assets(ctx context.Context, teamID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) + Assets(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) Projects(ctx context.Context, teamID id.ID, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) DatasetSchemas(ctx context.Context, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetSchemaConnection, error) Datasets(ctx context.Context, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetConnection, error) @@ -4822,7 +4822,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true + return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["filter"].(*gqlmodel.AssetFilterType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true case "Query.checkProjectAlias": if e.complexity.Query.CheckProjectAlias == nil { @@ -6153,6 +6153,15 @@ type Asset implements Node { team: Team @goField(forceResolver: true) } +enum AssetFilterType { + DATE + SIZE + NAME + REVERSE_DATE + REVERSE_SIZE + REVERSE_NAME +} + # User type User implements Node { @@ -7513,6 +7522,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! + filter: AssetFilterType first: Int last: Int after: Cursor @@ -8786,42 +8796,51 @@ func (ec *executionContext) field_Query_assets_args(ctx context.Context, rawArgs } } args["teamId"] = arg0 - var arg1 *int + var arg1 *gqlmodel.AssetFilterType + if tmp, ok := rawArgs["filter"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + arg1, err = ec.unmarshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx, tmp) + if err != nil { + return nil, err + } + } + args["filter"] = arg1 + var arg2 *int if tmp, ok := rawArgs["first"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) - arg1, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["first"] = arg1 - var arg2 *int + args["first"] = arg2 + var arg3 *int if tmp, ok := rawArgs["last"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) - arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["last"] = arg2 - var arg3 *usecase.Cursor + args["last"] = arg3 + var arg4 *usecase.Cursor if tmp, ok := rawArgs["after"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) - arg3, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) + arg4, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) if err != nil { return nil, err } } - args["after"] = arg3 - var arg4 *usecase.Cursor + args["after"] = arg4 + var arg5 *usecase.Cursor if tmp, ok := rawArgs["before"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) - arg4, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) + arg5, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) if err != nil { return nil, err } } - args["before"] = arg4 + args["before"] = arg5 return args, nil } @@ -25271,7 +25290,7 @@ func (ec *executionContext) _Query_assets(ctx context.Context, field graphql.Col fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) + return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["filter"].(*gqlmodel.AssetFilterType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) }) if err != nil { ec.Error(ctx, err) @@ -43364,6 +43383,22 @@ func (ec *executionContext) marshalOAsset2ᚖgithubᚗcomᚋreearthᚋreearthᚑ return ec._Asset(ctx, sel, v) } +func (ec *executionContext) unmarshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx context.Context, v interface{}) (*gqlmodel.AssetFilterType, error) { + if v == nil { + return nil, nil + } + var res = new(gqlmodel.AssetFilterType) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx context.Context, sel ast.SelectionSet, v *gqlmodel.AssetFilterType) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) marshalOAttachTagItemToGroupPayload2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAttachTagItemToGroupPayload(ctx context.Context, sel ast.SelectionSet, v *gqlmodel.AttachTagItemToGroupPayload) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/internal/adapter/gql/gqlmodel/models_gen.go b/internal/adapter/gql/gqlmodel/models_gen.go index af39ce5a..05711a71 100644 --- a/internal/adapter/gql/gqlmodel/models_gen.go +++ b/internal/adapter/gql/gqlmodel/models_gen.go @@ -1320,6 +1320,55 @@ type WidgetZone struct { Right *WidgetSection `json:"right"` } +type AssetFilterType string + +const ( + AssetFilterTypeDate AssetFilterType = "DATE" + AssetFilterTypeSize AssetFilterType = "SIZE" + AssetFilterTypeName AssetFilterType = "NAME" + AssetFilterTypeReverseDate AssetFilterType = "REVERSE_DATE" + AssetFilterTypeReverseSize AssetFilterType = "REVERSE_SIZE" + AssetFilterTypeReverseName AssetFilterType = "REVERSE_NAME" +) + +var AllAssetFilterType = []AssetFilterType{ + AssetFilterTypeDate, + AssetFilterTypeSize, + AssetFilterTypeName, + AssetFilterTypeReverseDate, + AssetFilterTypeReverseSize, + AssetFilterTypeReverseName, +} + +func (e AssetFilterType) IsValid() bool { + switch e { + case AssetFilterTypeDate, AssetFilterTypeSize, AssetFilterTypeName, AssetFilterTypeReverseDate, AssetFilterTypeReverseSize, AssetFilterTypeReverseName: + return true + } + return false +} + +func (e AssetFilterType) String() string { + return string(e) +} + +func (e *AssetFilterType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = AssetFilterType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid AssetFilterType", str) + } + return nil +} + +func (e AssetFilterType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + type LayerEncodingFormat string const ( diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 936af519..50e9cd4c 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -2,6 +2,7 @@ package gql import ( "context" + "fmt" "github.com/reearth/reearth-backend/internal/adapter/gql/gqldataloader" "github.com/reearth/reearth-backend/internal/adapter/gql/gqlmodel" @@ -32,9 +33,14 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. return assets, nil } -func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { p := usecase.NewPagination(first, last, before, after) - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), p, getOperator(ctx)) + var f interfaces.AssetFilterType + if filter != nil { + f = interfaces.AssetFilterType(*filter) + } + fmt.Println(f) + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), &f, p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/adapter/gql/resolver_query.go b/internal/adapter/gql/resolver_query.go index 5d8775c7..74b8a49d 100644 --- a/internal/adapter/gql/resolver_query.go +++ b/internal/adapter/gql/resolver_query.go @@ -14,11 +14,11 @@ func (r *Resolver) Query() QueryResolver { type queryResolver struct{ *Resolver } -func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { exit := trace(ctx) defer exit() - return r.loaders.Asset.FindByTeam(ctx, teamID, first, last, before, after) + return r.loaders.Asset.FindByTeam(ctx, teamID, filter, first, last, before, after) } func (r *queryResolver) Me(ctx context.Context) (*gqlmodel.User, error) { diff --git a/internal/adapter/gql/resolver_team.go b/internal/adapter/gql/resolver_team.go index 3501bb73..b3ac22bd 100644 --- a/internal/adapter/gql/resolver_team.go +++ b/internal/adapter/gql/resolver_team.go @@ -22,7 +22,7 @@ func (r *teamResolver) Assets(ctx context.Context, obj *gqlmodel.Team, first *in exit := trace(ctx) defer exit() - return r.loaders.Asset.FindByTeam(ctx, obj.ID, first, last, before, after) + return r.loaders.Asset.FindByTeam(ctx, obj.ID, nil, first, last, before, after) } func (r *teamResolver) Projects(ctx context.Context, obj *gqlmodel.Team, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) { diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index 9bde832c..1a7f6934 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/reearth/reearth-backend/internal/usecase" + "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" @@ -66,7 +67,7 @@ func (r *Asset) Remove(ctx context.Context, id id.AssetID) error { return nil } -func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, sortFilter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 2f5e93fa..744cef24 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -4,9 +4,11 @@ import ( "context" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "github.com/reearth/reearth-backend/internal/infrastructure/mongo/mongodoc" "github.com/reearth/reearth-backend/internal/usecase" + "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" @@ -52,11 +54,27 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { return r.client.RemoveOne(ctx, id.String()) } -func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { - filter := bson.D{ +func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, filter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { + var sortFilter primitive.E + filter2 := *filter + switch filter2 { + case interfaces.AssetFilterName: + sortFilter = primitive.E{Key: "name", Value: 1} + case interfaces.AssetFilterSize: + sortFilter = primitive.E{Key: "size", Value: 1} + case interfaces.AssetFilterReverseName: + sortFilter = primitive.E{Key: "name", Value: -1} + case interfaces.AssetFilterReverseSize: + sortFilter = primitive.E{Key: "size", Value: -1} + case interfaces.AssetFilterReverseDate: + sortFilter = primitive.E{Key: "createdat", Value: -1} + default: + sortFilter = primitive.E{Key: "createdat", Value: 1} + } + f := bson.D{ {Key: "team", Value: id.String()}, } - return r.paginate(ctx, filter, pagination) + return r.paginate(ctx, f, sortFilter, pagination) } func (r *assetRepo) init() { @@ -66,9 +84,9 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sortFilter bson.E, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, &sortFilter, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/dataset.go b/internal/infrastructure/mongo/dataset.go index 1b8e1e1a..6378fb8d 100644 --- a/internal/infrastructure/mongo/dataset.go +++ b/internal/infrastructure/mongo/dataset.go @@ -287,7 +287,7 @@ func (r *datasetRepo) RemoveByScene(ctx context.Context, sceneID id.SceneID) err func (r *datasetRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) (dataset.List, *usecase.PageInfo, error) { var c mongodoc.DatasetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/dataset_schema.go b/internal/infrastructure/mongo/dataset_schema.go index e4887edb..cb91ee4e 100644 --- a/internal/infrastructure/mongo/dataset_schema.go +++ b/internal/infrastructure/mongo/dataset_schema.go @@ -148,7 +148,7 @@ func (r *datasetSchemaRepo) findOne(ctx context.Context, filter bson.D) (*datase func (r *datasetSchemaRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) ([]*dataset.Schema, *usecase.PageInfo, error) { var c mongodoc.DatasetSchemaConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index c959875b..48972a7c 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -164,7 +164,7 @@ func getCursor(raw bson.Raw, key string) (*usecase.Cursor, error) { return &c, nil } -func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sortFilter *bson.E, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { if p == nil { return nil, nil } @@ -180,6 +180,9 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, p reverse := false var limit int64 findOptions := options.Find() + if sortFilter != nil { + findOptions.Sort = appendE(findOptions.Sort, *sortFilter) + } if first := p.First; first != nil { limit = int64(*first) findOptions.Sort = bson.D{ diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index 21916b4c..4c1fca42 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -4,6 +4,7 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) @@ -28,8 +29,8 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 return c.Client.Count(ctx, c.CollectionName, filter) } -func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { - return c.Client.Paginate(ctx, c.CollectionName, filter, p, consumer) +func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sortFilter *bson.E, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { + return c.Client.Paginate(ctx, c.CollectionName, filter, sortFilter, p, consumer) } func (c *ClientCollection) SaveOne(ctx context.Context, id string, replacement interface{}) error { diff --git a/internal/infrastructure/mongo/project.go b/internal/infrastructure/mongo/project.go index 1ebc566d..72cf5414 100644 --- a/internal/infrastructure/mongo/project.go +++ b/internal/infrastructure/mongo/project.go @@ -115,7 +115,7 @@ func (r *projectRepo) findOne(ctx context.Context, filter bson.D) (*project.Proj func (r *projectRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) ([]*project.Project, *usecase.PageInfo, error) { var c mongodoc.ProjectConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/usecase/interactor/asset.go b/internal/usecase/interactor/asset.go index 9c5e9e72..5ed9a720 100644 --- a/internal/usecase/interactor/asset.go +++ b/internal/usecase/interactor/asset.go @@ -34,12 +34,12 @@ func (i *Asset) Fetch(ctx context.Context, assets []id.AssetID, operator *usecas return i.assetRepo.FindByIDs(ctx, assets, operator.ReadableTeams) } -func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { +func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, filter *interfaces.AssetFilterType, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { if err := i.CanReadTeam(tid, operator); err != nil { return nil, nil, err } - return i.assetRepo.FindByTeam(ctx, tid, p) + return i.assetRepo.FindByTeam(ctx, tid, filter, p) } func (i *Asset) Create(ctx context.Context, inp interfaces.CreateAssetParam, operator *usecase.Operator) (result *asset.Asset, err error) { diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index 57a43e22..fd2fa0d1 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -10,6 +10,17 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) +type AssetFilterType string + +const ( + AssetFilterDate AssetFilterType = "DATE" + AssetFilterSize AssetFilterType = "SIZE" + AssetFilterName AssetFilterType = "NAME" + AssetFilterReverseDate AssetFilterType = "REVERSE_DATE" + AssetFilterReverseSize AssetFilterType = "REVERSE_SIZE" + AssetFilterReverseName AssetFilterType = "REVERSE_NAME" +) + type CreateAssetParam struct { TeamID id.TeamID File *file.File @@ -21,7 +32,7 @@ var ( type Asset interface { Fetch(context.Context, []id.AssetID, *usecase.Operator) ([]*asset.Asset, error) - FindByTeam(context.Context, id.TeamID, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *AssetFilterType, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, error) Remove(context.Context, id.AssetID, *usecase.Operator) (id.AssetID, error) } diff --git a/internal/usecase/repo/asset.go b/internal/usecase/repo/asset.go index 7648fd5c..e7f8e28e 100644 --- a/internal/usecase/repo/asset.go +++ b/internal/usecase/repo/asset.go @@ -4,6 +4,7 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" + "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" ) @@ -11,7 +12,7 @@ import ( type Asset interface { Save(context.Context, *asset.Asset) error Remove(context.Context, id.AssetID) error - FindByTeam(context.Context, id.TeamID, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *interfaces.AssetFilterType, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) FindByID(context.Context, id.AssetID, []id.TeamID) (*asset.Asset, error) FindByIDs(context.Context, []id.AssetID, []id.TeamID) ([]*asset.Asset, error) } diff --git a/schema.graphql b/schema.graphql index 43a4aae5..964175d6 100644 --- a/schema.graphql +++ b/schema.graphql @@ -144,6 +144,15 @@ type Asset implements Node { team: Team @goField(forceResolver: true) } +enum AssetFilterType { + DATE + SIZE + NAME + REVERSE_DATE + REVERSE_SIZE + REVERSE_NAME +} + # User type User implements Node { @@ -1504,6 +1513,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! + filter: AssetFilterType first: Int last: Int after: Cursor From dbb176f8e973adb184d9ba9117a3a391ed680194 Mon Sep 17 00:00:00 2001 From: KaWaite Date: Thu, 25 Nov 2021 17:07:50 +0900 Subject: [PATCH 02/18] asset filtering --- internal/adapter/gql/generated.go | 3 -- internal/adapter/gql/gqlmodel/models_gen.go | 14 +++----- internal/infrastructure/mongo/asset.go | 23 ++----------- .../infrastructure/mongo/mongodoc/client.go | 34 +++++++++---------- .../mongo/mongodoc/clientcol.go | 4 +-- internal/usecase/interfaces/asset.go | 9 ++--- pkg/plugin/manifest/schema_gen.go | 2 +- schema.graphql | 3 -- 8 files changed, 30 insertions(+), 62 deletions(-) diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 47f68023..0a9f8546 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -6291,9 +6291,6 @@ enum AssetFilterType { DATE SIZE NAME - REVERSE_DATE - REVERSE_SIZE - REVERSE_NAME } # User diff --git a/internal/adapter/gql/gqlmodel/models_gen.go b/internal/adapter/gql/gqlmodel/models_gen.go index dc44039f..5c60a158 100644 --- a/internal/adapter/gql/gqlmodel/models_gen.go +++ b/internal/adapter/gql/gqlmodel/models_gen.go @@ -1363,26 +1363,20 @@ type WidgetZone struct { type AssetFilterType string const ( - AssetFilterTypeDate AssetFilterType = "DATE" - AssetFilterTypeSize AssetFilterType = "SIZE" - AssetFilterTypeName AssetFilterType = "NAME" - AssetFilterTypeReverseDate AssetFilterType = "REVERSE_DATE" - AssetFilterTypeReverseSize AssetFilterType = "REVERSE_SIZE" - AssetFilterTypeReverseName AssetFilterType = "REVERSE_NAME" + AssetFilterTypeDate AssetFilterType = "DATE" + AssetFilterTypeSize AssetFilterType = "SIZE" + AssetFilterTypeName AssetFilterType = "NAME" ) var AllAssetFilterType = []AssetFilterType{ AssetFilterTypeDate, AssetFilterTypeSize, AssetFilterTypeName, - AssetFilterTypeReverseDate, - AssetFilterTypeReverseSize, - AssetFilterTypeReverseName, } func (e AssetFilterType) IsValid() bool { switch e { - case AssetFilterTypeDate, AssetFilterTypeSize, AssetFilterTypeName, AssetFilterTypeReverseDate, AssetFilterTypeReverseSize, AssetFilterTypeReverseName: + case AssetFilterTypeDate, AssetFilterTypeSize, AssetFilterTypeName: return true } return false diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 744cef24..5ce34bdd 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -4,7 +4,6 @@ import ( "context" "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" "github.com/reearth/reearth-backend/internal/infrastructure/mongo/mongodoc" "github.com/reearth/reearth-backend/internal/usecase" @@ -55,26 +54,10 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { } func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, filter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { - var sortFilter primitive.E - filter2 := *filter - switch filter2 { - case interfaces.AssetFilterName: - sortFilter = primitive.E{Key: "name", Value: 1} - case interfaces.AssetFilterSize: - sortFilter = primitive.E{Key: "size", Value: 1} - case interfaces.AssetFilterReverseName: - sortFilter = primitive.E{Key: "name", Value: -1} - case interfaces.AssetFilterReverseSize: - sortFilter = primitive.E{Key: "size", Value: -1} - case interfaces.AssetFilterReverseDate: - sortFilter = primitive.E{Key: "createdat", Value: -1} - default: - sortFilter = primitive.E{Key: "createdat", Value: 1} - } f := bson.D{ {Key: "team", Value: id.String()}, } - return r.paginate(ctx, f, sortFilter, pagination) + return r.paginate(ctx, f, filter, pagination) } func (r *assetRepo) init() { @@ -84,9 +67,9 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sortFilter bson.E, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sortFilter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, &sortFilter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, sortFilter, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 48972a7c..a71b4e29 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -7,6 +7,7 @@ import ( "io" "github.com/reearth/reearth-backend/internal/usecase" + "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/rerror" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -164,44 +165,50 @@ func getCursor(raw bson.Raw, key string) (*usecase.Cursor, error) { return &c, nil } -func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sortFilter *bson.E, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sortFilter *interfaces.AssetFilterType, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { if p == nil { return nil, nil } coll := c.Collection(col) key := "id" + if sortFilter != nil { + sortFilter2 := *sortFilter + switch sortFilter2 { + case interfaces.AssetFilterName: + key = "name" + case interfaces.AssetFilterSize: + key = "size" + } + } count, err := coll.CountDocuments(ctx, filter) if err != nil { return nil, fmt.Errorf("failed to count documents: %v", err.Error()) } - reverse := false var limit int64 findOptions := options.Find() - if sortFilter != nil { - findOptions.Sort = appendE(findOptions.Sort, *sortFilter) - } + findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) + if first := p.First; first != nil { limit = int64(*first) findOptions.Sort = bson.D{ {Key: key, Value: 1}, } if after := p.After; after != nil { - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ + filter = appendE(filter, bson.E{Key: "id", Value: bson.D{ {Key: "$gt", Value: *after}, }}) } } if last := p.Last; last != nil { - reverse = true limit = int64(*last) findOptions.Sort = bson.D{ {Key: key, Value: -1}, } if before := p.Before; before != nil { - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ + filter = appendE(filter, bson.E{Key: "id", Value: bson.D{ {Key: "$lt", Value: *before}, }}) } @@ -236,13 +243,6 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s results = results[:len(results)-1] } - if reverse { - for i := len(results)/2 - 1; i >= 0; i-- { - opp := len(results) - 1 - i - results[i], results[opp] = results[opp], results[i] - } - } - for _, result := range results { if err := consumer.Consume(result); err != nil { return nil, err @@ -251,12 +251,12 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s var startCursor, endCursor *usecase.Cursor if len(results) > 0 { - sc, err := getCursor(results[0], key) + sc, err := getCursor(results[0], "id") if err != nil { return nil, fmt.Errorf("failed to get start cursor: %v", err.Error()) } startCursor = sc - ec, err := getCursor(results[len(results)-1], key) + ec, err := getCursor(results[len(results)-1], "id") if err != nil { return nil, fmt.Errorf("failed to get end cursor: %v", err.Error()) } diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index 4c1fca42..f313a7eb 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -4,7 +4,7 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" - "go.mongodb.org/mongo-driver/bson" + "github.com/reearth/reearth-backend/internal/usecase/interfaces" "go.mongodb.org/mongo-driver/mongo" ) @@ -29,7 +29,7 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 return c.Client.Count(ctx, c.CollectionName, filter) } -func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sortFilter *bson.E, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sortFilter *interfaces.AssetFilterType, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { return c.Client.Paginate(ctx, c.CollectionName, filter, sortFilter, p, consumer) } diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index fd2fa0d1..ce35d539 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -13,12 +13,9 @@ import ( type AssetFilterType string const ( - AssetFilterDate AssetFilterType = "DATE" - AssetFilterSize AssetFilterType = "SIZE" - AssetFilterName AssetFilterType = "NAME" - AssetFilterReverseDate AssetFilterType = "REVERSE_DATE" - AssetFilterReverseSize AssetFilterType = "REVERSE_SIZE" - AssetFilterReverseName AssetFilterType = "REVERSE_NAME" + AssetFilterDate AssetFilterType = "DATE" + AssetFilterSize AssetFilterType = "SIZE" + AssetFilterName AssetFilterType = "NAME" ) type CreateAssetParam struct { diff --git a/pkg/plugin/manifest/schema_gen.go b/pkg/plugin/manifest/schema_gen.go index db3c4e3b..31f0073c 100644 --- a/pkg/plugin/manifest/schema_gen.go +++ b/pkg/plugin/manifest/schema_gen.go @@ -1,6 +1,6 @@ package manifest -// generated by "/var/folders/lz/nhqy382n28g31wb4f_40gbmc0000gp/T/go-build612118365/b001/exe/schematyper -o schema_gen.go --package manifest ../../../plugin_manifest_schema.json" -- DO NOT EDIT +// generated by "/var/folders/jg/1nqjhdbs6png0lqrfy2g9nq80000gn/T/go-build3063292847/b001/exe/schematyper -o schema_gen.go --package manifest ../../../plugin_manifest_schema.json" -- DO NOT EDIT type Choice struct { Icon string `json:"icon,omitempty"` diff --git a/schema.graphql b/schema.graphql index 947ccb02..1123949a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -148,9 +148,6 @@ enum AssetFilterType { DATE SIZE NAME - REVERSE_DATE - REVERSE_SIZE - REVERSE_NAME } # User From a776b49d9b5163a074a929e344c8d2f617fd2179 Mon Sep 17 00:00:00 2001 From: KaWaite Date: Fri, 26 Nov 2021 11:46:58 +0900 Subject: [PATCH 03/18] refactoring --- internal/adapter/gql/loader_asset.go | 30 ++++++++++--- internal/infrastructure/memory/asset.go | 4 +- internal/infrastructure/mongo/asset.go | 12 +++--- .../infrastructure/mongo/mongodoc/client.go | 43 +++++++------------ .../mongo/mongodoc/clientcol.go | 6 +-- internal/usecase/interactor/asset.go | 5 ++- internal/usecase/interfaces/asset.go | 3 +- internal/usecase/repo/asset.go | 4 +- 8 files changed, 59 insertions(+), 48 deletions(-) diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 50e9cd4c..09cffda9 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -2,13 +2,14 @@ package gql import ( "context" - "fmt" "github.com/reearth/reearth-backend/internal/adapter/gql/gqldataloader" "github.com/reearth/reearth-backend/internal/adapter/gql/gqlmodel" "github.com/reearth/reearth-backend/internal/usecase" "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/id" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" ) type AssetLoader struct { @@ -35,12 +36,31 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { p := usecase.NewPagination(first, last, before, after) - var f interfaces.AssetFilterType + + findOptions := options.Find() + findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) + + sortType := "id" if filter != nil { - f = interfaces.AssetFilterType(*filter) + switch *filter { + case gqlmodel.AssetFilterTypeName: + sortType = "name" + case gqlmodel.AssetFilterTypeSize: + sortType = "size" + } } - fmt.Println(f) - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), &f, p, getOperator(ctx)) + + if first != nil { + findOptions.Sort = bson.D{ + {Key: sortType, Value: 1}, + } + } else if last != nil { + findOptions.Sort = bson.D{ + {Key: sortType, Value: -1}, + } + } + + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), findOptions, p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index 1a7f6934..84a54355 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -5,11 +5,11 @@ import ( "sync" "github.com/reearth/reearth-backend/internal/usecase" - "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/rerror" + "go.mongodb.org/mongo-driver/mongo/options" ) type Asset struct { @@ -67,7 +67,7 @@ func (r *Asset) Remove(ctx context.Context, id id.AssetID) error { return nil } -func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, sortFilter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 5ce34bdd..98e89604 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -4,10 +4,10 @@ import ( "context" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/reearth/reearth-backend/internal/infrastructure/mongo/mongodoc" "github.com/reearth/reearth-backend/internal/usecase" - "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" @@ -53,11 +53,11 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { return r.client.RemoveOne(ctx, id.String()) } -func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, filter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { - f := bson.D{ +func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { + filter := bson.D{ {Key: "team", Value: id.String()}, } - return r.paginate(ctx, f, filter, pagination) + return r.paginate(ctx, filter, findOptions, pagination) } func (r *assetRepo) init() { @@ -67,9 +67,9 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sortFilter *interfaces.AssetFilterType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.D, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, sortFilter, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, findOptions, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index a71b4e29..84c75433 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -7,7 +7,6 @@ import ( "io" "github.com/reearth/reearth-backend/internal/usecase" - "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/rerror" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -165,22 +164,13 @@ func getCursor(raw bson.Raw, key string) (*usecase.Cursor, error) { return &c, nil } -func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sortFilter *interfaces.AssetFilterType, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, findOptions *options.FindOptions, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { if p == nil { return nil, nil } coll := c.Collection(col) key := "id" - if sortFilter != nil { - sortFilter2 := *sortFilter - switch sortFilter2 { - case interfaces.AssetFilterName: - key = "name" - case interfaces.AssetFilterSize: - key = "size" - } - } count, err := coll.CountDocuments(ctx, filter) if err != nil { @@ -188,37 +178,36 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s } var limit int64 - findOptions := options.Find() - findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) if first := p.First; first != nil { limit = int64(*first) - findOptions.Sort = bson.D{ - {Key: key, Value: 1}, - } if after := p.After; after != nil { - filter = appendE(filter, bson.E{Key: "id", Value: bson.D{ + filter = appendE(filter, bson.E{Key: key, Value: bson.D{ {Key: "$gt", Value: *after}, }}) } - } - if last := p.Last; last != nil { + } else if last := p.Last; last != nil { limit = int64(*last) - findOptions.Sort = bson.D{ - {Key: key, Value: -1}, - } if before := p.Before; before != nil { - filter = appendE(filter, bson.E{Key: "id", Value: bson.D{ + filter = appendE(filter, bson.E{Key: key, Value: bson.D{ {Key: "$lt", Value: *before}, }}) } } + + var findOptions2 *options.FindOptions + if findOptions != nil { + findOptions2 = findOptions + } else { + findOptions2 = options.Find() + } + // 更に読める要素があるのか確かめるために一つ多めに読み出す // Read one more element so that we can see whether there's a further one limit++ - findOptions.Limit = &limit + findOptions2.Limit = &limit - cursor, err := coll.Find(ctx, filter, findOptions) + cursor, err := coll.Find(ctx, filter, findOptions2) if err != nil { return nil, fmt.Errorf("failed to find: %v", err.Error()) } @@ -251,12 +240,12 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s var startCursor, endCursor *usecase.Cursor if len(results) > 0 { - sc, err := getCursor(results[0], "id") + sc, err := getCursor(results[0], key) if err != nil { return nil, fmt.Errorf("failed to get start cursor: %v", err.Error()) } startCursor = sc - ec, err := getCursor(results[len(results)-1], "id") + ec, err := getCursor(results[len(results)-1], key) if err != nil { return nil, fmt.Errorf("failed to get end cursor: %v", err.Error()) } diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index f313a7eb..db977c3b 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -4,8 +4,8 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" - "github.com/reearth/reearth-backend/internal/usecase/interfaces" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" ) type ClientCollection struct { @@ -29,8 +29,8 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 return c.Client.Count(ctx, c.CollectionName, filter) } -func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sortFilter *interfaces.AssetFilterType, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { - return c.Client.Paginate(ctx, c.CollectionName, filter, sortFilter, p, consumer) +func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, findOptions *options.FindOptions, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { + return c.Client.Paginate(ctx, c.CollectionName, filter, findOptions, p, consumer) } func (c *ClientCollection) SaveOne(ctx context.Context, id string, replacement interface{}) error { diff --git a/internal/usecase/interactor/asset.go b/internal/usecase/interactor/asset.go index 5ed9a720..3d19c9ce 100644 --- a/internal/usecase/interactor/asset.go +++ b/internal/usecase/interactor/asset.go @@ -11,6 +11,7 @@ import ( "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" + "go.mongodb.org/mongo-driver/mongo/options" ) type Asset struct { @@ -34,12 +35,12 @@ func (i *Asset) Fetch(ctx context.Context, assets []id.AssetID, operator *usecas return i.assetRepo.FindByIDs(ctx, assets, operator.ReadableTeams) } -func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, filter *interfaces.AssetFilterType, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { +func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, findOptions *options.FindOptions, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { if err := i.CanReadTeam(tid, operator); err != nil { return nil, nil, err } - return i.assetRepo.FindByTeam(ctx, tid, filter, p) + return i.assetRepo.FindByTeam(ctx, tid, findOptions, p) } func (i *Asset) Create(ctx context.Context, inp interfaces.CreateAssetParam, operator *usecase.Operator) (result *asset.Asset, err error) { diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index ce35d539..b9f2adc2 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -8,6 +8,7 @@ import ( "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/file" "github.com/reearth/reearth-backend/pkg/id" + "go.mongodb.org/mongo-driver/mongo/options" ) type AssetFilterType string @@ -29,7 +30,7 @@ var ( type Asset interface { Fetch(context.Context, []id.AssetID, *usecase.Operator) ([]*asset.Asset, error) - FindByTeam(context.Context, id.TeamID, *AssetFilterType, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *options.FindOptions, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, error) Remove(context.Context, id.AssetID, *usecase.Operator) (id.AssetID, error) } diff --git a/internal/usecase/repo/asset.go b/internal/usecase/repo/asset.go index e7f8e28e..f452e9c8 100644 --- a/internal/usecase/repo/asset.go +++ b/internal/usecase/repo/asset.go @@ -4,15 +4,15 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" - "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" + "go.mongodb.org/mongo-driver/mongo/options" ) type Asset interface { Save(context.Context, *asset.Asset) error Remove(context.Context, id.AssetID) error - FindByTeam(context.Context, id.TeamID, *interfaces.AssetFilterType, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *options.FindOptions, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) FindByID(context.Context, id.AssetID, []id.TeamID) (*asset.Asset, error) FindByIDs(context.Context, []id.AssetID, []id.TeamID) ([]*asset.Asset, error) } From e136f57c345ffc86d569238b4c5dd0b1e53e4c28 Mon Sep 17 00:00:00 2001 From: KaWaite Date: Fri, 26 Nov 2021 11:53:03 +0900 Subject: [PATCH 04/18] fix naming --- internal/adapter/gql/generated.go | 28 +++++++++---------- internal/adapter/gql/gqlmodel/models_gen.go | 30 ++++++++++----------- internal/adapter/gql/loader_asset.go | 20 +++++++------- internal/adapter/gql/resolver_query.go | 4 +-- pkg/plugin/manifest/schema_gen.go | 2 +- schema.graphql | 4 +-- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 0a9f8546..39c071af 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -766,7 +766,7 @@ type ComplexityRoot struct { } Query struct { - Assets func(childComplexity int, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int + Assets func(childComplexity int, teamID id.ID, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int CheckProjectAlias func(childComplexity int, alias string) int DatasetSchemas func(childComplexity int, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int Datasets func(childComplexity int, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int @@ -1276,7 +1276,7 @@ type QueryResolver interface { Plugins(ctx context.Context, id []*id.PluginID) ([]*gqlmodel.Plugin, error) Layer(ctx context.Context, id id.ID) (gqlmodel.Layer, error) Scene(ctx context.Context, projectID id.ID) (*gqlmodel.Scene, error) - Assets(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) + Assets(ctx context.Context, teamID id.ID, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) Projects(ctx context.Context, teamID id.ID, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) DatasetSchemas(ctx context.Context, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetSchemaConnection, error) Datasets(ctx context.Context, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetConnection, error) @@ -4921,7 +4921,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["filter"].(*gqlmodel.AssetFilterType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true + return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true case "Query.checkProjectAlias": if e.complexity.Query.CheckProjectAlias == nil { @@ -6287,7 +6287,7 @@ type Asset implements Node { team: Team @goField(forceResolver: true) } -enum AssetFilterType { +enum AssetSortType { DATE SIZE NAME @@ -7693,7 +7693,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! - filter: AssetFilterType + sort: AssetSortType first: Int last: Int after: Cursor @@ -9015,15 +9015,15 @@ func (ec *executionContext) field_Query_assets_args(ctx context.Context, rawArgs } } args["teamId"] = arg0 - var arg1 *gqlmodel.AssetFilterType - if tmp, ok := rawArgs["filter"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - arg1, err = ec.unmarshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx, tmp) + var arg1 *gqlmodel.AssetSortType + if tmp, ok := rawArgs["sort"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort")) + arg1, err = ec.unmarshalOAssetSortType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetSortType(ctx, tmp) if err != nil { return nil, err } } - args["filter"] = arg1 + args["sort"] = arg1 var arg2 *int if tmp, ok := rawArgs["first"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) @@ -25801,7 +25801,7 @@ func (ec *executionContext) _Query_assets(ctx context.Context, field graphql.Col fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["filter"].(*gqlmodel.AssetFilterType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) + return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) }) if err != nil { ec.Error(ctx, err) @@ -44406,16 +44406,16 @@ func (ec *executionContext) marshalOAsset2ᚖgithubᚗcomᚋreearthᚋreearthᚑ return ec._Asset(ctx, sel, v) } -func (ec *executionContext) unmarshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx context.Context, v interface{}) (*gqlmodel.AssetFilterType, error) { +func (ec *executionContext) unmarshalOAssetSortType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetSortType(ctx context.Context, v interface{}) (*gqlmodel.AssetSortType, error) { if v == nil { return nil, nil } - var res = new(gqlmodel.AssetFilterType) + var res = new(gqlmodel.AssetSortType) err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalOAssetFilterType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetFilterType(ctx context.Context, sel ast.SelectionSet, v *gqlmodel.AssetFilterType) graphql.Marshaler { +func (ec *executionContext) marshalOAssetSortType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetSortType(ctx context.Context, sel ast.SelectionSet, v *gqlmodel.AssetSortType) graphql.Marshaler { if v == nil { return graphql.Null } diff --git a/internal/adapter/gql/gqlmodel/models_gen.go b/internal/adapter/gql/gqlmodel/models_gen.go index 5c60a158..f3765117 100644 --- a/internal/adapter/gql/gqlmodel/models_gen.go +++ b/internal/adapter/gql/gqlmodel/models_gen.go @@ -1360,46 +1360,46 @@ type WidgetZone struct { Right *WidgetSection `json:"right"` } -type AssetFilterType string +type AssetSortType string const ( - AssetFilterTypeDate AssetFilterType = "DATE" - AssetFilterTypeSize AssetFilterType = "SIZE" - AssetFilterTypeName AssetFilterType = "NAME" + AssetSortTypeDate AssetSortType = "DATE" + AssetSortTypeSize AssetSortType = "SIZE" + AssetSortTypeName AssetSortType = "NAME" ) -var AllAssetFilterType = []AssetFilterType{ - AssetFilterTypeDate, - AssetFilterTypeSize, - AssetFilterTypeName, +var AllAssetSortType = []AssetSortType{ + AssetSortTypeDate, + AssetSortTypeSize, + AssetSortTypeName, } -func (e AssetFilterType) IsValid() bool { +func (e AssetSortType) IsValid() bool { switch e { - case AssetFilterTypeDate, AssetFilterTypeSize, AssetFilterTypeName: + case AssetSortTypeDate, AssetSortTypeSize, AssetSortTypeName: return true } return false } -func (e AssetFilterType) String() string { +func (e AssetSortType) String() string { return string(e) } -func (e *AssetFilterType) UnmarshalGQL(v interface{}) error { +func (e *AssetSortType) UnmarshalGQL(v interface{}) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = AssetFilterType(str) + *e = AssetSortType(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid AssetFilterType", str) + return fmt.Errorf("%s is not a valid AssetSortType", str) } return nil } -func (e AssetFilterType) MarshalGQL(w io.Writer) { +func (e AssetSortType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 09cffda9..ce2daef5 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -34,29 +34,29 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. return assets, nil } -func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, sortType *gqlmodel.AssetSortType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { p := usecase.NewPagination(first, last, before, after) findOptions := options.Find() findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) - sortType := "id" - if filter != nil { - switch *filter { - case gqlmodel.AssetFilterTypeName: - sortType = "name" - case gqlmodel.AssetFilterTypeSize: - sortType = "size" + sortKey := "id" + if sortType != nil { + switch *sortType { + case gqlmodel.AssetSortTypeName: + sortKey = "name" + case gqlmodel.AssetSortTypeSize: + sortKey = "size" } } if first != nil { findOptions.Sort = bson.D{ - {Key: sortType, Value: 1}, + {Key: sortKey, Value: 1}, } } else if last != nil { findOptions.Sort = bson.D{ - {Key: sortType, Value: -1}, + {Key: sortKey, Value: -1}, } } diff --git a/internal/adapter/gql/resolver_query.go b/internal/adapter/gql/resolver_query.go index 74b8a49d..e5f1cbc4 100644 --- a/internal/adapter/gql/resolver_query.go +++ b/internal/adapter/gql/resolver_query.go @@ -14,11 +14,11 @@ func (r *Resolver) Query() QueryResolver { type queryResolver struct{ *Resolver } -func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, filter *gqlmodel.AssetFilterType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, sortType *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { exit := trace(ctx) defer exit() - return r.loaders.Asset.FindByTeam(ctx, teamID, filter, first, last, before, after) + return r.loaders.Asset.FindByTeam(ctx, teamID, sortType, first, last, before, after) } func (r *queryResolver) Me(ctx context.Context) (*gqlmodel.User, error) { diff --git a/pkg/plugin/manifest/schema_gen.go b/pkg/plugin/manifest/schema_gen.go index 31f0073c..2456d721 100644 --- a/pkg/plugin/manifest/schema_gen.go +++ b/pkg/plugin/manifest/schema_gen.go @@ -1,6 +1,6 @@ package manifest -// generated by "/var/folders/jg/1nqjhdbs6png0lqrfy2g9nq80000gn/T/go-build3063292847/b001/exe/schematyper -o schema_gen.go --package manifest ../../../plugin_manifest_schema.json" -- DO NOT EDIT +// generated by "/var/folders/jg/1nqjhdbs6png0lqrfy2g9nq80000gn/T/go-build3654450581/b001/exe/schematyper -o schema_gen.go --package manifest ../../../plugin_manifest_schema.json" -- DO NOT EDIT type Choice struct { Icon string `json:"icon,omitempty"` diff --git a/schema.graphql b/schema.graphql index 1123949a..94e3d135 100644 --- a/schema.graphql +++ b/schema.graphql @@ -144,7 +144,7 @@ type Asset implements Node { team: Team @goField(forceResolver: true) } -enum AssetFilterType { +enum AssetSortType { DATE SIZE NAME @@ -1550,7 +1550,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! - filter: AssetFilterType + sort: AssetSortType first: Int last: Int after: Cursor From b9bc7f6efebacf19d57b8836613cfa8b5b9f9243 Mon Sep 17 00:00:00 2001 From: yk Date: Fri, 21 Jan 2022 13:18:43 +0300 Subject: [PATCH 05/18] add ability to search by key word code refactoring --- internal/adapter/gql/generated.go | 48 +++++++++++-------- internal/adapter/gql/loader_asset.go | 21 ++++---- internal/adapter/gql/resolver_query.go | 4 +- internal/adapter/gql/resolver_team.go | 2 +- internal/infrastructure/memory/asset.go | 2 +- internal/infrastructure/mongo/asset.go | 14 +++++- .../infrastructure/mongo/mongodoc/client.go | 44 +++++++++-------- internal/usecase/interactor/asset.go | 4 +- internal/usecase/interfaces/asset.go | 2 +- internal/usecase/repo/asset.go | 2 +- schema.graphql | 1 + 11 files changed, 85 insertions(+), 59 deletions(-) diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 1cfea5ff..0c2d640f 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -781,7 +781,7 @@ type ComplexityRoot struct { } Query struct { - Assets func(childComplexity int, teamID id.ID, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int + Assets func(childComplexity int, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int CheckProjectAlias func(childComplexity int, alias string) int DatasetSchemas func(childComplexity int, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int Datasets func(childComplexity int, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int @@ -1303,7 +1303,7 @@ type QueryResolver interface { Plugins(ctx context.Context, id []*id.PluginID) ([]*gqlmodel.Plugin, error) Layer(ctx context.Context, id id.ID) (gqlmodel.Layer, error) Scene(ctx context.Context, projectID id.ID) (*gqlmodel.Scene, error) - Assets(ctx context.Context, teamID id.ID, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) + Assets(ctx context.Context, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) Projects(ctx context.Context, teamID id.ID, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) DatasetSchemas(ctx context.Context, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetSchemaConnection, error) Datasets(ctx context.Context, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetConnection, error) @@ -4990,7 +4990,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true + return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true case "Query.checkProjectAlias": if e.complexity.Query.CheckProjectAlias == nil { @@ -7829,6 +7829,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! + keyword: String sort: AssetSortType first: Int last: Int @@ -9159,51 +9160,60 @@ func (ec *executionContext) field_Query_assets_args(ctx context.Context, rawArgs } } args["teamId"] = arg0 - var arg1 *gqlmodel.AssetSortType + var arg1 *string + if tmp, ok := rawArgs["keyword"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("keyword")) + arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["keyword"] = arg1 + var arg2 *gqlmodel.AssetSortType if tmp, ok := rawArgs["sort"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort")) - arg1, err = ec.unmarshalOAssetSortType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetSortType(ctx, tmp) + arg2, err = ec.unmarshalOAssetSortType2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐAssetSortType(ctx, tmp) if err != nil { return nil, err } } - args["sort"] = arg1 - var arg2 *int + args["sort"] = arg2 + var arg3 *int if tmp, ok := rawArgs["first"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) - arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["first"] = arg2 - var arg3 *int + args["first"] = arg3 + var arg4 *int if tmp, ok := rawArgs["last"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) - arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["last"] = arg3 - var arg4 *usecase.Cursor + args["last"] = arg4 + var arg5 *usecase.Cursor if tmp, ok := rawArgs["after"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) - arg4, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) + arg5, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) if err != nil { return nil, err } } - args["after"] = arg4 - var arg5 *usecase.Cursor + args["after"] = arg5 + var arg6 *usecase.Cursor if tmp, ok := rawArgs["before"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) - arg5, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) + arg6, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) if err != nil { return nil, err } } - args["before"] = arg5 + args["before"] = arg6 return args, nil } @@ -26108,7 +26118,7 @@ func (ec *executionContext) _Query_assets(ctx context.Context, field graphql.Col fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) + return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) }) if err != nil { ec.Error(ctx, err) diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index ce2daef5..733e0c1d 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -34,7 +34,7 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. return assets, nil } -func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, sortType *gqlmodel.AssetSortType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { p := usecase.NewPagination(first, last, before, after) findOptions := options.Find() @@ -47,20 +47,21 @@ func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, sortType *gq sortKey = "name" case gqlmodel.AssetSortTypeSize: sortKey = "size" + default: + sortKey = "id" } } - if first != nil { - findOptions.Sort = bson.D{ - {Key: sortKey, Value: 1}, - } - } else if last != nil { - findOptions.Sort = bson.D{ - {Key: sortKey, Value: -1}, - } + sortDir := 1 + if last != nil { + sortDir = -1 + } + + findOptions.Sort = bson.D{ + {Key: sortKey, Value: sortDir}, } - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), findOptions, p, getOperator(ctx)) + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, findOptions, p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/adapter/gql/resolver_query.go b/internal/adapter/gql/resolver_query.go index e5f1cbc4..9ad9c289 100644 --- a/internal/adapter/gql/resolver_query.go +++ b/internal/adapter/gql/resolver_query.go @@ -14,11 +14,11 @@ func (r *Resolver) Query() QueryResolver { type queryResolver struct{ *Resolver } -func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, sortType *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { +func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { exit := trace(ctx) defer exit() - return r.loaders.Asset.FindByTeam(ctx, teamID, sortType, first, last, before, after) + return r.loaders.Asset.FindByTeam(ctx, teamID, keyword, sortType, first, last, before, after) } func (r *queryResolver) Me(ctx context.Context) (*gqlmodel.User, error) { diff --git a/internal/adapter/gql/resolver_team.go b/internal/adapter/gql/resolver_team.go index b3ac22bd..26711359 100644 --- a/internal/adapter/gql/resolver_team.go +++ b/internal/adapter/gql/resolver_team.go @@ -22,7 +22,7 @@ func (r *teamResolver) Assets(ctx context.Context, obj *gqlmodel.Team, first *in exit := trace(ctx) defer exit() - return r.loaders.Asset.FindByTeam(ctx, obj.ID, nil, first, last, before, after) + return r.loaders.Asset.FindByTeam(ctx, obj.ID, nil, nil, first, last, before, after) } func (r *teamResolver) Projects(ctx context.Context, obj *gqlmodel.Team, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) { diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index 84a54355..6dbf727f 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -67,7 +67,7 @@ func (r *Asset) Remove(ctx context.Context, id id.AssetID) error { return nil } -func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 6e2baff0..63446001 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -2,8 +2,10 @@ package mongo import ( "context" + "fmt" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo/options" "github.com/reearth/reearth-backend/internal/infrastructure/mongo/mongodoc" @@ -53,9 +55,17 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { return r.client.RemoveOne(ctx, id.String()) } -func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { + filters := []bson.D{ + {{Key: "team", Value: id.String()}}, + } + if keyword != nil { + filters = append(filters, bson.D{{Key: "name", Value: bson.D{ + {Key: "$regex", Value: primitive.Regex{Pattern: fmt.Sprintf(".*%s.*", *keyword), Options: "i"}}, + }}}) + } filter := bson.D{ - {Key: "team", Value: id.String()}, + {Key: "$and", Value: filters}, } return r.paginate(ctx, filter, findOptions, pagination) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index c87274cd..2830cb4e 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -178,37 +178,41 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, f return nil, fmt.Errorf("failed to count documents: %v", err.Error()) } + if findOptions == nil { + findOptions = options.Find() + } + + paginate := false var limit int64 + var cur *usecase.Cursor + var op string - if first := p.First; first != nil { + if first, after := p.First, p.After; first != nil && after != nil { + paginate = true limit = int64(*first) - if after := p.After; after != nil { - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ - {Key: "$gt", Value: *after}, - }}) - } - } else if last := p.Last; last != nil { + op = "$gt" + cur = after + } + if last, before := p.Last, p.Before; last != nil && before != nil { + paginate = true limit = int64(*last) - if before := p.Before; before != nil { - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ - {Key: "$lt", Value: *before}, - }}) - } + op = "$lt" + cur = before } - - var findOptions2 *options.FindOptions - if findOptions != nil { - findOptions2 = findOptions - } else { - findOptions2 = options.Find() + if !paginate { + return nil, fmt.Errorf("missing pagination paramiters: %v", p) } + filter = appendE(filter, bson.E{Key: key, Value: bson.D{ + {Key: op, Value: *cur}, + }}) + // 更に読める要素があるのか確かめるために一つ多めに読み出す // Read one more element so that we can see whether there's a further one limit++ - findOptions2.Limit = &limit + findOptions.Limit = &limit - cursor, err := coll.Find(ctx, filter, findOptions2) + cursor, err := coll.Find(ctx, filter, findOptions) if err != nil { return nil, fmt.Errorf("failed to find: %v", err.Error()) } diff --git a/internal/usecase/interactor/asset.go b/internal/usecase/interactor/asset.go index 3d19c9ce..063a6f26 100644 --- a/internal/usecase/interactor/asset.go +++ b/internal/usecase/interactor/asset.go @@ -35,12 +35,12 @@ func (i *Asset) Fetch(ctx context.Context, assets []id.AssetID, operator *usecas return i.assetRepo.FindByIDs(ctx, assets, operator.ReadableTeams) } -func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, findOptions *options.FindOptions, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { +func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, keyword *string, findOptions *options.FindOptions, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { if err := i.CanReadTeam(tid, operator); err != nil { return nil, nil, err } - return i.assetRepo.FindByTeam(ctx, tid, findOptions, p) + return i.assetRepo.FindByTeam(ctx, tid, keyword, findOptions, p) } func (i *Asset) Create(ctx context.Context, inp interfaces.CreateAssetParam, operator *usecase.Operator) (result *asset.Asset, err error) { diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index b9f2adc2..4eb54764 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -30,7 +30,7 @@ var ( type Asset interface { Fetch(context.Context, []id.AssetID, *usecase.Operator) ([]*asset.Asset, error) - FindByTeam(context.Context, id.TeamID, *options.FindOptions, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *string, *options.FindOptions, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, error) Remove(context.Context, id.AssetID, *usecase.Operator) (id.AssetID, error) } diff --git a/internal/usecase/repo/asset.go b/internal/usecase/repo/asset.go index f452e9c8..4300c3cd 100644 --- a/internal/usecase/repo/asset.go +++ b/internal/usecase/repo/asset.go @@ -12,7 +12,7 @@ import ( type Asset interface { Save(context.Context, *asset.Asset) error Remove(context.Context, id.AssetID) error - FindByTeam(context.Context, id.TeamID, *options.FindOptions, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *string, *options.FindOptions, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) FindByID(context.Context, id.AssetID, []id.TeamID) (*asset.Asset, error) FindByIDs(context.Context, []id.AssetID, []id.TeamID) ([]*asset.Asset, error) } diff --git a/schema.graphql b/schema.graphql index 56b643ca..66b44862 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1568,6 +1568,7 @@ type Query { scene(projectId: ID!): Scene assets( teamId: ID! + keyword: String sort: AssetSortType first: Int last: Int From 9a3750a84f03e4a4fdbe11fc47fd15334b82dddb Mon Sep 17 00:00:00 2001 From: yk Date: Mon, 24 Jan 2022 15:32:10 +0300 Subject: [PATCH 06/18] make the pagination cursor optional --- internal/infrastructure/mongo/mongodoc/client.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 2830cb4e..c82e1b33 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -184,16 +184,16 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, f paginate := false var limit int64 - var cur *usecase.Cursor + var cur *usecase.Cursor = nil var op string - if first, after := p.First, p.After; first != nil && after != nil { + if first, after := p.First, p.After; first != nil { paginate = true limit = int64(*first) op = "$gt" cur = after } - if last, before := p.Last, p.Before; last != nil && before != nil { + if last, before := p.Last, p.Before; last != nil { paginate = true limit = int64(*last) op = "$lt" @@ -203,9 +203,11 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, f return nil, fmt.Errorf("missing pagination paramiters: %v", p) } - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ - {Key: op, Value: *cur}, - }}) + if cur != nil { + filter = appendE(filter, bson.E{Key: key, Value: bson.D{ + {Key: op, Value: *cur}, + }}) + } // 更に読める要素があるのか確かめるために一つ多めに読み出す // Read one more element so that we can see whether there's a further one From e47ce12d2c7530fdedf921ea34cf769eb45b6f68 Mon Sep 17 00:00:00 2001 From: yk Date: Mon, 7 Feb 2022 12:51:04 +0300 Subject: [PATCH 07/18] code restructuring --- internal/adapter/gql/generated.go | 112 +++++++++++------- internal/adapter/gql/gqlmodel/convert.go | 12 ++ internal/adapter/gql/gqlmodel/models_gen.go | 7 ++ internal/adapter/gql/loader_asset.go | 33 +----- internal/adapter/gql/resolver_query.go | 4 +- internal/adapter/gql/resolver_team.go | 7 +- internal/infrastructure/memory/asset.go | 3 +- internal/infrastructure/mongo/asset.go | 15 ++- .../infrastructure/mongo/mongodoc/client.go | 40 +++---- .../mongo/mongodoc/clientcol.go | 5 +- .../mongo/mongodoc/pagination.go | 52 ++++++++ internal/usecase/interactor/asset.go | 5 +- internal/usecase/interfaces/asset.go | 3 +- internal/usecase/repo/asset.go | 3 +- schema.graphql | 12 +- 15 files changed, 195 insertions(+), 118 deletions(-) create mode 100644 internal/infrastructure/mongo/mongodoc/pagination.go diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 0c2d640f..6bfe1a7e 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -781,7 +781,7 @@ type ComplexityRoot struct { } Query struct { - Assets func(childComplexity int, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int + Assets func(childComplexity int, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) int CheckProjectAlias func(childComplexity int, alias string) int DatasetSchemas func(childComplexity int, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int Datasets func(childComplexity int, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) int @@ -1303,7 +1303,7 @@ type QueryResolver interface { Plugins(ctx context.Context, id []*id.PluginID) ([]*gqlmodel.Plugin, error) Layer(ctx context.Context, id id.ID) (gqlmodel.Layer, error) Scene(ctx context.Context, projectID id.ID) (*gqlmodel.Scene, error) - Assets(ctx context.Context, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) + Assets(ctx context.Context, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) Projects(ctx context.Context, teamID id.ID, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) DatasetSchemas(ctx context.Context, sceneID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetSchemaConnection, error) Datasets(ctx context.Context, datasetSchemaID id.ID, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.DatasetConnection, error) @@ -4990,7 +4990,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)), true + return e.complexity.Query.Assets(childComplexity, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["pagination"].(*gqlmodel.Pagination)), true case "Query.checkProjectAlias": if e.complexity.Query.CheckProjectAlias == nil { @@ -6357,6 +6357,13 @@ type Rect { north: Float! } +input Pagination{ + first: Int + last: Int + after: Cursor + before: Cursor +} + enum TextAlign { LEFT CENTER @@ -7831,10 +7838,7 @@ type Query { teamId: ID! keyword: String sort: AssetSortType - first: Int - last: Int - after: Cursor - before: Cursor + pagination: Pagination ): AssetConnection! projects( teamId: ID! @@ -9178,42 +9182,15 @@ func (ec *executionContext) field_Query_assets_args(ctx context.Context, rawArgs } } args["sort"] = arg2 - var arg3 *int - if tmp, ok := rawArgs["first"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) - arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + var arg3 *gqlmodel.Pagination + if tmp, ok := rawArgs["pagination"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pagination")) + arg3, err = ec.unmarshalOPagination2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐPagination(ctx, tmp) if err != nil { return nil, err } } - args["first"] = arg3 - var arg4 *int - if tmp, ok := rawArgs["last"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) - arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) - if err != nil { - return nil, err - } - } - args["last"] = arg4 - var arg5 *usecase.Cursor - if tmp, ok := rawArgs["after"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) - arg5, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) - if err != nil { - return nil, err - } - } - args["after"] = arg5 - var arg6 *usecase.Cursor - if tmp, ok := rawArgs["before"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) - arg6, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, tmp) - if err != nil { - return nil, err - } - } - args["before"] = arg6 + args["pagination"] = arg3 return args, nil } @@ -26118,7 +26095,7 @@ func (ec *executionContext) _Query_assets(ctx context.Context, field graphql.Col fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["first"].(*int), args["last"].(*int), args["after"].(*usecase.Cursor), args["before"].(*usecase.Cursor)) + return ec.resolvers.Query().Assets(rctx, args["teamId"].(id.ID), args["keyword"].(*string), args["sort"].(*gqlmodel.AssetSortType), args["pagination"].(*gqlmodel.Pagination)) }) if err != nil { ec.Error(ctx, err) @@ -33826,6 +33803,53 @@ func (ec *executionContext) unmarshalInputMovePropertyItemInput(ctx context.Cont return it, nil } +func (ec *executionContext) unmarshalInputPagination(ctx context.Context, obj interface{}) (gqlmodel.Pagination, error) { + var it gqlmodel.Pagination + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + for k, v := range asMap { + switch k { + case "first": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + it.First, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "last": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + it.Last, err = ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + case "after": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + it.After, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, v) + if err != nil { + return it, err + } + case "before": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + it.Before, err = ec.unmarshalOCursor2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋusecaseᚐCursor(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputPublishProjectInput(ctx context.Context, obj interface{}) (gqlmodel.PublishProjectInput, error) { var it gqlmodel.PublishProjectInput asMap := map[string]interface{}{} @@ -45629,6 +45653,14 @@ func (ec *executionContext) marshalONode2githubᚗcomᚋreearthᚋreearthᚑback return ec._Node(ctx, sel, v) } +func (ec *executionContext) unmarshalOPagination2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐPagination(ctx context.Context, v interface{}) (*gqlmodel.Pagination, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputPagination(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) marshalOPlugin2ᚖgithubᚗcomᚋreearthᚋreearthᚑbackendᚋinternalᚋadapterᚋgqlᚋgqlmodelᚐPlugin(ctx context.Context, sel ast.SelectionSet, v *gqlmodel.Plugin) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/internal/adapter/gql/gqlmodel/convert.go b/internal/adapter/gql/gqlmodel/convert.go index f25dbf02..eecaa636 100644 --- a/internal/adapter/gql/gqlmodel/convert.go +++ b/internal/adapter/gql/gqlmodel/convert.go @@ -85,3 +85,15 @@ func FromListOperation(op ListOperation) interfaces.ListOperation { } return interfaces.ListOperation("") } + +func ToPagination(pagination *Pagination) *usecase.Pagination { + if pagination == nil { + return nil + } + return &usecase.Pagination{ + Before: pagination.Before, + After: pagination.After, + First: pagination.First, + Last: pagination.Last, + } +} diff --git a/internal/adapter/gql/gqlmodel/models_gen.go b/internal/adapter/gql/gqlmodel/models_gen.go index 668fd632..ee7a0f6b 100644 --- a/internal/adapter/gql/gqlmodel/models_gen.go +++ b/internal/adapter/gql/gqlmodel/models_gen.go @@ -661,6 +661,13 @@ type PageInfo struct { HasPreviousPage bool `json:"hasPreviousPage"` } +type Pagination struct { + First *int `json:"first"` + Last *int `json:"last"` + After *usecase.Cursor `json:"after"` + Before *usecase.Cursor `json:"before"` +} + type Plugin struct { ID id.PluginID `json:"id"` SceneID *id.ID `json:"sceneId"` diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 733e0c1d..3d11c8e6 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -8,8 +8,6 @@ import ( "github.com/reearth/reearth-backend/internal/usecase" "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/id" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/options" ) type AssetLoader struct { @@ -34,34 +32,15 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. return assets, nil } -func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, first *int, last *int, before *usecase.Cursor, after *usecase.Cursor) (*gqlmodel.AssetConnection, error) { - p := usecase.NewPagination(first, last, before, after) - - findOptions := options.Find() - findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) - - sortKey := "id" - if sortType != nil { - switch *sortType { - case gqlmodel.AssetSortTypeName: - sortKey = "name" - case gqlmodel.AssetSortTypeSize: - sortKey = "size" - default: - sortKey = "id" - } - } - - sortDir := 1 - if last != nil { - sortDir = -1 - } +func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { + p := gqlmodel.ToPagination(pagination) - findOptions.Sort = bson.D{ - {Key: sortKey, Value: sortDir}, + s := new(string) + if sort != nil { + *s = string(*sort) } - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, findOptions, p, getOperator(ctx)) + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, s, p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/adapter/gql/resolver_query.go b/internal/adapter/gql/resolver_query.go index 44069121..71677397 100644 --- a/internal/adapter/gql/resolver_query.go +++ b/internal/adapter/gql/resolver_query.go @@ -14,8 +14,8 @@ func (r *Resolver) Query() QueryResolver { type queryResolver struct{ *Resolver } -func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { - return loaders(ctx).Asset.FindByTeam(ctx, teamID, first, last, before, after) +func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { + return loaders(ctx).Asset.FindByTeam(ctx, teamID, keyword, sortType, pagination) } func (r *queryResolver) Me(ctx context.Context) (*gqlmodel.User, error) { diff --git a/internal/adapter/gql/resolver_team.go b/internal/adapter/gql/resolver_team.go index 9a47eb4c..021412d7 100644 --- a/internal/adapter/gql/resolver_team.go +++ b/internal/adapter/gql/resolver_team.go @@ -19,7 +19,12 @@ func (r *Resolver) TeamMember() TeamMemberResolver { type teamResolver struct{ *Resolver } func (r *teamResolver) Assets(ctx context.Context, obj *gqlmodel.Team, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.AssetConnection, error) { - return loaders(ctx).Asset.FindByTeam(ctx, obj.ID, nil, nil, first, last, before, after) + return loaders(ctx).Asset.FindByTeam(ctx, obj.ID, nil, nil, &gqlmodel.Pagination{ + First: first, + Last: last, + After: after, + Before: before, + }) } func (r *teamResolver) Projects(ctx context.Context, obj *gqlmodel.Team, includeArchived *bool, first *int, last *int, after *usecase.Cursor, before *usecase.Cursor) (*gqlmodel.ProjectConnection, error) { diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index 6dbf727f..bd5b9fb2 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -9,7 +9,6 @@ import ( "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/rerror" - "go.mongodb.org/mongo-driver/mongo/options" ) type Asset struct { @@ -67,7 +66,7 @@ func (r *Asset) Remove(ctx context.Context, id id.AssetID) error { return nil } -func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 63446001..c31561a6 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -4,10 +4,6 @@ import ( "context" "fmt" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo/options" - "github.com/reearth/reearth-backend/internal/infrastructure/mongo/mongodoc" "github.com/reearth/reearth-backend/internal/usecase" "github.com/reearth/reearth-backend/internal/usecase/repo" @@ -15,6 +11,8 @@ import ( "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/log" "github.com/reearth/reearth-backend/pkg/rerror" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" ) type assetRepo struct { @@ -55,7 +53,7 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { return r.client.RemoveOne(ctx, id.String()) } -func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { filters := []bson.D{ {{Key: "team", Value: id.String()}}, } @@ -67,7 +65,8 @@ func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *strin filter := bson.D{ {Key: "$and", Value: filters}, } - return r.paginate(ctx, filter, findOptions, pagination) + + return r.paginate(ctx, filter, sort, pagination) } func (r *assetRepo) init() { @@ -77,9 +76,9 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.D, findOptions *options.FindOptions, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, findOptions, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, sort, pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index c82e1b33..4bbdb135 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -165,42 +165,34 @@ func getCursor(raw bson.Raw, key string) (*usecase.Cursor, error) { return &c, nil } -func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, findOptions *options.FindOptions, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sort *string, p *Pagination, consumer Consumer) (*usecase.PageInfo, error) { if p == nil { return nil, nil } coll := c.Collection(col) - key := "id" + findOptions := options.Find() + findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) - count, err := coll.CountDocuments(ctx, filter) - if err != nil { - return nil, fmt.Errorf("failed to count documents: %v", err.Error()) + sortKey := "id" + if sort != nil { + sortKey = *sort } - if findOptions == nil { - findOptions = options.Find() + findOptions.Sort = bson.D{ + {Key: sortKey, Value: p.SortDirection()}, } - paginate := false - var limit int64 - var cur *usecase.Cursor = nil - var op string + key := "id" - if first, after := p.First, p.After; first != nil { - paginate = true - limit = int64(*first) - op = "$gt" - cur = after - } - if last, before := p.Last, p.Before; last != nil { - paginate = true - limit = int64(*last) - op = "$lt" - cur = before + count, err := coll.CountDocuments(ctx, filter) + if err != nil { + return nil, fmt.Errorf("failed to count documents: %v", err.Error()) } - if !paginate { - return nil, fmt.Errorf("missing pagination paramiters: %v", p) + + limit, op, cur, err := p.Parameters() + if err != nil { + return nil, fmt.Errorf("faild to parse pagination parameters: %w", err) } if cur != nil { diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index db977c3b..7231aadf 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -5,7 +5,6 @@ import ( "github.com/reearth/reearth-backend/internal/usecase" "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" ) type ClientCollection struct { @@ -29,8 +28,8 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 return c.Client.Count(ctx, c.CollectionName, filter) } -func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, findOptions *options.FindOptions, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { - return c.Client.Paginate(ctx, c.CollectionName, filter, findOptions, p, consumer) +func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sort *string, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { + return c.Client.Paginate(ctx, c.CollectionName, filter, sort, NewPaginationFrom(p), consumer) } func (c *ClientCollection) SaveOne(ctx context.Context, id string, replacement interface{}) error { diff --git a/internal/infrastructure/mongo/mongodoc/pagination.go b/internal/infrastructure/mongo/mongodoc/pagination.go new file mode 100644 index 00000000..2513d80f --- /dev/null +++ b/internal/infrastructure/mongo/mongodoc/pagination.go @@ -0,0 +1,52 @@ +package mongodoc + +import ( + "errors" + + "github.com/reearth/reearth-backend/internal/usecase" +) + +type Pagination struct { + Before *string + After *string + First *int + Last *int +} + +func NewPaginationFrom(pagination *usecase.Pagination) *Pagination { + if pagination == nil { + return nil + } + return &Pagination{ + Before: (*string)(pagination.Before), + After: (*string)(pagination.After), + First: pagination.First, + Last: pagination.Last, + } +} + +func (p *Pagination) SortDirection() int { + if p != nil { + return 1 + } + if p.Last != nil { + return -1 + } + return 1 +} + +func (p *Pagination) Parameters() (limit int64, op string, cursor *string, err error) { + if first, after := p.First, p.After; first != nil { + limit = int64(*first) + op = "$gt" + cursor = after + return + } + if last, before := p.Last, p.Before; last != nil { + limit = int64(*last) + op = "$lt" + cursor = before + return + } + return 0, "", nil, errors.New("neither first nor last are not set") +} diff --git a/internal/usecase/interactor/asset.go b/internal/usecase/interactor/asset.go index 063a6f26..5aa180b5 100644 --- a/internal/usecase/interactor/asset.go +++ b/internal/usecase/interactor/asset.go @@ -11,7 +11,6 @@ import ( "github.com/reearth/reearth-backend/internal/usecase/repo" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" - "go.mongodb.org/mongo-driver/mongo/options" ) type Asset struct { @@ -35,12 +34,12 @@ func (i *Asset) Fetch(ctx context.Context, assets []id.AssetID, operator *usecas return i.assetRepo.FindByIDs(ctx, assets, operator.ReadableTeams) } -func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, keyword *string, findOptions *options.FindOptions, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { +func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, keyword *string, sort *string, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { if err := i.CanReadTeam(tid, operator); err != nil { return nil, nil, err } - return i.assetRepo.FindByTeam(ctx, tid, keyword, findOptions, p) + return i.assetRepo.FindByTeam(ctx, tid, keyword, sort, p) } func (i *Asset) Create(ctx context.Context, inp interfaces.CreateAssetParam, operator *usecase.Operator) (result *asset.Asset, err error) { diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index 4eb54764..6ce150ee 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -8,7 +8,6 @@ import ( "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/file" "github.com/reearth/reearth-backend/pkg/id" - "go.mongodb.org/mongo-driver/mongo/options" ) type AssetFilterType string @@ -30,7 +29,7 @@ var ( type Asset interface { Fetch(context.Context, []id.AssetID, *usecase.Operator) ([]*asset.Asset, error) - FindByTeam(context.Context, id.TeamID, *string, *options.FindOptions, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *string, *string, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, error) Remove(context.Context, id.AssetID, *usecase.Operator) (id.AssetID, error) } diff --git a/internal/usecase/repo/asset.go b/internal/usecase/repo/asset.go index 4300c3cd..30131275 100644 --- a/internal/usecase/repo/asset.go +++ b/internal/usecase/repo/asset.go @@ -6,13 +6,12 @@ import ( "github.com/reearth/reearth-backend/internal/usecase" "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" - "go.mongodb.org/mongo-driver/mongo/options" ) type Asset interface { Save(context.Context, *asset.Asset) error Remove(context.Context, id.AssetID) error - FindByTeam(context.Context, id.TeamID, *string, *options.FindOptions, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *string, *string, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) FindByID(context.Context, id.AssetID, []id.TeamID) (*asset.Asset, error) FindByIDs(context.Context, []id.AssetID, []id.TeamID) ([]*asset.Asset, error) } diff --git a/schema.graphql b/schema.graphql index 66b44862..bd74bbd3 100644 --- a/schema.graphql +++ b/schema.graphql @@ -96,6 +96,13 @@ type Rect { north: Float! } +input Pagination{ + first: Int + last: Int + after: Cursor + before: Cursor +} + enum TextAlign { LEFT CENTER @@ -1570,10 +1577,7 @@ type Query { teamId: ID! keyword: String sort: AssetSortType - first: Int - last: Int - after: Cursor - before: Cursor + pagination: Pagination ): AssetConnection! projects( teamId: ID! From 6311e9c78ec5275e43c3817d0dded5bc8a4d6e0e Mon Sep 17 00:00:00 2001 From: yk Date: Thu, 24 Feb 2022 08:51:40 +0300 Subject: [PATCH 08/18] fix sort default value --- internal/infrastructure/mongo/mongodoc/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 4bbdb135..d413c2f8 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -175,7 +175,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) sortKey := "id" - if sort != nil { + if sort != nil && len(*sort) > 0 { sortKey = *sort } From be555708d1ef524ec481b238c21bc24d5897d357 Mon Sep 17 00:00:00 2001 From: yk Date: Fri, 25 Feb 2022 09:40:16 +0300 Subject: [PATCH 09/18] fix sort default value --- internal/infrastructure/mongo/mongodoc/client.go | 2 +- internal/infrastructure/mongo/mongodoc/pagination.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index d413c2f8..09ba85b1 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -192,7 +192,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s limit, op, cur, err := p.Parameters() if err != nil { - return nil, fmt.Errorf("faild to parse pagination parameters: %w", err) + return nil, fmt.Errorf("failed to parse pagination parameters: %w", err) } if cur != nil { diff --git a/internal/infrastructure/mongo/mongodoc/pagination.go b/internal/infrastructure/mongo/mongodoc/pagination.go index 2513d80f..f5787afc 100644 --- a/internal/infrastructure/mongo/mongodoc/pagination.go +++ b/internal/infrastructure/mongo/mongodoc/pagination.go @@ -26,7 +26,7 @@ func NewPaginationFrom(pagination *usecase.Pagination) *Pagination { } func (p *Pagination) SortDirection() int { - if p != nil { + if p == nil { return 1 } if p.Last != nil { @@ -48,5 +48,5 @@ func (p *Pagination) Parameters() (limit int64, op string, cursor *string, err e cursor = before return } - return 0, "", nil, errors.New("neither first nor last are not set") + return 0, "", nil, errors.New("neither first nor last are set") } From afb4535136060959b997d9f95f55ebbdf88e8ffd Mon Sep 17 00:00:00 2001 From: yk Date: Fri, 25 Feb 2022 10:36:41 +0300 Subject: [PATCH 10/18] fix sort --- internal/adapter/gql/loader_asset.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 3d11c8e6..5e37b48b 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -2,6 +2,7 @@ package gql import ( "context" + "strings" "github.com/reearth/reearth-backend/internal/adapter/gql/gqldataloader" "github.com/reearth/reearth-backend/internal/adapter/gql/gqlmodel" @@ -37,7 +38,7 @@ func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *str s := new(string) if sort != nil { - *s = string(*sort) + *s = strings.ToLower(string(*sort)) } assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, s, p, getOperator(ctx)) From ffb3b05c0ddf61ee21ff01927dda2654dd126e3c Mon Sep 17 00:00:00 2001 From: yk Date: Thu, 3 Mar 2022 09:41:09 +0300 Subject: [PATCH 11/18] pagination refactoring --- .../adapter/gql/gqlmodel/convert_asset.go | 15 ++++ internal/adapter/gql/loader_asset.go | 11 +-- internal/adapter/gql/resolver_query.go | 2 +- internal/infrastructure/mongo/asset.go | 20 ++--- internal/infrastructure/mongo/dataset.go | 38 ++++----- .../infrastructure/mongo/dataset_schema.go | 7 +- .../infrastructure/mongo/mongodoc/client.go | 18 +++-- .../mongo/mongodoc/clientcol.go | 3 +- internal/infrastructure/mongo/project.go | 7 +- pkg/asset/sort_type.go | 37 +++++++++ pkg/asset/sort_type_test.go | 79 +++++++++++++++++++ 11 files changed, 180 insertions(+), 57 deletions(-) create mode 100644 pkg/asset/sort_type.go create mode 100644 pkg/asset/sort_type_test.go diff --git a/internal/adapter/gql/gqlmodel/convert_asset.go b/internal/adapter/gql/gqlmodel/convert_asset.go index 0f60ad76..1404ac64 100644 --- a/internal/adapter/gql/gqlmodel/convert_asset.go +++ b/internal/adapter/gql/gqlmodel/convert_asset.go @@ -18,3 +18,18 @@ func ToAsset(a *asset.Asset) *Asset { ContentType: a.ContentType(), } } + +func AssetSortTypeFrom(ast *AssetSortType) *asset.SortType { + if ast == nil { + return nil + } + switch *ast { + case AssetSortTypeDate: + return &asset.SortTypeID + case AssetSortTypeName: + return &asset.SortTypeName + case AssetSortTypeSize: + return &asset.SortTypeSize + } + return &asset.SortTypeID +} diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 5e37b48b..3240d397 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -2,12 +2,12 @@ package gql import ( "context" - "strings" "github.com/reearth/reearth-backend/internal/adapter/gql/gqldataloader" "github.com/reearth/reearth-backend/internal/adapter/gql/gqlmodel" "github.com/reearth/reearth-backend/internal/usecase" "github.com/reearth/reearth-backend/internal/usecase/interfaces" + "github.com/reearth/reearth-backend/pkg/asset" "github.com/reearth/reearth-backend/pkg/id" ) @@ -33,15 +33,10 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. return assets, nil } -func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sort *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { +func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sort *asset.SortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { p := gqlmodel.ToPagination(pagination) - s := new(string) - if sort != nil { - *s = strings.ToLower(string(*sort)) - } - - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, s, p, getOperator(ctx)) + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, (*string)(sort), p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/adapter/gql/resolver_query.go b/internal/adapter/gql/resolver_query.go index 71677397..9adad2b1 100644 --- a/internal/adapter/gql/resolver_query.go +++ b/internal/adapter/gql/resolver_query.go @@ -15,7 +15,7 @@ func (r *Resolver) Query() QueryResolver { type queryResolver struct{ *Resolver } func (r *queryResolver) Assets(ctx context.Context, teamID id.ID, keyword *string, sortType *gqlmodel.AssetSortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { - return loaders(ctx).Asset.FindByTeam(ctx, teamID, keyword, sortType, pagination) + return loaders(ctx).Asset.FindByTeam(ctx, teamID, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination) } func (r *queryResolver) Me(ctx context.Context) (*gqlmodel.User, error) { diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index c31561a6..0111d6e8 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -54,16 +54,16 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { } func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { - filters := []bson.D{ - {{Key: "team", Value: id.String()}}, - } + filter := bson.M{"team": id.String()} + if keyword != nil { - filters = append(filters, bson.D{{Key: "name", Value: bson.D{ - {Key: "$regex", Value: primitive.Regex{Pattern: fmt.Sprintf(".*%s.*", *keyword), Options: "i"}}, - }}}) - } - filter := bson.D{ - {Key: "$and", Value: filters}, + keywordFilter := bson.M{"name": bson.M{ + "$regex": primitive.Regex{Pattern: fmt.Sprintf(".*%s.*", *keyword), Options: "i"}}, + } + filter = bson.M{"$and": []bson.M{ + filter, + keywordFilter, + }} } return r.paginate(ctx, filter, sort, pagination) @@ -76,7 +76,7 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.D, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.M, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer pageInfo, err2 := r.client.Paginate(ctx, filter, sort, pagination, &c) if err2 != nil { diff --git a/internal/infrastructure/mongo/dataset.go b/internal/infrastructure/mongo/dataset.go index 222905dd..9ed67a50 100644 --- a/internal/infrastructure/mongo/dataset.go +++ b/internal/infrastructure/mongo/dataset.go @@ -34,15 +34,13 @@ func (r *datasetRepo) init() { } func (r *datasetRepo) FindByID(ctx context.Context, id2 id.DatasetID, f []id.SceneID) (*dataset.Dataset, error) { - filter := r.sceneFilter(bson.D{{Key: "id", Value: id.ID(id2).String()}}, f) + filter := r.sceneFilter(bson.M{"id": id.ID(id2).String()}, f) return r.findOne(ctx, filter) } func (r *datasetRepo) FindByIDs(ctx context.Context, ids []id.DatasetID, f []id.SceneID) (dataset.List, error) { - filter := r.sceneFilter(bson.D{ - {Key: "id", Value: bson.D{ - {Key: "$in", Value: id.DatasetIDsToStrings(ids)}, - }}, + filter := r.sceneFilter(bson.M{ + "id": bson.M{"$in": id.DatasetIDsToStrings(ids)}, }, f) dst := make([]*dataset.Dataset, 0, len(ids)) res, err := r.find(ctx, dst, filter) @@ -53,16 +51,15 @@ func (r *datasetRepo) FindByIDs(ctx context.Context, ids []id.DatasetID, f []id. } func (r *datasetRepo) FindBySchema(ctx context.Context, schemaID id.DatasetSchemaID, f []id.SceneID, pagination *usecase.Pagination) (dataset.List, *usecase.PageInfo, error) { - filter := r.sceneFilter(bson.D{ - {Key: "schema", Value: id.ID(schemaID).String()}, + filter := r.sceneFilter(bson.M{ + "schema": id.ID(schemaID).String(), }, f) return r.paginate(ctx, filter, pagination) } func (r *datasetRepo) FindBySchemaAll(ctx context.Context, schemaID id.DatasetSchemaID) (dataset.List, error) { - filter := bson.D{ - {Key: "schema", Value: id.ID(schemaID).String()}, - } + filter := bson.M{"schema": id.ID(schemaID).String()} + return r.find(ctx, nil, filter) } @@ -87,9 +84,9 @@ func (r *datasetRepo) FindGraph(ctx context.Context, did id.DatasetID, f []id.Sc } pipeline := bson.D{ - {Key: "$match", Value: r.sceneFilter(bson.D{ - {Key: "id", Value: did.String()}, - {Key: "fields.id", Value: firstField}, + {Key: "$match", Value: r.sceneFilter(bson.M{ + "id": did.String(), + "fields.id": firstField, }, f)}, {Key: "$limit", Value: 1}, {Key: "$addFields", Value: bson.D{ @@ -116,7 +113,7 @@ func (r *datasetRepo) FindGraph(ctx context.Context, did id.DatasetID, f []id.Sc {Key: "connectToField", Value: "id"}, {Key: "depthField", Value: "depth"}, {Key: "as", Value: "graph"}, - {Key: "restrictSearchWithMatch", Value: r.sceneFilter(bson.D{}, f)}, + {Key: "restrictSearchWithMatch", Value: r.sceneFilter(bson.M{}, f)}, }}, {Key: "$addFields", Value: bson.D{ {Key: "firstGraph", Value: bson.D{ @@ -285,7 +282,7 @@ func (r *datasetRepo) RemoveByScene(ctx context.Context, sceneID id.SceneID) err return nil } -func (r *datasetRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) (dataset.List, *usecase.PageInfo, error) { +func (r *datasetRepo) paginate(ctx context.Context, filter bson.M, pagination *usecase.Pagination) (dataset.List, *usecase.PageInfo, error) { var c mongodoc.DatasetConsumer pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { @@ -294,7 +291,7 @@ func (r *datasetRepo) paginate(ctx context.Context, filter bson.D, pagination *u return c.Rows, pageInfo, nil } -func (r *datasetRepo) find(ctx context.Context, dst dataset.List, filter bson.D) (dataset.List, error) { +func (r *datasetRepo) find(ctx context.Context, dst dataset.List, filter bson.M) (dataset.List, error) { c := mongodoc.DatasetConsumer{ Rows: dst, } @@ -304,7 +301,7 @@ func (r *datasetRepo) find(ctx context.Context, dst dataset.List, filter bson.D) return c.Rows, nil } -func (r *datasetRepo) findOne(ctx context.Context, filter bson.D) (*dataset.Dataset, error) { +func (r *datasetRepo) findOne(ctx context.Context, filter bson.M) (*dataset.Dataset, error) { dst := make([]*dataset.Dataset, 0, 1) c := mongodoc.DatasetConsumer{ Rows: dst, @@ -330,13 +327,10 @@ func filterDatasets(ids []id.DatasetID, rows []*dataset.Dataset) []*dataset.Data return res } -func (*datasetRepo) sceneFilter(filter bson.D, scenes []id.SceneID) bson.D { +func (*datasetRepo) sceneFilter(filter bson.M, scenes []id.SceneID) bson.M { if scenes == nil { return filter } - filter = append(filter, bson.E{ - Key: "scene", - Value: bson.D{{Key: "$in", Value: id.SceneIDsToStrings(scenes)}}, - }) + filter["scene"] = bson.M{"$in": id.SceneIDsToStrings(scenes)} return filter } diff --git a/internal/infrastructure/mongo/dataset_schema.go b/internal/infrastructure/mongo/dataset_schema.go index 09b11db3..f28d28e4 100644 --- a/internal/infrastructure/mongo/dataset_schema.go +++ b/internal/infrastructure/mongo/dataset_schema.go @@ -53,9 +53,8 @@ func (r *datasetSchemaRepo) FindByIDs(ctx context.Context, ids []id.DatasetSchem } func (r *datasetSchemaRepo) FindByScene(ctx context.Context, sceneID id.SceneID, pagination *usecase.Pagination) (dataset.SchemaList, *usecase.PageInfo, error) { - filter := bson.D{ - {Key: "scene", Value: sceneID.String()}, - } + filter := bson.M{"scene": sceneID.String()} + return r.paginate(ctx, filter, pagination) } @@ -146,7 +145,7 @@ func (r *datasetSchemaRepo) findOne(ctx context.Context, filter bson.D) (*datase return c.Rows[0], nil } -func (r *datasetSchemaRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) ([]*dataset.Schema, *usecase.PageInfo, error) { +func (r *datasetSchemaRepo) paginate(ctx context.Context, filter bson.M, pagination *usecase.Pagination) ([]*dataset.Schema, *usecase.PageInfo, error) { var c mongodoc.DatasetSchemaConsumer pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 09ba85b1..8ab6aa7e 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -165,7 +165,7 @@ func getCursor(raw bson.Raw, key string) (*usecase.Cursor, error) { return &c, nil } -func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, sort *string, p *Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort *string, p *Pagination, consumer Consumer) (*usecase.PageInfo, error) { if p == nil { return nil, nil } @@ -179,9 +179,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s sortKey = *sort } - findOptions.Sort = bson.D{ - {Key: sortKey, Value: p.SortDirection()}, - } + findOptions.Sort = bson.M{sortKey: p.SortDirection()} key := "id" @@ -196,9 +194,15 @@ func (c *Client) Paginate(ctx context.Context, col string, filter interface{}, s } if cur != nil { - filter = appendE(filter, bson.E{Key: key, Value: bson.D{ - {Key: op, Value: *cur}, - }}) + // TODO: Pagination query with sort field + paginationFilter := bson.M{key: bson.M{op: *cur}} + + filter = bson.M{ + "$and": []bson.M{ + filter, + paginationFilter, + }, + } } // 更に読める要素があるのか確かめるために一つ多めに読み出す diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index 7231aadf..84417fe1 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -4,6 +4,7 @@ import ( "context" "github.com/reearth/reearth-backend/internal/usecase" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) @@ -28,7 +29,7 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 return c.Client.Count(ctx, c.CollectionName, filter) } -func (c *ClientCollection) Paginate(ctx context.Context, filter interface{}, sort *string, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { +func (c *ClientCollection) Paginate(ctx context.Context, filter bson.M, sort *string, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { return c.Client.Paginate(ctx, c.CollectionName, filter, sort, NewPaginationFrom(p), consumer) } diff --git a/internal/infrastructure/mongo/project.go b/internal/infrastructure/mongo/project.go index bdabc5cb..b69799c0 100644 --- a/internal/infrastructure/mongo/project.go +++ b/internal/infrastructure/mongo/project.go @@ -53,9 +53,8 @@ func (r *projectRepo) FindByID(ctx context.Context, id id.ProjectID, f []id.Team } func (r *projectRepo) FindByTeam(ctx context.Context, id id.TeamID, pagination *usecase.Pagination) ([]*project.Project, *usecase.PageInfo, error) { - filter := bson.D{ - {Key: "team", Value: id.String()}, - } + filter := bson.M{"team": id.String()} + return r.paginate(ctx, filter, pagination) } @@ -113,7 +112,7 @@ func (r *projectRepo) findOne(ctx context.Context, filter bson.D) (*project.Proj return c.Rows[0], nil } -func (r *projectRepo) paginate(ctx context.Context, filter bson.D, pagination *usecase.Pagination) ([]*project.Project, *usecase.PageInfo, error) { +func (r *projectRepo) paginate(ctx context.Context, filter bson.M, pagination *usecase.Pagination) ([]*project.Project, *usecase.PageInfo, error) { var c mongodoc.ProjectConsumer pageInfo, err2 := r.client.Paginate(ctx, filter, nil, pagination, &c) if err2 != nil { diff --git a/pkg/asset/sort_type.go b/pkg/asset/sort_type.go new file mode 100644 index 00000000..ab44259a --- /dev/null +++ b/pkg/asset/sort_type.go @@ -0,0 +1,37 @@ +package asset + +import ( + "errors" + "strings" +) + +var ( + SortTypeID = SortType("id") + SortTypeName = SortType("name") + SortTypeSize = SortType("size") + + ErrInvalidSortType = errors.New("invalid sort type") +) + +type SortType string + +func check(role SortType) bool { + switch role { + case SortTypeID: + return true + case SortTypeName: + return true + case SortTypeSize: + return true + } + return false +} + +func SortTypeFromString(r string) (SortType, error) { + role := SortType(strings.ToLower(r)) + + if check(role) { + return role, nil + } + return role, ErrInvalidSortType +} diff --git a/pkg/asset/sort_type_test.go b/pkg/asset/sort_type_test.go new file mode 100644 index 00000000..4e5cf49c --- /dev/null +++ b/pkg/asset/sort_type_test.go @@ -0,0 +1,79 @@ +package asset + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSortTypeFromString(t *testing.T) { + tests := []struct { + Name, Role string + Expected SortType + Err error + }{ + { + Name: "Success id", + Role: "id", + Expected: SortType("id"), + Err: nil, + }, + { + Name: "fail invalid sort type", + Role: "xxx", + Expected: SortType("xxx"), + Err: ErrInvalidSortType, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.Name, func(t *testing.T) { + t.Parallel() + res, err := SortTypeFromString(tt.Role) + if tt.Err == nil { + assert.Equal(t, tt.Expected, res) + } else { + assert.Equal(t, tt.Err, err) + } + }) + } +} + +func TestCheck(t *testing.T) { + tests := []struct { + Name string + Input SortType + Expected bool + }{ + { + Name: "check id", + Input: SortType("id"), + Expected: true, + }, + { + Name: "check name", + Input: SortType("name"), + Expected: true, + }, + { + Name: "check size", + Input: SortType("size"), + Expected: true, + }, + { + Name: "check unknown sort type", + Input: SortType("xxx"), + Expected: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.Name, func(t *testing.T) { + t.Parallel() + res := check(tt.Input) + assert.Equal(t, tt.Expected, res) + }) + } +} From d8c5c3c9d55e3e64ee33214bebb814093bdedba6 Mon Sep 17 00:00:00 2001 From: yk Date: Thu, 3 Mar 2022 23:50:11 +0300 Subject: [PATCH 12/18] support sort with pagination --- .../infrastructure/mongo/mongodoc/client.go | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 66304571..0488bf66 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -207,18 +207,20 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * return nil, nil } coll := c.Collection(col) + const key = "id" findOptions := options.Find() findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) - sortKey := "id" - if sort != nil && len(*sort) > 0 { + var sortOptions bson.D + var sortKey = "" + if sort != nil && len(*sort) > 0 && *sort != "id" { sortKey = *sort + sortOptions = append(sortOptions, bson.E{Key: sortKey, Value: p.SortDirection()}) } + sortOptions = append(sortOptions, bson.E{Key: key, Value: p.SortDirection()}) - findOptions.Sort = bson.M{sortKey: p.SortDirection()} - - key := "id" + findOptions.Sort = sortOptions count, err := coll.CountDocuments(ctx, filter) if err != nil { @@ -231,8 +233,29 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * } if cur != nil { - // TODO: Pagination query with sort field - paginationFilter := bson.M{key: bson.M{op: *cur}} + + var paginationFilter bson.M + + if sortKey == "" { + paginationFilter = bson.M{key: bson.M{op: *cur}} + } else { + var curObj bson.M + if err := coll.FindOne(ctx, bson.M{key: *cur}).Decode(&curObj); err != nil { + return nil, fmt.Errorf("failed to find cursor") + } + if curObj[sortKey] == nil { + return nil, fmt.Errorf("invalied sort key") + } + paginationFilter = bson.M{ + "$or": []bson.M{ + {sortKey: bson.M{"$gt": curObj[sortKey]}}, + { + sortKey: curObj[sortKey], + key: bson.M{"$gt": *cur}, + }, + }, + } + } filter = bson.M{ "$and": []bson.M{ From c73241916bf6e3090f2c764f3d3bd71f7a1570c4 Mon Sep 17 00:00:00 2001 From: yk Date: Mon, 7 Mar 2022 11:24:42 +0300 Subject: [PATCH 13/18] - remove unused code --- internal/infrastructure/mongo/mongodoc/util.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/util.go b/internal/infrastructure/mongo/mongodoc/util.go index e2e144c1..adbcd42c 100644 --- a/internal/infrastructure/mongo/mongodoc/util.go +++ b/internal/infrastructure/mongo/mongodoc/util.go @@ -18,19 +18,3 @@ func convertDToM(i interface{}) interface{} { } return i } - -func appendE(f interface{}, elements ...bson.E) interface{} { - switch f2 := f.(type) { - case bson.D: - for _, e := range elements { - f2 = append(f2, e) - } - return f2 - case bson.M: - for _, e := range elements { - f2[e.Key] = e.Value - } - return f2 - } - return f -} From 7b8176b7842a16cc8ba9deabe67286afb637a1a9 Mon Sep 17 00:00:00 2001 From: yk Date: Wed, 9 Mar 2022 10:45:21 +0300 Subject: [PATCH 14/18] - refactor pagination code --- internal/adapter/gql/loader_asset.go | 2 +- internal/infrastructure/memory/asset.go | 2 +- internal/infrastructure/mongo/asset.go | 12 +- .../infrastructure/mongo/mongodoc/client.go | 107 ++++++++++-------- internal/usecase/interactor/asset.go | 8 +- internal/usecase/interfaces/asset.go | 2 +- internal/usecase/repo/asset.go | 8 +- 7 files changed, 81 insertions(+), 60 deletions(-) diff --git a/internal/adapter/gql/loader_asset.go b/internal/adapter/gql/loader_asset.go index 3240d397..fdf9159e 100644 --- a/internal/adapter/gql/loader_asset.go +++ b/internal/adapter/gql/loader_asset.go @@ -36,7 +36,7 @@ func (c *AssetLoader) Fetch(ctx context.Context, ids []id.AssetID) ([]*gqlmodel. func (c *AssetLoader) FindByTeam(ctx context.Context, teamID id.ID, keyword *string, sort *asset.SortType, pagination *gqlmodel.Pagination) (*gqlmodel.AssetConnection, error) { p := gqlmodel.ToPagination(pagination) - assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, (*string)(sort), p, getOperator(ctx)) + assets, pi, err := c.usecase.FindByTeam(ctx, id.TeamID(teamID), keyword, sort, p, getOperator(ctx)) if err != nil { return nil, err } diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index bd5b9fb2..ff06b1cf 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -66,7 +66,7 @@ func (r *Asset) Remove(ctx context.Context, id id.AssetID) error { return nil } -func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, filter repo.AssetFilter) ([]*asset.Asset, *usecase.PageInfo, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/asset.go b/internal/infrastructure/mongo/asset.go index 0111d6e8..a5f29534 100644 --- a/internal/infrastructure/mongo/asset.go +++ b/internal/infrastructure/mongo/asset.go @@ -53,12 +53,12 @@ func (r *assetRepo) Remove(ctx context.Context, id id.AssetID) error { return r.client.RemoveOne(ctx, id.String()) } -func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *string, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, uFilter repo.AssetFilter) ([]*asset.Asset, *usecase.PageInfo, error) { filter := bson.M{"team": id.String()} - if keyword != nil { + if uFilter.Keyword != nil { keywordFilter := bson.M{"name": bson.M{ - "$regex": primitive.Regex{Pattern: fmt.Sprintf(".*%s.*", *keyword), Options: "i"}}, + "$regex": primitive.Regex{Pattern: fmt.Sprintf(".*%s.*", *uFilter.Keyword), Options: "i"}}, } filter = bson.M{"$and": []bson.M{ filter, @@ -66,7 +66,7 @@ func (r *assetRepo) FindByTeam(ctx context.Context, id id.TeamID, keyword *strin }} } - return r.paginate(ctx, filter, sort, pagination) + return r.paginate(ctx, filter, uFilter.Sort, uFilter.Pagination) } func (r *assetRepo) init() { @@ -76,9 +76,9 @@ func (r *assetRepo) init() { } } -func (r *assetRepo) paginate(ctx context.Context, filter bson.M, sort *string, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { +func (r *assetRepo) paginate(ctx context.Context, filter bson.M, sort *asset.SortType, pagination *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) { var c mongodoc.AssetConsumer - pageInfo, err2 := r.client.Paginate(ctx, filter, sort, pagination, &c) + pageInfo, err2 := r.client.Paginate(ctx, filter, (*string)(sort), pagination, &c) if err2 != nil { return nil, nil, rerror.ErrInternalBy(err2) } diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 0488bf66..367d72a1 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -209,16 +209,9 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * coll := c.Collection(col) const key = "id" - findOptions := options.Find() - findOptions.SetCollation(&options.Collation{Strength: 1, Locale: "en"}) + findOptions := options.Find().SetCollation(&options.Collation{Strength: 1, Locale: "en"}) - var sortOptions bson.D - var sortKey = "" - if sort != nil && len(*sort) > 0 && *sort != "id" { - sortKey = *sort - sortOptions = append(sortOptions, bson.E{Key: sortKey, Value: p.SortDirection()}) - } - sortOptions = append(sortOptions, bson.E{Key: key, Value: p.SortDirection()}) + sortOptions, sortKey := sortOptionsFrom(sort, p, key) findOptions.Sort = sortOptions @@ -227,48 +220,15 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * return nil, fmt.Errorf("failed to count documents: %v", err.Error()) } - limit, op, cur, err := p.Parameters() + filter, limit, err := paginationFilter(ctx, coll, p, sortKey, key, filter) if err != nil { - return nil, fmt.Errorf("failed to parse pagination parameters: %w", err) - } - - if cur != nil { - - var paginationFilter bson.M - - if sortKey == "" { - paginationFilter = bson.M{key: bson.M{op: *cur}} - } else { - var curObj bson.M - if err := coll.FindOne(ctx, bson.M{key: *cur}).Decode(&curObj); err != nil { - return nil, fmt.Errorf("failed to find cursor") - } - if curObj[sortKey] == nil { - return nil, fmt.Errorf("invalied sort key") - } - paginationFilter = bson.M{ - "$or": []bson.M{ - {sortKey: bson.M{"$gt": curObj[sortKey]}}, - { - sortKey: curObj[sortKey], - key: bson.M{"$gt": *cur}, - }, - }, - } - } - - filter = bson.M{ - "$and": []bson.M{ - filter, - paginationFilter, - }, - } + return nil, err } // 更に読める要素があるのか確かめるために一つ多めに読み出す // Read one more element so that we can see whether there's a further one - limit++ - findOptions.Limit = &limit + *limit++ + findOptions.Limit = limit cursor, err := coll.Find(ctx, filter, findOptions) if err != nil { @@ -278,7 +238,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * _ = cursor.Close(ctx) }() - results := make([]bson.Raw, 0, limit) + results := make([]bson.Raw, 0, *limit) for cursor.Next(ctx) { raw := make(bson.Raw, len(cursor.Current)) copy(raw, cursor.Current) @@ -289,7 +249,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * } hasMore := false - if len(results) == int(limit) { + if len(results) == int(*limit) { hasMore = true // Remove the extra one reading. results = results[:len(results)-1] @@ -330,6 +290,57 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * return usecase.NewPageInfo(int(count), startCursor, endCursor, hasNextPage, hasPreviousPage), nil } +func sortOptionsFrom(sort *string, p *Pagination, key string) (bson.D, string) { + var sortOptions bson.D + var sortKey = "" + if sort != nil && len(*sort) > 0 && *sort != "id" { + sortKey = *sort + sortOptions = append(sortOptions, bson.E{Key: sortKey, Value: p.SortDirection()}) + } + sortOptions = append(sortOptions, bson.E{Key: key, Value: p.SortDirection()}) + return sortOptions, sortKey +} + +func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination, sortKey, key string, filter bson.M) (bson.M, *int64, error) { + limit, op, cur, err := p.Parameters() + if err != nil { + return nil, nil, fmt.Errorf("failed to parse pagination parameters: %w", err) + } + + if cur != nil { + var paginationFilter bson.M + + if sortKey == "" { + paginationFilter = bson.M{key: bson.M{op: *cur}} + } else { + var curObj bson.M + if err := coll.FindOne(ctx, bson.M{key: *cur}).Decode(&curObj); err != nil { + return nil, nil, fmt.Errorf("failed to find cursor element") + } + if curObj[sortKey] == nil { + return nil, nil, fmt.Errorf("invalied sort key") + } + paginationFilter = bson.M{ + "$or": []bson.M{ + {sortKey: bson.M{"$gt": curObj[sortKey]}}, + { + sortKey: curObj[sortKey], + key: bson.M{"$gt": *cur}, + }, + }, + } + } + + filter = bson.M{ + "$and": []bson.M{ + filter, + paginationFilter, + }, + } + } + return filter, &limit, nil +} + func (c *Client) CreateIndex(ctx context.Context, col string, keys []string) []string { coll := c.Collection(col) indexedKeys := indexes(ctx, coll) diff --git a/internal/usecase/interactor/asset.go b/internal/usecase/interactor/asset.go index 5aa180b5..9d2df7fc 100644 --- a/internal/usecase/interactor/asset.go +++ b/internal/usecase/interactor/asset.go @@ -34,12 +34,16 @@ func (i *Asset) Fetch(ctx context.Context, assets []id.AssetID, operator *usecas return i.assetRepo.FindByIDs(ctx, assets, operator.ReadableTeams) } -func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, keyword *string, sort *string, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { +func (i *Asset) FindByTeam(ctx context.Context, tid id.TeamID, keyword *string, sort *asset.SortType, p *usecase.Pagination, operator *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) { if err := i.CanReadTeam(tid, operator); err != nil { return nil, nil, err } - return i.assetRepo.FindByTeam(ctx, tid, keyword, sort, p) + return i.assetRepo.FindByTeam(ctx, tid, repo.AssetFilter{ + Sort: sort, + Keyword: keyword, + Pagination: p, + }) } func (i *Asset) Create(ctx context.Context, inp interfaces.CreateAssetParam, operator *usecase.Operator) (result *asset.Asset, err error) { diff --git a/internal/usecase/interfaces/asset.go b/internal/usecase/interfaces/asset.go index 6ce150ee..1028d7b2 100644 --- a/internal/usecase/interfaces/asset.go +++ b/internal/usecase/interfaces/asset.go @@ -29,7 +29,7 @@ var ( type Asset interface { Fetch(context.Context, []id.AssetID, *usecase.Operator) ([]*asset.Asset, error) - FindByTeam(context.Context, id.TeamID, *string, *string, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, *string, *asset.SortType, *usecase.Pagination, *usecase.Operator) ([]*asset.Asset, *usecase.PageInfo, error) Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, error) Remove(context.Context, id.AssetID, *usecase.Operator) (id.AssetID, error) } diff --git a/internal/usecase/repo/asset.go b/internal/usecase/repo/asset.go index 30131275..3e4233aa 100644 --- a/internal/usecase/repo/asset.go +++ b/internal/usecase/repo/asset.go @@ -8,10 +8,16 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) +type AssetFilter struct { + Sort *asset.SortType + Keyword *string + Pagination *usecase.Pagination +} + type Asset interface { Save(context.Context, *asset.Asset) error Remove(context.Context, id.AssetID) error - FindByTeam(context.Context, id.TeamID, *string, *string, *usecase.Pagination) ([]*asset.Asset, *usecase.PageInfo, error) + FindByTeam(context.Context, id.TeamID, AssetFilter) ([]*asset.Asset, *usecase.PageInfo, error) FindByID(context.Context, id.AssetID, []id.TeamID) (*asset.Asset, error) FindByIDs(context.Context, []id.AssetID, []id.TeamID) ([]*asset.Asset, error) } From d3bf277d42d553b0fe31d2e0edc4c7669062aaca Mon Sep 17 00:00:00 2001 From: yk Date: Thu, 10 Mar 2022 13:25:39 +0300 Subject: [PATCH 15/18] - fix paginationFilter return type --- .../infrastructure/mongo/mongodoc/client.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index 367d72a1..d84b3437 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -227,8 +227,8 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * // 更に読める要素があるのか確かめるために一つ多めに読み出す // Read one more element so that we can see whether there's a further one - *limit++ - findOptions.Limit = limit + limit++ + findOptions.Limit = &limit cursor, err := coll.Find(ctx, filter, findOptions) if err != nil { @@ -238,7 +238,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * _ = cursor.Close(ctx) }() - results := make([]bson.Raw, 0, *limit) + results := make([]bson.Raw, 0, limit) for cursor.Next(ctx) { raw := make(bson.Raw, len(cursor.Current)) copy(raw, cursor.Current) @@ -249,7 +249,7 @@ func (c *Client) Paginate(ctx context.Context, col string, filter bson.M, sort * } hasMore := false - if len(results) == int(*limit) { + if len(results) == int(limit) { hasMore = true // Remove the extra one reading. results = results[:len(results)-1] @@ -301,10 +301,10 @@ func sortOptionsFrom(sort *string, p *Pagination, key string) (bson.D, string) { return sortOptions, sortKey } -func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination, sortKey, key string, filter bson.M) (bson.M, *int64, error) { +func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination, sortKey, key string, filter bson.M) (bson.M, int64, error) { limit, op, cur, err := p.Parameters() if err != nil { - return nil, nil, fmt.Errorf("failed to parse pagination parameters: %w", err) + return nil, 0, fmt.Errorf("failed to parse pagination parameters: %w", err) } if cur != nil { @@ -315,10 +315,10 @@ func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination } else { var curObj bson.M if err := coll.FindOne(ctx, bson.M{key: *cur}).Decode(&curObj); err != nil { - return nil, nil, fmt.Errorf("failed to find cursor element") + return nil, 0, fmt.Errorf("failed to find cursor element") } if curObj[sortKey] == nil { - return nil, nil, fmt.Errorf("invalied sort key") + return nil, 0, fmt.Errorf("invalied sort key") } paginationFilter = bson.M{ "$or": []bson.M{ @@ -338,7 +338,7 @@ func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination }, } } - return filter, &limit, nil + return filter, limit, nil } func (c *Client) CreateIndex(ctx context.Context, col string, keys []string) []string { From f9abc627d47b7f59e9edcc7e1449d158eedae877 Mon Sep 17 00:00:00 2001 From: yk Date: Thu, 10 Mar 2022 14:36:14 +0300 Subject: [PATCH 16/18] - code refactoring --- internal/infrastructure/mongo/mongodoc/clientcol.go | 2 +- internal/infrastructure/mongo/mongodoc/pagination.go | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/clientcol.go b/internal/infrastructure/mongo/mongodoc/clientcol.go index 0e7d2316..5db3a1f5 100644 --- a/internal/infrastructure/mongo/mongodoc/clientcol.go +++ b/internal/infrastructure/mongo/mongodoc/clientcol.go @@ -30,7 +30,7 @@ func (c *ClientCollection) Count(ctx context.Context, filter interface{}) (int64 } func (c *ClientCollection) Paginate(ctx context.Context, filter bson.M, sort *string, p *usecase.Pagination, consumer Consumer) (*usecase.PageInfo, error) { - return c.Client.Paginate(ctx, c.CollectionName, filter, sort, NewPaginationFrom(p), consumer) + return c.Client.Paginate(ctx, c.CollectionName, filter, sort, PaginationFrom(p), consumer) } func (c *ClientCollection) SaveOne(ctx context.Context, id string, replacement interface{}) error { diff --git a/internal/infrastructure/mongo/mongodoc/pagination.go b/internal/infrastructure/mongo/mongodoc/pagination.go index f5787afc..ce0f7504 100644 --- a/internal/infrastructure/mongo/mongodoc/pagination.go +++ b/internal/infrastructure/mongo/mongodoc/pagination.go @@ -13,7 +13,7 @@ type Pagination struct { Last *int } -func NewPaginationFrom(pagination *usecase.Pagination) *Pagination { +func PaginationFrom(pagination *usecase.Pagination) *Pagination { if pagination == nil { return nil } @@ -26,10 +26,7 @@ func NewPaginationFrom(pagination *usecase.Pagination) *Pagination { } func (p *Pagination) SortDirection() int { - if p == nil { - return 1 - } - if p.Last != nil { + if p != nil && p.Last != nil { return -1 } return 1 From b60c5faceb591bf86e64430a0f26d86a7904614f Mon Sep 17 00:00:00 2001 From: yk Date: Mon, 14 Mar 2022 10:59:08 +0300 Subject: [PATCH 17/18] - fix pagination when it contains reverse order --- internal/infrastructure/mongo/mongodoc/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/client.go b/internal/infrastructure/mongo/mongodoc/client.go index d84b3437..e8a53a82 100644 --- a/internal/infrastructure/mongo/mongodoc/client.go +++ b/internal/infrastructure/mongo/mongodoc/client.go @@ -322,10 +322,10 @@ func paginationFilter(ctx context.Context, coll *mongo.Collection, p *Pagination } paginationFilter = bson.M{ "$or": []bson.M{ - {sortKey: bson.M{"$gt": curObj[sortKey]}}, + {sortKey: bson.M{op: curObj[sortKey]}}, { sortKey: curObj[sortKey], - key: bson.M{"$gt": *cur}, + key: bson.M{op: *cur}, }, }, } From 258aad9dda2f8a9851b17985d4b921cf7e080f3d Mon Sep 17 00:00:00 2001 From: rot1024 Date: Tue, 15 Mar 2022 14:25:41 +0900 Subject: [PATCH 18/18] fix memory --- internal/infrastructure/memory/asset.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/infrastructure/memory/asset.go b/internal/infrastructure/memory/asset.go index dcaa6497..557023f9 100644 --- a/internal/infrastructure/memory/asset.go +++ b/internal/infrastructure/memory/asset.go @@ -2,6 +2,8 @@ package memory import ( "context" + "sort" + "strings" "sync" "github.com/reearth/reearth-backend/internal/usecase" @@ -69,11 +71,27 @@ func (r *Asset) FindByTeam(ctx context.Context, id id.TeamID, filter repo.AssetF result := []*asset.Asset{} for _, d := range r.data { - if d.Team() == id { + if d.Team() == id && (filter.Keyword == nil || strings.Contains(d.Name(), *filter.Keyword)) { result = append(result, d) } } + if filter.Sort != nil { + s := *filter.Sort + sort.SliceStable(result, func(i, j int) bool { + if s == asset.SortTypeID { + return result[i].ID().ID().Compare(result[j].ID().ID()) < 0 + } + if s == asset.SortTypeSize { + return result[i].Size() < result[j].Size() + } + if s == asset.SortTypeName { + return strings.Compare(result[i].Name(), result[j].Name()) < 0 + } + return false + }) + } + var startCursor, endCursor *usecase.Cursor if len(result) > 0 { _startCursor := usecase.Cursor(result[0].ID().String())