···
10
+
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
11
+
"github.com/haileyok/peruse/internal/helpers"
12
+
"github.com/labstack/echo/v4"
15
+
type SeattleFeed struct {
18
+
cached []RankedFeedPost
19
+
cacheExpiresAt time.Time
23
+
func NewSeattleFeed(s *Server) *SeattleFeed {
24
+
logger := s.logger.With("feed", "seattle-feed")
25
+
return &SeattleFeed{
31
+
func (f *SeattleFeed) Name() string {
35
+
func (f *SeattleFeed) HandleGetFeedSkeleton(e echo.Context, req FeedSkeletonRequest) error {
36
+
ctx := e.Request().Context()
38
+
cursor, err := getTimeBasedCursor(req)
40
+
f.logger.Error("error getting cursor", "error", err)
41
+
return helpers.InputError(e, "FeedError", "Invalid cursor for feed")
44
+
posts, err := f.getPosts(ctx)
46
+
f.logger.Error("error getting posts", "error", err)
47
+
return helpers.ServerError(e, "FeedError", "Unable to get posts for feed")
50
+
for i, p := range posts {
51
+
if p.CreatedAt.Before(cursor) {
57
+
if len(posts) > 30 {
61
+
var items []FeedPostItem
62
+
for _, p := range posts {
63
+
items = append(items, FeedPostItem{
68
+
newCursor := fmt.Sprintf("%d", posts[len(posts)-1].CreatedAt.UnixMilli())
70
+
return e.JSON(200, FeedSkeletonResponse{
76
+
func (f *SeattleFeed) getPosts(ctx context.Context) ([]RankedFeedPost, error) {
79
+
expiresAt := f.cacheExpiresAt
82
+
if posts != nil && now.Before(expiresAt) {
89
+
if f.cached != nil && now.Before(expiresAt) {
90
+
return f.cached, nil
93
+
if err := f.conn.Select(ctx, &posts, seattleQuery); err != nil {
97
+
f.cacheExpiresAt = now
102
+
var seattleQuery = `
104
+
count(*) as like_ct,
107
+
dateDiff('hour', sp.created_at, now()) as hours_old,
108
+
count(*) * exp(-0.1 * dateDiff('hour', sp.created_at, now())) as decay_score
109
+
FROM seattle_post sp
110
+
LEFT JOIN default.like_by_subject i ON sp.uri = i.subject_uri
111
+
WHERE sp.created_at > now() - INTERVAL 1 DAY
112
+
GROUP BY sp.uri, sp.created_at
113
+
ORDER BY decay_score DESC