Read/seen ranking
When track_read or track_seen is enabled on a feed group, you can use is_read and is_seen as variables in ranking expressions. On flat content feeds, enable them with activity_marks; notification feeds use their notification configuration. On content feeds, the ranking expression may only reference is_read or is_seen when the matching activity_marks.track_read or activity_marks.track_seen is enabled, otherwise creating or updating the feed group fails with ranking by read/seen status requires notification config or matching activity_marks tracking. Notification feeds can always rank by is_read and is_seen.
A mark only applies to activities created at or before the time it was set. On aggregated notification feeds, a group that is updated after it was marked counts as unread or unseen again.
Ranking by is_read / is_seen is only available on Enterprise plans. Contact support to enable this feature for your organization.
For example, a custom flat feed group that sorts by popularity and subtracts 100 points from activities the user has read:
response, err := client.Feeds().CreateFeedGroup(ctx, &getstream.CreateFeedGroupRequest{
ID: "ranked_timeline",
ActivityMarks: &getstream.ActivityMarksConfig{
TrackSeen: getstream.PtrTo(true),
TrackRead: getstream.PtrTo(true),
},
ActivitySelectors: getstream.PtrTo([]getstream.ActivitySelectorConfig{
{Type: getstream.PtrTo("following")},
}),
Ranking: &getstream.RankingConfig{
Type: getstream.PtrTo("expression"),
Score: getstream.PtrTo("popularity - (is_read ? 100 : 0)"),
},
})To rank unseen activities first instead (this does not include popularity):
response, err := client.Feeds().CreateFeedGroup(ctx, &getstream.CreateFeedGroupRequest{
ID: "unseen_timeline",
ActivityMarks: &getstream.ActivityMarksConfig{
TrackSeen: getstream.PtrTo(true),
TrackRead: getstream.PtrTo(true),
},
ActivitySelectors: getstream.PtrTo([]getstream.ActivitySelectorConfig{
{Type: getstream.PtrTo("following")},
}),
Ranking: &getstream.RankingConfig{
Type: getstream.PtrTo("expression"),
Score: getstream.PtrTo("is_seen ? 0 : 100"),
},
})The same expressions work on notification feeds, where marks may be resolved per activity or aggregated group.
Example expressions
| Expression | Effect |
|---|---|
is_seen ? 0 : 100 |
Unseen activities ranked first |
is_read ? 0 : 100 |
Unread activities ranked first |
popularity - (is_read ? 10 : 0) |
Penalize read activities by 10 points |
popularity - (is_read ? 10 : 0) - (is_seen ? 5 : 0) |
Penalize both read and seen activities |
(is_seen ? 0 : 1000) + decay_linear(time) |
Unseen activities first, with time decay |
Aggregated feeds
On aggregated notification feeds, is_read and is_seen are resolved at the group level. All activities within an aggregated group share the same read/seen status. On flat feeds (content feeds with activity_marks, and flat notification feeds), they are resolved per activity. Content feeds with aggregation can't use activity_marks, so they can't rank by read/seen status.
Ranking by is_read / is_seen on content feeds requires matching activity_marks.track_read / activity_marks.track_seen configuration. activity_marks cannot be combined with notification, aggregation, or stories configuration.