atdatabases icon indicating copy to clipboard operation
atdatabases copied to clipboard

LimitOffset implementation

Open miunau opened this issue 3 years ago • 7 comments

To address #234

I've added a skeleton for a limitOffset next to limit that would pass in an offset value for pg-typed and mysql-typed.

This probably still needs some work but I just wanted to ask if you're open for such a feature.

I also added some tests that seem to run.

miunau avatar Jul 31 '22 17:07 miunau

There is no change log for this pull request yet.

Create a changelog

rollingversions[bot] avatar Jul 31 '22 17:07 rollingversions[bot]

P.S. it would save me time when merging if you also fill in the change log on Rolling Versions (see automated comment) for pg-typed and mysql-typed.

ForbesLindesay avatar Aug 02 '22 12:08 ForbesLindesay

OK I'm moving house right now but will take a look soon.

miunau avatar Aug 02 '22 20:08 miunau

@ForbesLindesay I gave it a shot but I'm not sure how the interface composing should turn out based on your example. I've ended up with tests not recognising offset or limit, or orderByAsc/orderByDesc breaking due to mismatched types. For example, returning Promise<OrderedSelectQueryWithOffset<TRecord>> from offset() will block .limit chaining. Could you give more guidance on how you were thinking about the implementation?

miunau avatar Aug 20 '22 22:08 miunau

i agree to match chaining it should be .offset honestly i'm surprised this doesn't exist already

tripflex avatar Jun 19 '23 18:06 tripflex

I'm very open to a feature like this. Offset is often the easiest way to handle pagination. I think rather than a combined limitOffset function that does both the offset and limit, I'd prefer that we add an .offset(count: number) method that you can then chain a .limit(count: number) onto:

export interface OrderedSelectQueryWithOffset<TRecord> extends SelectQuery<TRecord> {
  first(): Promise<TRecord | null>;
  limit(count: number): Promise<TRecord[]>;
}
export interface OrderedSelectQuery<TRecord> extends OrderedSelectQueryWithOffset<TRecord> {
  offset(count: number): Promise<OrderedSelectQueryWithOffset<TRecord>>;
}

Splitting out these two interfaces ensures you cannot call .offset(...) multiple times.

Usage would be:

import db, {users} from './database';

// Example for simple offset-based pagination
export async function offsetPaginatedEmails(offset?: number = 0) {
  const records = await users(db)
    .find()
    .orderByAsc(`email`)
    .offset(offset)
    .limit(10);
  return {
    records: records.map((record) => record.email),
  };
}

Seems pretty simple to implement, should I start a new PR or did you want to do it or what would you prefer?

tripflex avatar Jun 19 '23 18:06 tripflex

@ForbesLindesay I took my first stab at this, can you please take a look and let me know of any changes required or any input on handling this?

https://github.com/ForbesLindesay/atdatabases/pull/300

Thanks!!

tripflex avatar Jul 18 '23 20:07 tripflex