tonsser / diesel-keyset-pagination

[EXPERIMENTAL] Seamless keyset pagination for Diesel
0 stars 0 forks source link

Support a query complex enough to use in graph-ql #1

Open mastfissh opened 5 years ago

mastfissh commented 5 years ago

I think it's good to have a goal, so this should be able to handle a query like:


fn followers_or_followings(
    user: &models::User,
    follow_relationship: FollowRelationship,
    cursor: Option<i64>,
    page_size: i32,
    db: &db::Connection,
) -> QueryResult<Vec<models::User>> {
    use crate::db::schema::{follows, users};
    let mut query = follows::table.into_boxed();
    if let Some(cursor) = cursor {
        query = query.filter(
                sql(" (\"firstname\", \"lastname\") > (SELECT firstname, lastname from users where id = ")
                    .bind::<BigInt, _>(cursor)
                    .sql(") "),
            );
    }

    if let FollowRelationship::Following = follow_relationship {
        return query
            .inner_join(users::table.on(users::id.eq(follows::followee_id)))
            .filter(
                follows::followee_type
                    .eq("User")
                    .and(follows::follower_id.eq(user.id))
                    .and(follows::unfollowed_at.is_null()),
            )
            .order((users::firstname, users::lastname))
            .limit(page_size.into())
            .select(users::all_columns)
            .load::<models::User>(db);
    } else {
        return query
            .inner_join(users::table.on(users::id.eq(follows::follower_id)))
            .filter(
                follows::followee_type
                    .eq("User")
                    .and(follows::followee_id.eq(user.id))
                    .and(follows::unfollowed_at.is_null()),
            )
            .filter(users::anonymous.eq(false))
            .order((users::firstname, users::lastname))
            .limit(page_size.into())
            .select(users::all_columns)
            .load::<models::User>(db);
    }
}
davidpdrsn commented 5 years ago

I have the feeling we're already really close. This almost works

    #[test]
    fn test_advanced_query() {
        let db = connect_to_db();

        let user = UserFactory::default().insert(&db);

        let query = follows::table
            .inner_join(users::table.on(users::id.eq(follows::followee_id)))
            .filter(
                follows::followee_type
                    .eq("User")
                    .and(follows::follower_id.eq(user.id))
                    .and(follows::unfollowed_at.is_null()),
            )
            .select(users::all_columns);

        let query = KeysetPaginated {
            query,
            order: (users::firstname, users::lastname),
            // TODO: This should be a real cursor so an id from a previous query
            filter: users::id.eq(user.id),
            page_size: 20,
        };

        let users = query.load::<User>(&db).unwrap();

        assert!(users.is_empty());
    }

It fails saying that HasTable isn't implemented for a join query

212 |         let users = query.load::<User>(&db).unwrap();
    |                           ^^^^ the trait `diesel::associations::HasTable` is not implemented for `diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<test::schema::follows::table, test::schema::users::table, diesel::query_source::joins::Inner>, diesel::expression::operators::Eq<test::schema::users::columns::id, test::schema::follows::columns::followee_id>>`

I bet there is way around that.