Trying out paginated cursortoken variant [UNTESTED]
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m11s
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m11s
This commit is contained in:
@@ -120,25 +120,25 @@ func (c *Coll[TData]) createToken(fieldPrimary string, dirPrimary ct.SortDirecti
|
||||
|
||||
valuePrimary, err := c.getFieldValueAsTokenString(lastEntity, fieldPrimary)
|
||||
if err != nil {
|
||||
return ct.CursorToken{}, exerr.Wrap(err, "failed to get (primary) field-value as token-string").Type("lastEntity", lastEntity).Str("fieldPrimary", fieldPrimary).Build()
|
||||
return nil, exerr.Wrap(err, "failed to get (primary) field-value as token-string").Type("lastEntity", lastEntity).Str("fieldPrimary", fieldPrimary).Build()
|
||||
}
|
||||
|
||||
valueSeconary := ""
|
||||
if fieldSecondary != nil && dirSecondary != nil {
|
||||
valueSeconary, err = c.getFieldValueAsTokenString(lastEntity, *fieldSecondary)
|
||||
if err != nil {
|
||||
return ct.CursorToken{}, exerr.Wrap(err, "failed to get (secondary) field-value as token-string").Type("lastEntity", lastEntity).StrPtr("fieldSecondary", fieldSecondary).Build()
|
||||
return nil, exerr.Wrap(err, "failed to get (secondary) field-value as token-string").Type("lastEntity", lastEntity).StrPtr("fieldSecondary", fieldSecondary).Build()
|
||||
}
|
||||
}
|
||||
|
||||
return ct.CursorToken{
|
||||
Mode: ct.CTMNormal,
|
||||
ValuePrimary: valuePrimary,
|
||||
ValueSecondary: valueSeconary,
|
||||
Direction: dirPrimary,
|
||||
PageSize: langext.Coalesce(pageSize, 0),
|
||||
Extra: ct.Extra{},
|
||||
}, nil
|
||||
return ct.NewKeySortToken(
|
||||
valuePrimary,
|
||||
valueSeconary,
|
||||
dirPrimary,
|
||||
dirPrimary,
|
||||
langext.Coalesce(pageSize, 0),
|
||||
ct.Extra{},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) needsDoubleSort(ctx context.Context) bool {
|
||||
|
122
wmo/queryList.go
122
wmo/queryList.go
@@ -10,6 +10,24 @@ import (
|
||||
)
|
||||
|
||||
func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int, inTok ct.CursorToken) ([]TData, ct.CursorToken, error) {
|
||||
if ctks, ok := inTok.(ct.CTKeySort); ok {
|
||||
d, tok, err := c.listWithKSToken(ctx, filter, pageSize, ctks)
|
||||
if err != nil {
|
||||
return nil, ct.End(), err
|
||||
}
|
||||
return d, tok, nil
|
||||
} else if ctks, ok := inTok.(ct.CTPaginated); ok {
|
||||
d, tok, err := c.listWithPaginatedToken(ctx, filter, pageSize, ctks)
|
||||
if err != nil {
|
||||
return nil, ct.End(), err
|
||||
}
|
||||
return d, tok, nil
|
||||
} else {
|
||||
return nil, ct.End(), exerr.New(exerr.TypeCursorTokenDecode, "unknown ct type").Any("token", inTok).Type("tokenType", inTok).Build()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) listWithKSToken(ctx context.Context, filter ct.Filter, pageSize *int, inTok ct.CTKeySort) ([]TData, ct.CursorToken, error) {
|
||||
if inTok.Mode == ct.CTMEnd {
|
||||
return make([]TData, 0), ct.End(), nil
|
||||
}
|
||||
@@ -41,7 +59,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
|
||||
paginationPipeline, doubleSortPipeline, err := createPaginationPipeline(c, inTok, sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, pageSize)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, exerr.
|
||||
return nil, nil, exerr.
|
||||
Wrap(err, "failed to create pagination").
|
||||
WithType(exerr.TypeCursorTokenDecode).
|
||||
Str("collection", c.Name()).
|
||||
@@ -66,7 +84,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
|
||||
cursor, err := c.coll.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipeline).Str("collection", c.Name()).Build()
|
||||
return nil, nil, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipeline).Str("collection", c.Name()).Build()
|
||||
}
|
||||
|
||||
defer func() { _ = cursor.Close(ctx) }()
|
||||
@@ -75,7 +93,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
if pageSize == nil {
|
||||
entries, err := c.decodeAll(ctx, cursor)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, exerr.Wrap(err, "failed to all-decode entities").Build()
|
||||
return nil, nil, exerr.Wrap(err, "failed to all-decode entities").Build()
|
||||
}
|
||||
return entries, ct.End(), nil
|
||||
}
|
||||
@@ -85,7 +103,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
var entry TData
|
||||
entry, err = c.decodeSingle(ctx, cursor)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, exerr.Wrap(err, "failed to decode entity").Build()
|
||||
return nil, nil, exerr.Wrap(err, "failed to decode entity").Build()
|
||||
}
|
||||
entities = append(entities, entry)
|
||||
}
|
||||
@@ -100,12 +118,70 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
|
||||
nextToken, err := c.createToken(sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, last, pageSize)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, exerr.Wrap(err, "failed to create (out)-token").Build()
|
||||
return nil, nil, exerr.Wrap(err, "failed to create (out)-token").Build()
|
||||
}
|
||||
|
||||
return entities, nextToken, nil
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) listWithPaginatedToken(ctx context.Context, filter ct.Filter, pageSize *int, inTok ct.CTPaginated) ([]TData, ct.CursorToken, error) {
|
||||
var err error
|
||||
|
||||
page := inTok.Page
|
||||
|
||||
if page < 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
pipelineSort := mongo.Pipeline{}
|
||||
pipelineFilter := mongo.Pipeline{}
|
||||
|
||||
if filter != nil {
|
||||
pipelineFilter = filter.FilterQuery(ctx)
|
||||
pf1, pd1, pf2, pd2 := filter.Pagination(ctx)
|
||||
|
||||
pipelineSort, err = createSortOnlyPipeline(pf1, pd1, &pf2, &pd2)
|
||||
if err != nil {
|
||||
return nil, nil, exerr.Wrap(err, "failed to create sort pipeline").Build()
|
||||
}
|
||||
}
|
||||
|
||||
pipelinePaginate := mongo.Pipeline{}
|
||||
if pageSize != nil {
|
||||
pipelinePaginate = append(pipelinePaginate, bson.D{{Key: "$skip", Value: *pageSize * (page - 1)}})
|
||||
pipelinePaginate = append(pipelinePaginate, bson.D{{Key: "$limit", Value: *pageSize}})
|
||||
} else {
|
||||
page = 1
|
||||
}
|
||||
|
||||
pipelineCount := mongo.Pipeline{}
|
||||
pipelineCount = append(pipelineCount, bson.D{{Key: "$count", Value: "count"}})
|
||||
|
||||
extrModPipelineResolved := mongo.Pipeline{}
|
||||
for _, ppl := range c.extraModPipeline {
|
||||
extrModPipelineResolved = langext.ArrConcat(extrModPipelineResolved, ppl(ctx))
|
||||
}
|
||||
|
||||
pipelineList := langext.ArrConcat(pipelineFilter, pipelineSort, pipelinePaginate, extrModPipelineResolved, pipelineSort)
|
||||
|
||||
cursorList, err := c.coll.Aggregate(ctx, pipelineList)
|
||||
if err != nil {
|
||||
return nil, nil, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipelineList).Str("collection", c.Name()).Build()
|
||||
}
|
||||
|
||||
entities, err := c.decodeAll(ctx, cursorList)
|
||||
if err != nil {
|
||||
return nil, nil, exerr.Wrap(err, "failed to all-decode entities").Build()
|
||||
}
|
||||
|
||||
tokOut := ct.Page(page + 1)
|
||||
if pageSize == nil || len(entities) < *pageSize {
|
||||
tokOut = ct.PageEnd()
|
||||
}
|
||||
|
||||
return entities, tokOut, nil
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) Count(ctx context.Context, filter ct.RawFilter) (int64, error) {
|
||||
type countRes struct {
|
||||
Count int64 `bson:"c"`
|
||||
@@ -138,12 +214,12 @@ func (c *Coll[TData]) ListWithCount(ctx context.Context, filter ct.Filter, pageS
|
||||
// NOTE: Possible optimization: Cache count in CursorToken, then fetch count only on first page.
|
||||
count, err := c.Count(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
data, token, err := c.List(ctx, filter, pageSize, inTok)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
return data, token, count, nil
|
||||
}
|
||||
@@ -184,7 +260,7 @@ func (c *Coll[TData]) ListAllIDs(ctx context.Context, filter ct.RawFilter) ([]st
|
||||
return langext.ArrMap(res, func(v idObject) string { return v.ID }), nil
|
||||
}
|
||||
|
||||
func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken, fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection, pageSize *int) ([]bson.D, []bson.D, error) {
|
||||
func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort, fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection, pageSize *int) ([]bson.D, []bson.D, error) {
|
||||
|
||||
cond := bson.A{}
|
||||
sort := bson.D{}
|
||||
@@ -265,3 +341,33 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken
|
||||
|
||||
return pipeline, pipelineSort, nil
|
||||
}
|
||||
|
||||
func createSortOnlyPipeline(fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection) ([]bson.D, error) {
|
||||
|
||||
sort := bson.D{}
|
||||
|
||||
if sortPrimary == ct.SortASC {
|
||||
// We sort ASC on <field> - so we want all entries newer ($gt) than the $primary
|
||||
sort = append(sort, bson.E{Key: fieldPrimary, Value: +1})
|
||||
} else if sortPrimary == ct.SortDESC {
|
||||
// We sort DESC on <field> - so we want all entries older ($lt) than the $primary
|
||||
sort = append(sort, bson.E{Key: fieldPrimary, Value: -1})
|
||||
}
|
||||
|
||||
if fieldSecondary != nil && sortSecondary != nil && *fieldSecondary != fieldPrimary {
|
||||
|
||||
if *sortSecondary == ct.SortASC {
|
||||
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: +1})
|
||||
|
||||
} else if *sortSecondary == ct.SortDESC {
|
||||
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: -1})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pipelineSort := mongo.Pipeline{bson.D{{Key: "$sort", Value: sort}}}
|
||||
|
||||
return pipelineSort, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user