web-ridge / gqlgen-sqlboiler

This is a plugin for gqlgen to generate converts + filter queries and resolvers for sqlboiler
MIT License
75 stars 13 forks source link

Support edges/connections [WIP] #6

Closed RichardLindhout closed 3 years ago

RichardLindhout commented 3 years ago

Nice write up from https://github.com/graphql/graphql-relay-js/issues/94

Thus when I get the request I process it in the following way:

Start from the greedy query: SELECT * FROM table If the after argument is provided, add id > parsed_cursor to the WHERE clause If the before argument is provided, add id < parsed_cursor to the WHERE clause If the first argument is provided, add ORDER BY id DESC LIMIT first+1 to the query If the last argument is provided, add ORDER BY id ASC LIMIT last+1 to the query If the last argument is provided, I reverse the order of the results If the first argument is provided then I set hasPreviousPage: false (see spec for a description of this behavior). If no less than first+1 results are returned, I set hasNextPage: true, otherwise I set it to false. If the last argument is provided then I set hasNextPage: false (see spec for a description of this behavior). If no less last+1 results are returned, I set hasPreviousPage: true, otherwise I set it to false.

RichardLindhout commented 3 years ago

This does not work with custom sorting I guess..

RichardLindhout commented 3 years ago

And string ids?

RichardLindhout commented 3 years ago

This looks nice: https://github.com/nrfta/go-paging but it removes the whole point of connections as it embeds the offset

RichardLindhout commented 3 years ago

https://engineering.dubsmash.com/bi-directional-pagination-using-graphql-relay-b523c919c96

RichardLindhout commented 3 years ago

I think best thing will probably be to try to have use the best option

If no sort is provided

If sort is provided

Based on forward or backward navigation we'll use the exact opposite '>' to '<' signs

RichardLindhout commented 3 years ago

https://relay.dev/graphql/connections.htm

You may order the edges however your business logic dictates, and may determine the ordering based upon additional arguments not covered by this specification. But the ordering must be consistent from page to page, and importantly, The ordering of edges should be the same when using first/after as when using last/before, all other arguments being equal. It should not be reversed when using last/before. More formally:

When before: cursor is used, the edge closest to cursor must come last in the result edges. When after: cursor is used, the edge closest to cursor must come first in the result edges.

RichardLindhout commented 3 years ago

Ok, I think i've got basic things working now with sorting too!

End result in resolvers will more or less look like this

func (r *queryResolver) Users(ctx context.Context, pagination fm.ConnectionPagination, ordering []*fm.UserOrdering, filter *fm.UserFilter) (*fm.UserConnection, error) {
    mods := GetUserPreloadMods(ctx)
    mods = append(mods, UserFilterToMods(filter)...)
    connection, err := GetUserConnection(ctx, r.db, mods, pagination, ordering)
    if err != nil {
        log.Error().Err(err).Msg(publicUserListError)
        return nil, errors.New(publicUserListError)
    }
    return connection, nil
}
RichardLindhout commented 3 years ago

Code can be seen here: https://github.com/web-ridge/gqlgen-sqlboiler-examples/blob/development/issue-6-edges-connections/resolver.go

It still needs a cleanup though, but basic way to work universally has been provided, although a little rough around the 'edges' ;)

RichardLindhout commented 3 years ago

We cannot make it work with non-unique sorting so if it's an unique column like id, email, createdAt it works but if it something else it won't and we need to fallback on offset based pagination

RichardLindhout commented 3 years ago

These columns get generated in sqlboiler

var mySQLUserUniqueColumns = []string{ "id", "email", }

RichardLindhout commented 3 years ago

Above statement is not true we can check order of sorting by WHERE ((first_name, last_name, id) < (?,?,?))

RichardLindhout commented 3 years ago

I have tested all cases and it works great, I still need to verify and automatically test the last case were we have ASC and DESC both provided and verify if forward and backward still works.

If this case does not work we will fallback to offset-based pagination for those cases. And I need to to test the postgres version too

RichardLindhout commented 3 years ago

I still need to look at nested connections this will be a little bit dirtier as it probably would make the convert code more difficult

RichardLindhout commented 3 years ago

Released in v3.0.0