From d089d04025ff8f44930e5bab79e205f6e8ba43ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ga=C4=87e=C5=A1a?= Date: Wed, 14 Aug 2024 16:49:30 +0000 Subject: [PATCH] add space descendants data layer fn (#2511) * add space descendants data layer fn --- app/services/label/label_pullreq.go | 2 +- app/store/database.go | 12 ++- app/store/database/pullreq.go | 122 ++++++++++------------------ app/store/database/repo.go | 34 ++------ app/store/database/space.go | 104 ++++++++++++++++++++---- types/repo.go | 2 + types/space.go | 6 ++ 7 files changed, 154 insertions(+), 128 deletions(-) diff --git a/app/services/label/label_pullreq.go b/app/services/label/label_pullreq.go index ace666872..1d3dad383 100644 --- a/app/services/label/label_pullreq.go +++ b/app/services/label/label_pullreq.go @@ -241,7 +241,7 @@ func (s *Service) ListPullReqLabels( pullreqID int64, filter *types.AssignableLabelFilter, ) (*types.ScopesLabels, int64, error) { - spaces, err := s.spaceStore.GetHierarchy(ctx, spaceID) + spaces, err := s.spaceStore.GetAncestors(ctx, spaceID) if err != nil { return nil, 0, fmt.Errorf("failed to get space hierarchy: %w", err) } diff --git a/app/store/database.go b/app/store/database.go index fe6185b02..b78d32f15 100644 --- a/app/store/database.go +++ b/app/store/database.go @@ -169,10 +169,14 @@ type ( // GetAncestorIDs returns a list of all space IDs along the recursive path to the root space. GetAncestorIDs(ctx context.Context, spaceID int64) ([]int64, error) - GetHierarchy( - ctx context.Context, - spaceID int64, - ) ([]*types.Space, error) + // GetAncestors returns a list of all spaces along the recursive path to the root space. + GetAncestors(ctx context.Context, spaceID int64) ([]*types.Space, error) + + // GetAncestorsData returns a list of space parent data for spaces that are ancestors of the space. + GetAncestorsData(ctx context.Context, spaceID int64) ([]types.SpaceParentData, error) + + // GetDescendantsData returns a list of space parent data for spaces that are descendants of the space. + GetDescendantsData(ctx context.Context, spaceID int64) ([]types.SpaceParentData, error) // Create creates a new space Create(ctx context.Context, space *types.Space) error diff --git a/app/store/database/pullreq.go b/app/store/database/pullreq.go index 9d49b2856..26348d0f0 100644 --- a/app/store/database/pullreq.go +++ b/app/store/database/pullreq.go @@ -433,45 +433,7 @@ func (s *PullReqStore) Count(ctx context.Context, opts *types.PullReqFilter) (in } stmt = stmt.From("pullreqs") - if len(opts.States) == 1 { - stmt = stmt.Where("pullreq_state = ?", opts.States[0]) - } else if len(opts.States) > 1 { - stmt = stmt.Where(squirrel.Eq{"pullreq_state": opts.States}) - } - - if opts.SourceRepoID != 0 { - stmt = stmt.Where("pullreq_source_repo_id = ?", opts.SourceRepoID) - } - - if opts.SourceBranch != "" { - stmt = stmt.Where("pullreq_source_branch = ?", opts.SourceBranch) - } - - if opts.TargetRepoID != 0 { - stmt = stmt.Where("pullreq_target_repo_id = ?", opts.TargetRepoID) - } - - if opts.TargetBranch != "" { - stmt = stmt.Where("pullreq_target_branch = ?", opts.TargetBranch) - } - - if opts.Query != "" { - stmt = stmt.Where("LOWER(pullreq_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) - } - - if len(opts.CreatedBy) > 0 { - stmt = stmt.Where(squirrel.Eq{"pullreq_created_by": opts.CreatedBy}) - } - - if opts.CreatedLt > 0 { - stmt = stmt.Where("pullreq_created < ?", opts.CreatedLt) - } - - if opts.CreatedGt > 0 { - stmt = stmt.Where("pullreq_created > ?", opts.CreatedGt) - } - - setLabelKeyQuery(&stmt, opts) + s.applyFilter(&stmt, opts) sql, args, err := stmt.ToSql() if err != nil { @@ -500,45 +462,7 @@ func (s *PullReqStore) List(ctx context.Context, opts *types.PullReqFilter) ([]* } stmt = stmt.From("pullreqs") - if len(opts.States) == 1 { - stmt = stmt.Where("pullreq_state = ?", opts.States[0]) - } else if len(opts.States) > 1 { - stmt = stmt.Where(squirrel.Eq{"pullreq_state": opts.States}) - } - - if opts.SourceRepoID != 0 { - stmt = stmt.Where("pullreq_source_repo_id = ?", opts.SourceRepoID) - } - - if opts.SourceBranch != "" { - stmt = stmt.Where("pullreq_source_branch = ?", opts.SourceBranch) - } - - if opts.TargetRepoID != 0 { - stmt = stmt.Where("pullreq_target_repo_id = ?", opts.TargetRepoID) - } - - if opts.TargetBranch != "" { - stmt = stmt.Where("pullreq_target_branch = ?", opts.TargetBranch) - } - - if opts.Query != "" { - stmt = stmt.Where("LOWER(pullreq_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) - } - - if len(opts.CreatedBy) > 0 { - stmt = stmt.Where(squirrel.Eq{"pullreq_created_by": opts.CreatedBy}) - } - - if opts.CreatedLt > 0 { - stmt = stmt.Where("pullreq_created < ?", opts.CreatedLt) - } - - if opts.CreatedGt > 0 { - stmt = stmt.Where("pullreq_created > ?", opts.CreatedGt) - } - - setLabelKeyQuery(&stmt, opts) + s.applyFilter(&stmt, opts) stmt = stmt.Limit(database.Limit(opts.Size)) stmt = stmt.Offset(database.Offset(opts.Page, opts.Size)) @@ -570,7 +494,47 @@ func (s *PullReqStore) List(ctx context.Context, opts *types.PullReqFilter) ([]* return result, nil } -func setLabelKeyQuery(stmt *squirrel.SelectBuilder, opts *types.PullReqFilter) { +func (*PullReqStore) applyFilter(stmt *squirrel.SelectBuilder, opts *types.PullReqFilter) { + if len(opts.States) == 1 { + *stmt = stmt.Where("pullreq_state = ?", opts.States[0]) + } else if len(opts.States) > 1 { + *stmt = stmt.Where(squirrel.Eq{"pullreq_state": opts.States}) + } + + if opts.SourceRepoID != 0 { + *stmt = stmt.Where("pullreq_source_repo_id = ?", opts.SourceRepoID) + } + + if opts.SourceBranch != "" { + *stmt = stmt.Where("pullreq_source_branch = ?", opts.SourceBranch) + } + + if opts.TargetRepoID != 0 { + *stmt = stmt.Where("pullreq_target_repo_id = ?", opts.TargetRepoID) + } + + if opts.TargetBranch != "" { + *stmt = stmt.Where("pullreq_target_branch = ?", opts.TargetBranch) + } + + if opts.Query != "" { + *stmt = stmt.Where("LOWER(pullreq_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) + } + + if len(opts.CreatedBy) > 0 { + *stmt = stmt.Where(squirrel.Eq{"pullreq_created_by": opts.CreatedBy}) + } + + if opts.CreatedLt > 0 { + *stmt = stmt.Where("pullreq_created < ?", opts.CreatedLt) + } + + if opts.CreatedGt > 0 { + *stmt = stmt.Where("pullreq_created > ?", opts.CreatedGt) + } + + // labels + if len(opts.LabelID) == 0 && len(opts.ValueID) == 0 { return } diff --git a/app/store/database/repo.go b/app/store/database/repo.go index b14912fc9..bb84f0b1e 100644 --- a/app/store/database/repo.go +++ b/app/store/database/repo.go @@ -562,19 +562,9 @@ func (s *RepoStore) countAll( parentID int64, filter *types.RepoFilter, ) (int64, error) { - query := `WITH RECURSIVE SpaceHierarchy AS ( - SELECT space_id, space_parent_id - FROM spaces - WHERE space_id = $1 - - UNION - - SELECT s.space_id, s.space_parent_id - FROM spaces s - JOIN SpaceHierarchy h ON s.space_parent_id = h.space_id -) -SELECT space_id -FROM SpaceHierarchy h1;` + query := spaceDescendantsQuery + ` + SELECT space_descendant_id + FROM space_descendants` db := dbtx.GetAccessor(ctx, s.db) @@ -649,24 +639,14 @@ func (s *RepoStore) listAll( parentID int64, filter *types.RepoFilter, ) ([]*types.Repository, error) { - where := `WITH RECURSIVE SpaceHierarchy AS ( - SELECT space_id, space_parent_id - FROM spaces - WHERE space_id = $1 - - UNION - - SELECT s.space_id, s.space_parent_id - FROM spaces s - JOIN SpaceHierarchy h ON s.space_parent_id = h.space_id -) -SELECT space_id -FROM SpaceHierarchy h1;` + query := spaceDescendantsQuery + ` + SELECT space_descendant_id + FROM space_descendants` db := dbtx.GetAccessor(ctx, s.db) var spaceIDs []int64 - if err := db.SelectContext(ctx, &spaceIDs, where, parentID); err != nil { + if err := db.SelectContext(ctx, &spaceIDs, query, parentID); err != nil { return nil, database.ProcessSQLErrorf(ctx, err, "failed to retrieve spaces") } diff --git a/app/store/database/space.go b/app/store/database/space.go index 2c1c179d5..796ac156f 100644 --- a/app/store/database/space.go +++ b/app/store/database/space.go @@ -192,26 +192,40 @@ func (s *SpaceStore) findByPathAndDeletedAt( return s.find(ctx, spaceID, &deletedAt) } -const spaceRecursiveQuery = ` -WITH RECURSIVE SpaceHierarchy(space_hierarchy_id, space_hierarchy_parent_id) AS ( - SELECT space_id, space_parent_id +const spaceAncestorsQuery = ` +WITH RECURSIVE space_ancestors(space_ancestor_id, space_ancestor_uid, space_ancestor_parent_id) AS ( + SELECT space_id, space_uid, space_parent_id FROM spaces WHERE space_id = $1 UNION - SELECT s.space_id, s.space_parent_id - FROM spaces s - JOIN SpaceHierarchy h ON s.space_id = h.space_hierarchy_parent_id + SELECT space_id, space_uid, space_parent_id + FROM spaces + JOIN space_ancestors ON space_id = space_ancestor_parent_id +) +` + +const spaceDescendantsQuery = ` +WITH RECURSIVE space_descendants(space_descendant_id, space_descendant_uid, space_descendant_parent_id) AS ( + SELECT space_id, space_uid, space_parent_id + FROM spaces + WHERE space_id = $1 + + UNION + + SELECT space_id, space_uid, space_parent_id + FROM spaces + JOIN space_descendants ON space_descendant_id = space_parent_id ) ` // GetRootSpace returns a space where space_parent_id is NULL. func (s *SpaceStore) GetRootSpace(ctx context.Context, spaceID int64) (*types.Space, error) { - query := spaceRecursiveQuery + ` - SELECT space_hierarchy_id - FROM SpaceHierarchy - WHERE space_hierarchy_parent_id IS NULL;` + query := spaceAncestorsQuery + ` + SELECT space_ancestor_id + FROM space_ancestors + WHERE space_ancestor_parent_id IS NULL` db := dbtx.GetAccessor(ctx, s.db) @@ -225,37 +239,93 @@ func (s *SpaceStore) GetRootSpace(ctx context.Context, spaceID int64) (*types.Sp // GetAncestorIDs returns a list of all space IDs along the recursive path to the root space. func (s *SpaceStore) GetAncestorIDs(ctx context.Context, spaceID int64) ([]int64, error) { - query := spaceRecursiveQuery + ` - SELECT space_hierarchy_id FROM SpaceHierarchy` + query := spaceAncestorsQuery + ` + SELECT space_ancestor_id FROM space_ancestors` db := dbtx.GetAccessor(ctx, s.db) var spaceIDs []int64 if err := db.SelectContext(ctx, &spaceIDs, query, spaceID); err != nil { - return nil, database.ProcessSQLErrorf(ctx, err, "failed to get space hierarchy") + return nil, database.ProcessSQLErrorf(ctx, err, "failed to get space ancestors IDs") } return spaceIDs, nil } -func (s *SpaceStore) GetHierarchy( +func (s *SpaceStore) GetAncestors( ctx context.Context, spaceID int64, ) ([]*types.Space, error) { - query := spaceRecursiveQuery + ` + query := spaceAncestorsQuery + ` SELECT ` + spaceColumns + ` - FROM spaces INNER JOIN SpaceHierarchy ON space_id = space_hierarchy_id` + FROM spaces INNER JOIN space_ancestors ON space_id = space_ancestor_id` db := dbtx.GetAccessor(ctx, s.db) var dst []*space if err := db.SelectContext(ctx, &dst, query, spaceID); err != nil { - return nil, database.ProcessSQLErrorf(ctx, err, "Failed executing custom list query") + return nil, database.ProcessSQLErrorf(ctx, err, "Failed executing get space ancestors query") } return s.mapToSpaces(ctx, s.db, dst) } +// GetAncestorsData returns a list of space parent data for spaces that are ancestors of the space. +func (s *SpaceStore) GetAncestorsData(ctx context.Context, spaceID int64) ([]types.SpaceParentData, error) { + query := spaceAncestorsQuery + ` + SELECT space_ancestor_id, space_ancestor_uid, space_ancestor_parent_id FROM space_ancestors` + + return s.readParentsData(ctx, query, spaceID) +} + +// GetDescendantsData returns a list of space parent data for spaces that are descendants of the space. +func (s *SpaceStore) GetDescendantsData(ctx context.Context, spaceID int64) ([]types.SpaceParentData, error) { + query := spaceDescendantsQuery + ` + SELECT space_descendant_id, space_descendant_uid, space_descendant_parent_id FROM space_descendants` + + return s.readParentsData(ctx, query, spaceID) +} + +func (s *SpaceStore) readParentsData( + ctx context.Context, + query string, + spaceID int64, +) ([]types.SpaceParentData, error) { + db := dbtx.GetAccessor(ctx, s.db) + + rows, err := db.QueryContext(ctx, query, spaceID) + if err != nil { + return nil, database.ProcessSQLErrorf(ctx, err, "failed to run space parent data query") + } + + defer func() { _ = rows.Close() }() + + var result []types.SpaceParentData + + for rows.Next() { + var id int64 + var uid string + var parent null.Int + + err = rows.Scan(&id, &uid, &parent) + if err != nil { + return nil, database.ProcessSQLErrorf(ctx, err, "failed to scan space parent data") + } + + result = append(result, types.SpaceParentData{ + ID: id, + Identifier: uid, + ParentID: parent.Int64, + }) + } + + if err := rows.Err(); err != nil { + return nil, database.ProcessSQLErrorf(ctx, err, "failed to read space parent data") + } + + return result, nil +} + // Create a new space. func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error { if space == nil { diff --git a/types/repo.go b/types/repo.go index 5e9d2491b..ee94ff9fe 100644 --- a/types/repo.go +++ b/types/repo.go @@ -100,6 +100,8 @@ type RepositoryGitInfo struct { GitUID string } +func (rgi *RepositoryGitInfo) GetGitUID() string { return rgi.GitUID } + type RepositoryPullReqSummary struct { OpenCount int `json:"open_count"` ClosedCount int `json:"closed_count"` diff --git a/types/space.go b/types/space.go index 31538c1f4..f2f3d7dbd 100644 --- a/types/space.go +++ b/types/space.go @@ -43,6 +43,12 @@ type Space struct { Deleted *int64 `json:"deleted,omitempty"` } +type SpaceParentData struct { + ID int64 `json:"id"` + Identifier string `json:"identifier"` + ParentID int64 `json:"parent_id"` +} + // Stores spaces query parameters. type SpaceFilter struct { Page int `json:"page"`