v0.0.641 Handle cursortokens with non-decodable values gracefully
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m20s
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m20s
This commit is contained in:
+55
-25
@@ -401,14 +401,18 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort,
|
||||
return nil, nil, exerr.Wrap(err, "failed to get (primary) token-value as mongo-type").Build()
|
||||
}
|
||||
|
||||
if sortPrimary == ct.SortASC {
|
||||
// We sort ASC on <field> - so we want all entries newer ($gt) than the $primary
|
||||
cond = append(cond, bson.M{fieldPrimary: bson.M{"$gt": valuePrimary}})
|
||||
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
|
||||
cond = append(cond, bson.M{fieldPrimary: bson.M{"$lt": valuePrimary}})
|
||||
sort = append(sort, bson.E{Key: fieldPrimary, Value: -1})
|
||||
if isValidTokenValue(valuePrimary) {
|
||||
|
||||
if sortPrimary == ct.SortASC {
|
||||
// We sort ASC on <field> - so we want all entries newer ($gt) than the $primary
|
||||
cond = append(cond, bson.M{fieldPrimary: bson.M{"$gt": valuePrimary}})
|
||||
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
|
||||
cond = append(cond, bson.M{fieldPrimary: bson.M{"$lt": valuePrimary}})
|
||||
sort = append(sort, bson.E{Key: fieldPrimary, Value: -1})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if fieldSecondary != nil && sortSecondary != nil && *fieldSecondary != fieldPrimary {
|
||||
@@ -418,25 +422,29 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort,
|
||||
return nil, nil, exerr.Wrap(err, "failed to get (secondary) token-value as mongo-type").Build()
|
||||
}
|
||||
|
||||
if *sortSecondary == ct.SortASC {
|
||||
if isValidTokenValue(valueSecondary) {
|
||||
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
|
||||
}})
|
||||
if *sortSecondary == ct.SortASC {
|
||||
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: +1})
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
|
||||
}})
|
||||
|
||||
} else if *sortSecondary == ct.SortDESC {
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: +1})
|
||||
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
|
||||
}})
|
||||
} else if *sortSecondary == ct.SortDESC {
|
||||
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: -1})
|
||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
|
||||
cond = append(cond, bson.M{"$and": bson.A{
|
||||
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||
bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
|
||||
}})
|
||||
|
||||
sort = append(sort, bson.E{Key: *fieldSecondary, Value: -1})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -449,7 +457,9 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort,
|
||||
|
||||
} else if token.Mode == ct.CTMNormal {
|
||||
|
||||
pipeline = append(pipeline, bson.D{{Key: "$match", Value: bson.M{"$or": cond}}})
|
||||
if len(cond) > 0 {
|
||||
pipeline = append(pipeline, bson.D{{Key: "$match", Value: bson.M{"$or": cond}}})
|
||||
}
|
||||
|
||||
} else if token.Mode == ct.CTMEnd {
|
||||
|
||||
@@ -462,9 +472,15 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort,
|
||||
|
||||
}
|
||||
|
||||
pipeline = append(pipeline, bson.D{{Key: "$sort", Value: sort}})
|
||||
if len(sort) > 0 {
|
||||
pipeline = append(pipeline, bson.D{{Key: "$sort", Value: sort}})
|
||||
}
|
||||
|
||||
pipelineSort := mongo.Pipeline{bson.D{{Key: "$sort", Value: sort}}}
|
||||
pipelineSort := mongo.Pipeline{}
|
||||
|
||||
if len(sort) > 0 {
|
||||
pipelineSort = append(pipelineSort, bson.D{{Key: "$sort", Value: sort}})
|
||||
}
|
||||
|
||||
if pageSize != nil {
|
||||
pipeline = append(pipeline, bson.D{{Key: "$limit", Value: int64(*pageSize + 1)}})
|
||||
@@ -473,6 +489,20 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CTKeySort,
|
||||
return pipeline, pipelineSort, nil
|
||||
}
|
||||
|
||||
func isValidTokenValue(val any) bool {
|
||||
|
||||
// special handling for Mongo EntityIDs
|
||||
// We want to prevent failing late (in mongoDB layer) when client sends invalid IDs as token values
|
||||
|
||||
if mongoID, ok := val.(MongoEntityID); ok {
|
||||
if !mongoID.Valid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func createSortOnlyPipeline(fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection) ([]bson.D, error) {
|
||||
|
||||
sort := bson.D{}
|
||||
|
||||
Reference in New Issue
Block a user