[MM-62553]+[MM-62554] Property Architecture: cursor based pagination (#30119)

* refactor: Replace pagination with cursor-based pagination for custom profile attributes

* remove pagination loop on property value retrieval for CPA

* add migrations to optimize pagination on property fields and values

* adapt test to remove pagination check

* update migrations list

* postgres: drop index concurrently

* concurrent index manipulation must be done outside of a Tx

* fix: Correct SQL index drop syntax from "OM" to "ON" in migration files

* test: Add CountForGroup test cases for property field store

* refactor: Add CountForGroup method to PropertyFieldStore interface and implementations

* Fix style and i18n

* feat: Add optional deleted property field filtering to CountForGroup method

* refactor: Update CountForGroup to support optional deleted property fields

* test: Add comprehensive tests for CountForGroup with includeDeleted parameter

* adapt test + gen layers

* rename property service method and set the includeDelete to false

* refactor: Remove redundant constant and use CustomProfileAttributesFieldLimit directly

* fix tests

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Julien Tant
2025-02-13 08:23:50 -07:00
коммит произвёл GitHub
родитель 4615ca5f28
Коммит 632a60b332
25 изменённых файлов: 820 добавлений и 61 удалений

Просмотреть файл

@@ -78,9 +78,26 @@ func (s *SqlPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, e
return fields, nil
}
func (s *SqlPropertyFieldStore) CountForGroup(groupID string, includeDeleted bool) (int64, error) {
var count int64
builder := s.getQueryBuilder().
Select("COUNT(id)").
From("PropertyFields").
Where(sq.Eq{"GroupID": groupID})
if !includeDeleted {
builder = builder.Where(sq.Eq{"DeleteAt": 0})
}
if err := s.GetReplica().GetBuilder(&count, builder); err != nil {
return int64(0), errors.Wrap(err, "failed to count Sessions")
}
return count, nil
}
func (s *SqlPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
if opts.Page < 0 {
return nil, errors.New("page must be positive integer")
if err := opts.Cursor.IsValid(); err != nil {
return nil, fmt.Errorf("cursor is invalid: %w", err)
}
if opts.PerPage < 1 {
@@ -88,10 +105,19 @@ func (s *SqlPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSea
}
builder := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
OrderBy("CreateAt ASC, Id ASC").
Limit(uint64(opts.PerPage))
if !opts.Cursor.IsEmpty() {
builder = builder.Where(sq.Or{
sq.Gt{"CreateAt": opts.Cursor.CreateAt},
sq.And{
sq.Eq{"CreateAt": opts.Cursor.CreateAt},
sq.Gt{"Id": opts.Cursor.PropertyFieldID},
},
})
}
if !opts.IncludeDeleted {
builder = builder.Where(sq.Eq{"DeleteAt": 0})
}

Просмотреть файл

@@ -83,8 +83,8 @@ func (s *SqlPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, e
}
func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
if opts.Page < 0 {
return nil, errors.New("page must be positive integer")
if err := opts.Cursor.IsValid(); err != nil {
return nil, fmt.Errorf("cursor is invalid: %w", err)
}
if opts.PerPage < 1 {
@@ -92,10 +92,19 @@ func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSea
}
builder := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
OrderBy("CreateAt ASC, Id ASC").
Limit(uint64(opts.PerPage))
if !opts.Cursor.IsEmpty() {
builder = builder.Where(sq.Or{
sq.Gt{"CreateAt": opts.Cursor.CreateAt},
sq.And{
sq.Eq{"CreateAt": opts.Cursor.CreateAt},
sq.Gt{"Id": opts.Cursor.PropertyValueID},
},
})
}
if !opts.IncludeDeleted {
builder = builder.Where(sq.Eq{"DeleteAt": 0})
}