diff --git a/client/internal/http/object.go b/client/internal/http/object.go index 6d140f0..34acdfd 100644 --- a/client/internal/http/object.go +++ b/client/internal/http/object.go @@ -39,14 +39,14 @@ func (s *ObjectService) ListByPath(ctx *gin.Context) { return } - objs, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, req.IsPrefix) + coms, objs, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, req.IsPrefix, req.NoRecursive) if err != nil { log.Warnf("listing objects: %s", err.Error()) ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err))) return } - ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByPathResp{Objects: objs})) + ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByPathResp{CommonPrefixes: coms, Objects: objs})) } func (s *ObjectService) ListByIDs(ctx *gin.Context) { @@ -191,7 +191,7 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) { return } - obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false) + _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false, false) if err != nil { log.Warnf("getting object by path: %s", err.Error()) ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed")) @@ -266,7 +266,7 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) { return } - obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, true) + _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, true, false) if err != nil { log.Warnf("getting object by path: %s", err.Error()) ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed")) @@ -343,7 +343,7 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) { return } - obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false) + _, obj, err := s.svc.ObjectSvc().GetByPath(req.UserID, req.PackageID, req.Path, false, false) if err != nil { log.Warnf("getting object by path: %s", err.Error()) ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed")) diff --git a/client/internal/services/object.go b/client/internal/services/object.go index 4c2478d..1476d35 100644 --- a/client/internal/services/object.go +++ b/client/internal/services/object.go @@ -22,19 +22,19 @@ func (svc *Service) ObjectSvc() *ObjectService { return &ObjectService{Service: svc} } -func (svc *ObjectService) GetByPath(userID cdssdk.UserID, pkgID cdssdk.PackageID, path string, isPrefix bool) ([]cdssdk.Object, error) { +func (svc *ObjectService) GetByPath(userID cdssdk.UserID, pkgID cdssdk.PackageID, path string, isPrefix bool, noRecursive bool) ([]string, []cdssdk.Object, error) { coorCli, err := stgglb.CoordinatorMQPool.Acquire() if err != nil { - return nil, fmt.Errorf("new coordinator client: %w", err) + return nil, nil, fmt.Errorf("new coordinator client: %w", err) } defer stgglb.CoordinatorMQPool.Release(coorCli) - listResp, err := coorCli.GetObjectsByPath(coormq.ReqGetObjectsByPath(userID, pkgID, path, isPrefix)) + listResp, err := coorCli.GetObjectsByPath(coormq.ReqGetObjectsByPath(userID, pkgID, path, isPrefix, noRecursive)) if err != nil { - return nil, fmt.Errorf("requsting to coodinator: %w", err) + return nil, nil, fmt.Errorf("requsting to coodinator: %w", err) } - return listResp.Objects, nil + return listResp.CommonPrefixes, listResp.Objects, nil } func (svc *ObjectService) GetByIDs(userID cdssdk.UserID, objectIDs []cdssdk.ObjectID) ([]*cdssdk.Object, error) { diff --git a/common/pkgs/db2/object.go b/common/pkgs/db2/object.go index 6d86e30..9f0cef3 100644 --- a/common/pkgs/db2/object.go +++ b/common/pkgs/db2/object.go @@ -2,6 +2,7 @@ package db2 import ( "fmt" + "strings" "time" "gorm.io/gorm/clause" @@ -38,6 +39,44 @@ func (db *ObjectDB) GetWithPathPrefix(ctx SQLContext, packageID cdssdk.PackageID return ret, err } +func (db *ObjectDB) GetCommonPrefixes(ctx SQLContext, packageID cdssdk.PackageID, pathPrefix string) ([]string, error) { + var ret []string + + sepCnt := strings.Count(pathPrefix, cdssdk.ObjectPathSeparator) + 1 + + prefixStatm := fmt.Sprintf("Substring_Index(Path, '%s', %d)", cdssdk.ObjectPathSeparator, sepCnt) + + err := ctx.Table("Object").Select(prefixStatm+" as Prefix"). + Where("PackageID = ?", packageID). + Where("Path like ?", pathPrefix+"%"). + Where(prefixStatm + " <> Path"). + Group("Prefix").Find(&ret).Error + if err != nil { + return nil, err + } + + for i := range ret { + ret[i] = ret[i] + cdssdk.ObjectPathSeparator + } + + return ret, nil +} + +func (db *ObjectDB) GetDirectChildren(ctx SQLContext, packageID cdssdk.PackageID, pathPrefix string) ([]cdssdk.Object, error) { + var ret []cdssdk.Object + + sepCnt := strings.Count(pathPrefix, cdssdk.ObjectPathSeparator) + 1 + + prefixStatm := fmt.Sprintf("Substring_Index(Path, '%s', %d)", cdssdk.ObjectPathSeparator, sepCnt) + + err := ctx.Table("Object"). + Where("PackageID = ?", packageID). + Where("Path like ?", pathPrefix+"%"). + Where(prefixStatm + " = Path"). + Find(&ret).Error + return ret, err +} + func (db *ObjectDB) BatchTestObjectID(ctx SQLContext, objectIDs []cdssdk.ObjectID) (map[cdssdk.ObjectID]bool, error) { if len(objectIDs) == 0 { return make(map[cdssdk.ObjectID]bool), nil diff --git a/common/pkgs/mq/coordinator/object.go b/common/pkgs/mq/coordinator/object.go index ad63e6e..4ee0005 100644 --- a/common/pkgs/mq/coordinator/object.go +++ b/common/pkgs/mq/coordinator/object.go @@ -67,27 +67,31 @@ var _ = Register(Service.GetObjectsByPath) type GetObjectsByPath struct { mq.MessageBodyBase - UserID cdssdk.UserID `json:"userID"` - PackageID cdssdk.PackageID `json:"packageID"` - Path string `json:"path"` - IsPrefix bool `json:"isPrefix"` + UserID cdssdk.UserID `json:"userID"` + PackageID cdssdk.PackageID `json:"packageID"` + Path string `json:"path"` + IsPrefix bool `json:"isPrefix"` + NoRecursive bool `json:"noRecursive"` } type GetObjectsByPathResp struct { mq.MessageBodyBase - Objects []model.Object `json:"objects"` + CommonPrefixes []string `json:"commonPrefixes"` + Objects []model.Object `json:"objects"` } -func ReqGetObjectsByPath(userID cdssdk.UserID, packageID cdssdk.PackageID, path string, isPrefix bool) *GetObjectsByPath { +func ReqGetObjectsByPath(userID cdssdk.UserID, packageID cdssdk.PackageID, path string, isPrefix bool, noRecursive bool) *GetObjectsByPath { return &GetObjectsByPath{ - UserID: userID, - PackageID: packageID, - Path: path, - IsPrefix: isPrefix, + UserID: userID, + PackageID: packageID, + Path: path, + IsPrefix: isPrefix, + NoRecursive: noRecursive, } } -func RespGetObjectsByPath(objects []model.Object) *GetObjectsByPathResp { +func RespGetObjectsByPath(commonPrefixes []string, objects []model.Object) *GetObjectsByPathResp { return &GetObjectsByPathResp{ - Objects: objects, + CommonPrefixes: commonPrefixes, + Objects: objects, } } func (client *Client) GetObjectsByPath(msg *GetObjectsByPath) (*GetObjectsByPathResp, error) { diff --git a/coordinator/internal/mq/object.go b/coordinator/internal/mq/object.go index 1c2e256..fa5e012 100644 --- a/coordinator/internal/mq/object.go +++ b/coordinator/internal/mq/object.go @@ -56,6 +56,7 @@ func (svc *Service) GetObjects(msg *coormq.GetObjects) (*coormq.GetObjectsResp, } func (svc *Service) GetObjectsByPath(msg *coormq.GetObjectsByPath) (*coormq.GetObjectsByPathResp, *mq.CodeMessage) { + var coms []string var objs []cdssdk.Object err := svc.db2.DoTx(func(tx db2.SQLContext) error { var err error @@ -65,16 +66,31 @@ func (svc *Service) GetObjectsByPath(msg *coormq.GetObjectsByPath) (*coormq.GetO return fmt.Errorf("getting package by id: %w", err) } - if msg.IsPrefix { - objs, err = svc.db2.Object().GetWithPathPrefix(tx, msg.PackageID, msg.Path) - if err != nil { - return fmt.Errorf("getting objects with prefix: %w", err) - } - } else { + if !msg.IsPrefix { objs, err = svc.db2.Object().GetByPath(tx, msg.PackageID, msg.Path) if err != nil { return fmt.Errorf("getting object by path: %w", err) } + + return nil + } + + if !msg.NoRecursive { + objs, err = svc.db2.Object().GetWithPathPrefix(tx, msg.PackageID, msg.Path) + if err != nil { + return fmt.Errorf("getting objects with prefix: %w", err) + } + return nil + } + + coms, err = svc.db2.Object().GetCommonPrefixes(tx, msg.PackageID, msg.Path) + if err != nil { + return fmt.Errorf("getting common prefixes: %w", err) + } + + objs, err = svc.db2.Object().GetDirectChildren(tx, msg.PackageID, msg.Path) + if err != nil { + return fmt.Errorf("getting direct children: %w", err) } return nil @@ -84,7 +100,7 @@ func (svc *Service) GetObjectsByPath(msg *coormq.GetObjectsByPath) (*coormq.GetO return nil, mq.Failed(errorcode.OperationFailed, "get objects with prefix failed") } - return mq.ReplyOK(coormq.RespGetObjectsByPath(objs)) + return mq.ReplyOK(coormq.RespGetObjectsByPath(coms, objs)) } func (svc *Service) GetPackageObjects(msg *coormq.GetPackageObjects) (*coormq.GetPackageObjectsResp, *mq.CodeMessage) {