Skip to content

Commit

Permalink
Decode functionality to pass struct for decoding data added
Browse files Browse the repository at this point in the history
  • Loading branch information
gobeam committed Mar 3, 2021
1 parent 2c11eee commit 8a934a3
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 52 deletions.
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

For all your simple query to aggregation pipeline this is simple and easy to use Pagination driver with information like Total, Page, PerPage, Prev, Next, TotalPage and your actual mongo result.


:speaker: :speaker:
***For normal queries new feature have been added to directly pass struct and decode data without manual marshalling later. Only normal queries support this feature for now. Sort chaining is also added as new feature***
## Install

``` bash
Expand Down Expand Up @@ -64,7 +65,7 @@ func main() {
// you can easily chain function and pass multiple query like here we are passing match
// query and projection query as params in Aggregate function you cannot use filter with Aggregate
// because you can pass filters directly through Aggregate param
aggPaginatedData, err := New(collection).Limit(limit).Page(page).Sort("price", -1).Aggregate(match, projectQuery)
aggPaginatedData, err := New(collection).Context(ctx).Limit(limit).Page(page).Sort("price", -1).Aggregate(match, projectQuery)
if err != nil {
panic(err)
}
Expand All @@ -88,7 +89,6 @@ func main() {
```

## For Normal queries

``` go


Expand All @@ -111,31 +111,27 @@ func main() {
}
// Querying paginated data
// Sort and select are optional
paginatedData, err := New(collection).Limit(limit).Page(page).Sort("price", -1).Select(projection).Filter(filter).Find()
// Multiple Sort chaining is also allowed
var products []Product
paginatedData, err := New(collection).Context(ctx).Limit(limit).Page(page).Sort("price", -1).Select(projection).Filter(filter).Decode(&products).Find()
if err != nil {
panic(err)
}

// paginated data is in paginatedData.Data
// paginated data or paginatedData.Data will be nil because data is already decoded on through Decode function
// pagination info can be accessed in paginatedData.Pagination
// if you want to marshal data to your defined struct

var lists []Product
for _, raw := range paginatedData.Data {
var product *Product
if marshallErr := bson.Unmarshal(raw, &product); marshallErr == nil {
lists = append(lists, *product)
}

}
// print ProductList
fmt.Printf("Norm Find Data: %+v\n", lists)
fmt.Printf("Normal Find Data: %+v\n", products)

// print pagination data
fmt.Printf("Normal find pagination info: %+v\n", paginatedData.Pagination)
}

```
***Notice:***
```go
paginatedData.data //it will be nil incase of normal queries because data is already decoded on through Decode function
```

## Running the tests

Expand Down
20 changes: 6 additions & 14 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,17 @@ func main() {
{"qty", 1},
}
// Querying paginated data
paginatedData, err := paginate.New(collection).Limit(limit).Page(page).Sort("price", -1).Sort("qty", -1).Select(projection).Filter(filter).Find()
var products []Product
paginatedData, err := paginate.New(collection).Context(ctx).Limit(limit).Page(page).Sort("price", -1).Sort("qty", -1).Select(projection).Filter(filter).Decode(&products).Find()
if err != nil {
panic(err)
}

// paginated data is in paginatedData.Data
// pagination info can be accessed in paginatedData.Pagination
// if you want to marshal data to your defined struct

var lists []Product
for _, raw := range paginatedData.Data {
var product *Product
if marshallErr := bson.Unmarshal(raw, &product); marshallErr == nil {
lists = append(lists, *product)
}

}
//// if you want to marshal data to your defined struct
// print ProductList
fmt.Printf("Norm Find Data: %+v\n", lists)
fmt.Printf("Norm Find Data: %+v\n", products)

