From dc1bde2fe693e4f73ac06b100ab5a926eb72a1aa Mon Sep 17 00:00:00 2001 From: Alessio Date: Thu, 12 Oct 2023 15:34:24 -0300 Subject: [PATCH] Implement 'likes sort order' for Likes tab --- pkg/persistence/compound_queries_test.go | 33 +++++++++++++++++++++--- pkg/persistence/compound_ssf_queries.go | 26 +++++++++++++++---- pkg/persistence/schema.sql | 10 ++++--- sample_data/seed_data.sql | 5 ++++ 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/pkg/persistence/compound_queries_test.go b/pkg/persistence/compound_queries_test.go index 8207cbb..d1f6063 100644 --- a/pkg/persistence/compound_queries_test.go +++ b/pkg/persistence/compound_queries_test.go @@ -155,13 +155,40 @@ func TestUserLikesFeed(t *testing.T) { // Fetch @Peter_Nimitz user feed while logged in as @MysteryGrove c := persistence.NewUserFeedLikesCursor(UserHandle("MysteryGrove")) + require.Equal(c.SortOrder, persistence.SORT_ORDER_LIKED_AT) + c.PageSize = 2 feed, err := profile.NextPage(c, UserID(0)) require.NoError(err) - // Should have "liked" 1 tweet + require.Len(feed.Tweets, 2) + for i, expected_tweet_id := range []TweetID{1698765208393576891, 1426669666928414720} { + assert.Equal(feed.Items[i].TweetID, expected_tweet_id) + _, is_ok := feed.Tweets[expected_tweet_id] + assert.True(is_ok) + } + + require.Equal(feed.CursorBottom.CursorValue, 4) + feed, err = profile.NextPage(feed.CursorBottom, UserID(0)) + require.NoError(err) + + require.Len(feed.Tweets, 2) + for i, expected_tweet_id := range []TweetID{1343633011364016128, 1513313535480287235} { + assert.Equal(feed.Items[i].TweetID, expected_tweet_id) + _, is_ok := feed.Tweets[expected_tweet_id] + assert.True(is_ok) + } + + assert.Equal(feed.CursorBottom.CursorValue, 2) + feed, err = profile.NextPage(feed.CursorBottom, UserID(0)) + require.NoError(err) + require.Len(feed.Tweets, 1) - _, is_ok := feed.Tweets[1413646595493568516] - assert.True(is_ok) + for i, expected_tweet_id := range []TweetID{1413646595493568516} { + assert.Equal(feed.Items[i].TweetID, expected_tweet_id) + _, is_ok := feed.Tweets[expected_tweet_id] + assert.True(is_ok) + } + assert.Equal(feed.CursorBottom.CursorPosition, persistence.CURSOR_END) } func TestTweetDetailWithReplies(t *testing.T) { diff --git a/pkg/persistence/compound_ssf_queries.go b/pkg/persistence/compound_ssf_queries.go index 8fe79cc..596bc7e 100644 --- a/pkg/persistence/compound_ssf_queries.go +++ b/pkg/persistence/compound_ssf_queries.go @@ -16,6 +16,7 @@ const ( SORT_ORDER_OLDEST SORT_ORDER_MOST_LIKES SORT_ORDER_MOST_RETWEETS + SORT_ORDER_LIKED_AT ) func (o SortOrder) OrderByClause() string { @@ -28,6 +29,8 @@ func (o SortOrder) OrderByClause() string { return "order by num_likes desc" case SORT_ORDER_MOST_RETWEETS: return "order by num_retweets desc" + case SORT_ORDER_LIKED_AT: + return "order by likes_sort_order desc" default: panic(fmt.Sprintf("Invalid sort order: %d", o)) } @@ -42,6 +45,8 @@ func (o SortOrder) PaginationWhereClause() string { return "num_likes < ?" case SORT_ORDER_MOST_RETWEETS: return "num_retweets < ?" + case SORT_ORDER_LIKED_AT: + return "likes_sort_order < ?" default: panic(fmt.Sprintf("Invalid sort order: %d", o)) } @@ -56,6 +61,8 @@ func (o SortOrder) NextCursorValue(r CursorResult) int { return r.NumLikes case SORT_ORDER_MOST_RETWEETS: return r.NumRetweets + case SORT_ORDER_LIKED_AT: + return r.LikeSortOrder default: panic(fmt.Sprintf("Invalid sort order: %d", o)) } @@ -94,8 +101,9 @@ const ( type CursorResult struct { scraper.Tweet scraper.Retweet - Chrono int `db:"chrono"` - ByUserID scraper.UserID `db:"by_user_id"` + Chrono int `db:"chrono"` + LikeSortOrder int `db:"likes_sort_order"` + ByUserID scraper.UserID `db:"by_user_id"` } type Cursor struct { @@ -181,7 +189,7 @@ func NewUserFeedLikesCursor(h scraper.UserHandle) Cursor { UntilTimestamp: scraper.TimestampFromUnix(0), CursorPosition: CURSOR_START, CursorValue: 0, - SortOrder: SORT_ORDER_NEWEST, + SortOrder: SORT_ORDER_LIKED_AT, PageSize: 50, LikedByUserHandle: h, @@ -255,6 +263,8 @@ func (c *Cursor) apply_token(token string) error { c.ToUserHandles = append(c.ToUserHandles, scraper.UserHandle(parts[1])) case "retweeted_by": c.RetweetedByUserHandle = scraper.UserHandle(parts[1]) + case "liked_by": + c.LikedByUserHandle = scraper.UserHandle(parts[1]) case "since": c.SinceTimestamp.Time, err = time.Parse("2006-01-02", parts[1]) case "until": @@ -396,10 +406,16 @@ func (p Profile) NextPage(c Cursor, current_user_id scraper.UserID) (Feed, error } liked_by_filter_join_clause := "" + likes_sort_order_field := "" if c.LikedByUserHandle != "" { liked_by_filter_join_clause = " join likes filter_likes on tweets.id = filter_likes.tweet_id " where_clauses = append(where_clauses, "filter_likes.user_id = (select id from users where handle like ?) ") bind_values = append(bind_values, c.LikedByUserHandle) + likes_sort_order_field = ", coalesce(filter_likes.sort_order, -1) likes_sort_order " + + // Don't include retweets on "liked by" searches because it doesn't distinguish which retweet + // version was the "liked" one + where_clauses = append(where_clauses, "retweet_id = 0") } // Pagination @@ -414,7 +430,7 @@ func (p Profile) NextPage(c Cursor, current_user_id scraper.UserID) (Feed, error } q := `select * from ( - select ` + TWEETS_ALL_SQL_FIELDS + `, + select ` + TWEETS_ALL_SQL_FIELDS + likes_sort_order_field + `, 0 tweet_id, 0 retweet_id, 0 retweeted_by, 0 retweeted_at, posted_at chrono, tweets.user_id by_user_id from tweets @@ -427,7 +443,7 @@ func (p Profile) NextPage(c Cursor, current_user_id scraper.UserID) (Feed, error union select * from ( - select ` + TWEETS_ALL_SQL_FIELDS + `, + select ` + TWEETS_ALL_SQL_FIELDS + likes_sort_order_field + `, retweets.tweet_id, retweet_id, retweeted_by, retweeted_at, retweeted_at chrono, retweeted_by by_user_id from retweets diff --git a/pkg/persistence/schema.sql b/pkg/persistence/schema.sql index 0cb9820..b3b7f54 100644 --- a/pkg/persistence/schema.sql +++ b/pkg/persistence/schema.sql @@ -190,10 +190,6 @@ create table hashtags (rowid integer primary key, foreign key(tweet_id) references tweets(id) ); -create table database_version(rowid integer primary key, - version_number integer not null unique -); - create table likes(rowid integer primary key, sort_order integer unique not null, user_id integer not null, @@ -202,6 +198,12 @@ create table likes(rowid integer primary key, foreign key(user_id) references users(id) foreign key(tweet_id) references tweets(id) ); +create index if not exists index_likes_user_id on likes (user_id); +create index if not exists index_likes_tweet_id on likes (tweet_id); create table fake_user_sequence(latest_fake_id integer not null); insert into fake_user_sequence values(0x4000000000000000); + +create table database_version(rowid integer primary key, + version_number integer not null unique +); diff --git a/sample_data/seed_data.sql b/sample_data/seed_data.sql index 18b44c0..7a8cdfe 100644 --- a/sample_data/seed_data.sql +++ b/sample_data/seed_data.sql @@ -307,6 +307,11 @@ create table likes(rowid integer primary key, foreign key(tweet_id) references tweets(id) ); insert into likes values(1, 1, 1178839081222115328, 1413646595493568516); +insert into likes values(2, 2, 1178839081222115328, 1513313535480287235); +insert into likes values(3, 3, 1178839081222115328, 1343633011364016128); +insert into likes values(4, 4, 1178839081222115328, 1426669666928414720); +insert into likes values(5, 5, 1178839081222115328, 1698765208393576891); + create table fake_user_sequence(latest_fake_id integer not null); insert into fake_user_sequence values(0x4000000000000000);