diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/db/bundb/timeline.go | 51 | ||||
| -rw-r--r-- | internal/db/bundb/timeline_test.go | 33 | 
2 files changed, 68 insertions, 16 deletions
diff --git a/internal/db/bundb/timeline.go b/internal/db/bundb/timeline.go index 1230a84d4..f63937bc1 100644 --- a/internal/db/bundb/timeline.go +++ b/internal/db/bundb/timeline.go @@ -19,11 +19,13 @@ package bundb  import (  	"context" +	"errors"  	"fmt"  	"time"  	"github.com/superseriousbusiness/gotosocial/internal/db"  	"github.com/superseriousbusiness/gotosocial/internal/gtscontext" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/internal/id"  	"github.com/superseriousbusiness/gotosocial/internal/log" @@ -101,22 +103,39 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI  		q = q.Order("status.id ASC")  	} -	// Subquery to select target (followed) account -	// IDs from follows owned by given accountID. -	subQ := t.db. -		NewSelect(). -		TableExpr("? AS ?", bun.Ident("follows"), bun.Ident("follow")). -		Column("follow.target_account_id"). -		Where("? = ?", bun.Ident("follow.account_id"), accountID) - -	// Use the subquery in a WhereGroup here to specify that we want EITHER -	// - statuses posted by accountID itself OR -	// - statuses posted by accounts that accountID follows -	q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { -		return q. -			Where("? = ?", bun.Ident("status.account_id"), accountID). -			WhereOr("? IN (?)", bun.Ident("status.account_id"), subQ) -	}) +	// As this is the home timeline, it should be +	// populated by statuses from accounts followed +	// by accountID, and posts from accountID itself. +	// +	// So, begin by seeing who accountID follows. +	// It should be a little cheaper to do this in +	// a separate query like this, rather than using +	// a join, since followIDs are cached in memory. +	follows, err := t.state.DB.GetAccountFollows( +		gtscontext.SetBarebones(ctx), +		accountID, +	) +	if err != nil && !errors.Is(err, db.ErrNoEntries) { +		return nil, gtserror.Newf("db error getting follows for account %s: %w", accountID, err) +	} + +	// Extract just the accountID from each follow. +	targetAccountIDs := make([]string, len(follows)+1) +	for i, f := range follows { +		targetAccountIDs[i] = f.TargetAccountID +	} + +	// Add accountID itself as a pseudo follow so that +	// accountID can see its own posts in the timeline. +	targetAccountIDs[len(targetAccountIDs)-1] = accountID + +	// Select only statuses authored by +	// accounts with IDs in the slice. +	q = q.Where( +		"? IN (?)", +		bun.Ident("status.account_id"), +		bun.In(targetAccountIDs), +	)  	if err := q.Scan(ctx, &statusIDs); err != nil {  		return nil, err diff --git a/internal/db/bundb/timeline_test.go b/internal/db/bundb/timeline_test.go index 2bc9c9d0d..e5a78dfd1 100644 --- a/internal/db/bundb/timeline_test.go +++ b/internal/db/bundb/timeline_test.go @@ -24,6 +24,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/ap" +	"github.com/superseriousbusiness/gotosocial/internal/gtscontext"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/superseriousbusiness/gotosocial/internal/id"  	"github.com/superseriousbusiness/gotosocial/internal/util" @@ -156,6 +157,38 @@ func (suite *TimelineTestSuite) TestGetHomeTimeline() {  	suite.checkStatuses(s, id.Highest, id.Lowest, 16)  } +func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() { +	var ( +		ctx            = context.Background() +		viewingAccount = suite.testAccounts["local_account_1"] +	) + +	// Remove all of viewingAccount's follows. +	follows, err := suite.state.DB.GetAccountFollows( +		gtscontext.SetBarebones(ctx), +		viewingAccount.ID, +	) + +	if err != nil { +		suite.FailNow(err.Error()) +	} + +	for _, f := range follows { +		if err := suite.state.DB.DeleteFollowByID(ctx, f.ID); err != nil { +			suite.FailNow(err.Error()) +		} +	} + +	// Query should work fine; though far +	// fewer statuses will be returned ofc. +	s, err := suite.db.GetHomeTimeline(ctx, viewingAccount.ID, "", "", "", 20, false) +	if err != nil { +		suite.FailNow(err.Error()) +	} + +	suite.checkStatuses(s, id.Highest, id.Lowest, 5) +} +  func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {  	var (  		ctx            = context.Background()  | 
