Search code examples
javaandroidrx-javasqlbrite

RxJava and Sqlbrite - what operator to use


I started using RxJava in combination with SqlBrite and I am having some issues with the zip operator.

Let's say I have 2 classes, User and Tweet.

public class User {
    public long id;
    public List<Tweet> users;
    ...
}

public class Tweet {
    public long id;
    public User poster;
    ...
}

And their respective SQLite tables:

user
---------
id INTEGER PRIMARY KEY

tweet
---------
id INTEGER PRIMARY KEY
poster_id INTEGER
FOREIGN KEY(poster_id) REFERENCES user(id)

and their respective SqlBrite DAO

UserDao:

public class UserDao {
    private final BriteDatabase briteDb;
    private final TweetDAO tweetDao;

    ...

    public Observable<List<User>> getUsersWithTheirTweets() {
        Observable<User> usersObs = briteDb.createQuery("user", "SELECT * FROM user")
                .map(new Func1<SqlBrite.Query, List<User>>() {
                    @Override
                    public List<User> call(SqlBrite.Query query) {
                        Cursor cursor = query.run();
                        List<User> result = new ArrayList<>(cursor.getCount());
                        while (cursor.moveToNext()) {
                            User user = UserTable.parseCursor(cursor);
                            result.add(user);
                        }
                        cursor.close();
                        return result;
                    }
                })
                // transform Observable<List<User>> into Observable<User>
                .flatMap(new Func1<List<User>, Observable<User>>() {
                    @Override
                    public Observable<User> call(List<User> users) {
                        return Observable.from(users);
                    }
                });

        // combine each user with his tweets
        return Observable.zip(usersObs, usersObs.flatMap(new Func1<User, Observable<List<Tweet>>>() {
            @Override
            public Observable<List<Tweet>> call(User user) {
                return tweetDao.getTweetsByUser(user);
            }
        }), new Func2<User, List<Tweet>, User>() {
            @Override
            public User call(User user, List<Tweet> tweets) {
                user.tweets = tweets;
                return user;
            }
        }).toList();
    }

}

TweetDAO:

public class TweetDAO {
    private final BriteDatabase briteDb;

    ...

    public Observable<List<Tweet>> getTweetsForUser(final User user) {
        briteDb.createQuery("tweet", "SELECT * FROM tweet WHERE poster_id = ?", Long.toString(user.id))
                .map(new Func1<SqlBrite.Query, List<User>>() {
                    @Override
                    public List<Tweet> call(SqlBrite.Query query) {
                        Cursor cursor = query.run();
                        List<Tweet> result = new ArrayList<>(cursor.getCount());
                        while (cursor.moveToNext()) {
                            Tweet tweet = TweetTable.parseCursor(cursor);
                            tweet.user = user;
                            result.add(tweet);
                        }
                        cursor.close();
                        return result;
                    }
                })
    }
}

As you can see in UserDao, I tried to use the a combination of zip and flatMap operators to populate the list of Tweet for each User.

First question: is there a better way to do that?

Second question: that zip operator seems to hang forever and never completes... I see that tweetDao.getTweetsByUser(user) is called but the Func of the zip is never called... Would anyone have an idea why?

Third question: is there a better way of transforming an Observable<List<T>> to an Observable<T> other than using a combination of flatMap and from ?

Damn, that RxJava is powerful but the learning curve is pretty steep...


Solution

  • Why not let the database handle the join?

    SELECT * FROM user INNER JOIN tweet ON user._id = tweet.poster_id
    

    You might have to rename some columns if the two tables have columns with the same name.

    More about joins here