// print pagination data
fmt.Printf("Normal find pagination info: %+v\n", paginatedData.Pagination)
Expand All @@ -71,7 +63,7 @@ func main() {
// you can easily chain function and pass multiple query like here we are passing match
// query and projection query as params in Aggregate function you cannot use filter with Aggregate
// because you can pass filters directly through Aggregate param
aggPaginatedData, err := paginate.New(collection).Limit(limit).Page(page).Sort("price", -1).Aggregate(match, projectQuery)
aggPaginatedData, err := paginate.New(collection).Context(ctx).Limit(limit).Page(page).Sort("price", -1).Aggregate(match, projectQuery)
if err != nil {
panic(err)
}
Expand All @@ -86,7 +78,7 @@ func main() {
}

// print ProductList
//fmt.Printf("Aggregate Product List: %+v\n", aggProductList)
fmt.Printf("Aggregate Product List: %+v\n", aggProductList)

// print pagination data
fmt.Printf("Aggregate Pagination Data: %+v\n", aggPaginatedData.Pagination)
Expand Down
4 changes: 2 additions & 2 deletions pagination.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package mongopagination

import (
"context"
"math"
)

Expand Down Expand Up @@ -53,8 +52,9 @@ func Paging(p *pagingQuery, paginationInfo chan<- *Paginator, aggregate bool, ag
var paginator Paginator
var offset int64
var count int64
ctx := p.getContext()
if !aggregate {
count, _ = p.Collection.CountDocuments(context.Background(), p.FilterQuery)
count, _ = p.Collection.CountDocuments(ctx, p.FilterQuery)
} else {
count = aggCount
}
Expand Down
63 changes: 45 additions & 18 deletions pagingQuery.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
// Error constants
const (
PageLimitError = "page or limit cannot be less than 0"
DecodeEmptyError = "struct should be provide to decode data"
DecodeNotAvail = "this feature is not available for aggregate query"
FilterInAggregateError = "you cannot use filter in aggregate query but you can pass multiple filter as param in aggregate function"
NilFilterError = "filter query cannot be nil"
)
Expand All @@ -22,6 +24,8 @@ const (
type pagingQuery struct {
Collection *mongo.Collection
SortFields bson.D
Ctx context.Context
Decoder interface{}
Project interface{}
FilterQuery interface{}
LimitCount int64
Expand Down Expand Up @@ -51,6 +55,8 @@ type PagingQuery interface {
Limit(limit int64) PagingQuery
Page(page int64) PagingQuery
Sort(sortField string, sortValue int) PagingQuery
Decode(decode interface{}) PagingQuery
Context(ctx context.Context) PagingQuery
}

// New is to construct PagingQuery object with mongo.Database and collection name
Expand All @@ -60,6 +66,17 @@ func New(collection *mongo.Collection) PagingQuery {
}
}

// Decode is function to decode result data
func (paging *pagingQuery) Decode(decode interface{}) PagingQuery {
paging.Decoder = decode
return paging
}

func (paging *pagingQuery) Context(ctx context.Context) PagingQuery {
paging.Ctx = ctx
return paging
}

// Select helps you to add projection on query
func (paging *pagingQuery) Select(selector interface{}) PagingQuery {
paging.Project = selector
Expand Down Expand Up @@ -102,19 +119,33 @@ func (paging *pagingQuery) Sort(sortField string, sortValue int) PagingQuery {
}

// validateQuery query is to check if user has added certain required params or not
func (paging *pagingQuery) validateQuery() error {
func (paging *pagingQuery) validateQuery(isNormal bool) error {
if paging.LimitCount <= 0 || paging.PageCount <= 0 {
return errors.New(PageLimitError)
}
if isNormal && paging.Decoder == nil {
return errors.New(DecodeEmptyError)
}
if !isNormal && paging.Decoder != nil {
return errors.New(DecodeNotAvail)
}
return nil
}

func (paging *pagingQuery) getContext() context.Context {
if paging.Ctx != nil {
return paging.Ctx
} else {
return context.Background()
}
}

// Aggregate help you to paginate mongo pipeline query
// it returns PaginatedData struct and error if any error
// occurs during document query
func (paging *pagingQuery) Aggregate(filters ...interface{}) (paginatedData *PaginatedData, err error) {
// checking if user added required params
if err := paging.validateQuery(); err != nil {
if err := paging.validateQuery(false); err != nil {
return nil, err
}
if paging.FilterQuery != nil {
Expand Down Expand Up @@ -149,14 +180,14 @@ func (paging *pagingQuery) Aggregate(filters ...interface{}) (paginatedData *Pag
opt := &options.AggregateOptions{
AllowDiskUse: &diskUse,
}

cursor, err := paging.Collection.Aggregate(context.Background(), aggregationFilter, opt)
ctx := paging.getContext()
cursor, err := paging.Collection.Aggregate(ctx, aggregationFilter, opt)
if err != nil {
return nil, err
}
defer cursor.Close(context.Background())
defer cursor.Close(ctx)
var docs []AutoGenerated
for cursor.Next(context.Background()) {
for cursor.Next(ctx) {
var document *AutoGenerated
if err := cursor.Decode(&document); err == nil {
docs = append(docs, *document)
Expand All @@ -183,14 +214,12 @@ func (paging *pagingQuery) Aggregate(filters ...interface{}) (paginatedData *Pag
// Find returns two value pagination data with document queried from mongodb and
// error if any error occurs during document query
func (paging *pagingQuery) Find() (paginatedData *PaginatedData, err error) {

if err := paging.validateQuery(); err != nil {
if err := paging.validateQuery(true); err != nil {
return nil, err
}
if paging.FilterQuery == nil {
return nil, errors.New(NilFilterError)
}

// get Pagination Info
paginationInfoChan := make(chan *Paginator, 1)
Paging(paging, paginationInfoChan, false, 0)
Expand All @@ -207,22 +236,20 @@ func (paging *pagingQuery) Find() (paginatedData *PaginatedData, err error) {
if len(paging.SortFields) > 0 {
opt.SetSort(paging.SortFields)
}
cursor, err := paging.Collection.Find(context.Background(), paging.FilterQuery, opt)
ctx := paging.getContext()
cursor, err := paging.Collection.Find(ctx, paging.FilterQuery, opt)
if err != nil {
return nil, err
}
defer cursor.Close(context.Background())
var docs []bson.Raw
for cursor.Next(context.Background()) {
var document *bson.Raw
if err := cursor.Decode(&document); err == nil {
docs = append(docs, *document)
}
defer cursor.Close(ctx)
docs := paging.Decoder
err = cursor.All(ctx, docs)
if err != nil {
return nil, err
}
paginationInfo := <-paginationInfoChan
result := PaginatedData{
Pagination: *paginationInfo.PaginationData(),
Data: docs,
}
return &result, nil
}
Expand Down
5 changes: 3 additions & 2 deletions pagingQuery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ func TestPagingQuery_Find(t *testing.T) {
{"status", 1},
}
collection := db.Collection(DatabaseCollection)
paginatedData, err := New(collection).Limit(limit).Page(page).Sort("price", -1).Select(projection).Filter(filter).Find()
var todos []TodoTest
paginatedData, err := New(collection).Limit(limit).Page(page).Sort("price", -1).Select(projection).Filter(filter).Decode(&todos).Find()

if err != nil {
t.Errorf("Error while pagination. Error: %s", err.Error())
Expand All @@ -75,7 +76,7 @@ func TestPagingQuery_Find(t *testing.T) {
return
}

if len(paginatedData.Data) < 1 {
if len(todos) < 1 {
t.Errorf("Error fetching data")
}

Expand Down

0 comments on commit 8a934a3

Please sign in to comment.