Ranking expressions
A ranking expression computes a score for each activity, and the feed is ordered by that score, highest first. This page is the reference for what an expression or interest ranking can use: activity fields, data types, functions, operators, constants and decay functions. For how to configure ranking on a feed group or feed view, see Ranking.
Activity data
It's not possible to access all fields of an ActivityResponse inside the ranking context. Below you'll find the list of supported fields:
| name | description |
|---|---|
type |
The type of the activity (e.g. the content type string set when creating the activity) |
time |
UNIX timestamp of when the activity was created |
popularity |
The popularity score of an activity (formula: activity.popularity = reactions + comments * 2 + bookmarks * 3 + shares * 3) |
reaction_count |
The sum of all reactions |
reaction_counts |
Reaction counts by type, you can access specific types with dot notation (for example, reaction_counts.like) |
comment_count |
How many comments the activity has |
bookmark_count |
How many bookmarks the activity has |
share_count |
How many shares the activity has (Increased when an activity is set as the parent_id of another activity) |
custom |
The custom field of the activity, nested fields can be accessed with . notation, for example: custom.topic |
selector_source |
Which activity selector provided this activity (e.g., "following", "popular", "interest"). See Ranking by Selector Source for usage examples. |
interest_score |
Only available when using interest type ranking. A number between -1 and 1 reflecting how much a given activity matches the user's interests. See Interest weights to see how a user's interests are computed/configured. |
preference_score |
Available when using expression or interest type ranking. A number reflecting how much a given activity matches the user's preferences based on activity feedback (show more/less). Activities with interest_tags that have strong dislike are filtered out. See Activity feedback for more information. |
is_read |
Boolean. Available when track_read is enabled on a custom feed group or the built-in notification group (including flat). true if the activity (or its aggregated group) has been marked as read. Uses time-based invalidation. Enterprise plan only. See Ranking by Read/Seen Status. |
is_seen |
Boolean. Available when track_seen is enabled on a custom feed group or the built-in notification group (including flat). Same time-based invalidation as is_read. Enterprise plan only. See Ranking by Read/Seen Status. |
Every field the expression references that isn't a built-in field, such as custom.*, external.*, metrics.* and selector_source, needs a default value in defaults. The default is used for activities that don't have the field, and creating or updating a feed group or feed view without it fails with expression references fields without defaults: <field>. Write defaults as nested objects ({ "custom": { "foo": 0 } }), not dotted keys ("custom.foo"). Built-in fields such as popularity or share_count can't have defaults.
from getstream.models import ActivitySelectorConfig, RankingConfig
client.feeds.create_feed_group(
id="myid",
activity_selectors=[ActivitySelectorConfig(type="following")],
ranking=RankingConfig(
type="expression",
score="popularity * (custom.isBoosted ? 1.5 : 1)",
defaults={"custom": {"isBoosted": False}},
),
)Data types
The following data types are supported in the ranking expression context:
| name | example |
|---|---|
| Boolean | true, false |
| Integer | 36, 0b101010 |
| Float | 0.2 |
| String | "foo" |
| Array | ["a", "b", "c"] |
| Map | {foo: "bar"} |
| Nil | nil |
Functions
The following functions can be used in ranking expressions:
| name | description |
|---|---|
| ln(x) | Natural logarithm function |
| log10(x) | Logarithm function base 10 |
| sin(x) | Trigonometric sine function |
| cos(x) | Trigonometric cosine function |
| tan(x) | Trigonometric tangent function |
| asin(x) | Arcsine function |
| acos(x) | Arccosine function |
| atan(x) | Arctangent function |
| abs(x) | Absolute value |
| min(a,b) | Minimum value |
| max(a,b) | Maximum value |
| trunc(x) | Truncates to the nearest integer value |
| round(x) | Rounds to the nearest integer value |
| decay_linear(t) | Linear decay, see Decay & Ranking for more information |
| decay_exp(t) | Exponential decay, see Decay & Ranking for more information |
| decay_gauss(t) | Gaussian decay, see Decay & Ranking for more information |
| rand_normal() | Returns a normally distributed number in the range [-inf, +inf] with standard normal distribution (stddev = 1, mean = 0) |
| rand_normal(a,b,σ,µ) | Returns a normally distributed number in the range [a, b] with specific normal distribution (stddev = σ, mean = µ) |
| rand() | Returns a random number in the range [0, 1.0) |
| to_unix_timestamp(t) | Converts a time value to a UNIX timestamp |
| dist(lat1,lng1,lat2,lng2,unit) | Returns the distance between the two points given by (lat1,lng1) and (lat2,lng2). By default the unit is in kilometers, but the unit can also be "M" for miles or "N" for nautical miles. Coordinates written in the expression must be decimals (51.0, not 51). This function can be combined with the external ranking parameters to rank activities based on a user's distance to them. |
| map_lookup(key, map) | Look up value from a map by key (0.0 if the key is missing), see Arrays and Maps for more information |
| sum_map_lookup(keys, map) | Sum values from a map for multiple keys, see Arrays and Maps for more information |
| in_array(needle, haystack) | Returns 1.0 if a value exists in an array and 0.0 otherwise, see Arrays and Maps for more information |
Operators
The following operators can be used in ranking expressions:
-
Arithmetic operators:
+,-,*,/,%(modulus),^,**(exponent) -
Logical operators:
not/!,&&/and,||/or -
Conditional:
x ? y : z,??,if/else -
Comparison:
==,!=,<,>,<=,>= -
Control order of evaluation with parentheses
() -
Array/map operators:
[](index),.,?.,in(checks if an array/map has an item/key) -
String operators:
+(concatenation),contains,startsWith,endsWith
This lets you construct a scoring algorithm like this:
"score":"(a > 2 || (b > 4 && c > 3)) ? 1 : -1"Constants
The following constants and helpers are also available to use in ranking expressions:
| name | description |
|---|---|
e / E |
Euler's number |
pi / PI |
π |
current_time |
UNIX timestamp of the current time |
Decay & Ranking
Stream supports linear, exponential, and Gaussian decay. The built-in decay_linear(t), decay_exp(t) and decay_gauss(t) take one argument and always use the default parameters below (origin now, scale 5 days, no offset, decay 0.5); extra arguments are ignored. To change the parameters, define your own function under functions with a base of decay_linear, decay_exp or decay_gauss, and call it by name in score. All parameter values are strings.

Parameters
| name | description | default |
|---|---|---|
| base | Required. The decay function to use: decay_linear, decay_exp or decay_gauss. |
|
| origin | The best possible value, as a number (for example a UNIX timestamp, or a popularity). If the value is equal to the origin, the decay function will return 1. | now |
| scale | Determines how quickly the score drops from 1.0 to the decay value. If the scale is set to "3d", the score is equal to the decay value 3 days beyond the offset. | 5d |
| offset | Values within the offset of the origin score 1. The decay starts beyond the offset. | 0 |
| decay | The score that a value at scale distance from the origin should receive. | 0.5 |
| direction | both, right or left. With right, only values below the origin decay (for timestamps, older activities) and values above it score 1. With left, only values above the origin decay. |
both |
For scale and offset you can use s, m, h, d, w and y to specify seconds, minutes, hours, days, weeks and years, with a whole number (for example 36h, not 1.5d). A plain number is used as is.
The example below defines a simple_gauss with the following parameters:
-
scale: 5 days
-
offset: 1 day
-
decay: 0.3
This means that an activity younger than 1 day (the offset) will return a score of 1. An activity that is exactly 6 days old (offset + scale) will get a score of 0.3 (the decay factor).
The example also defines a popularity_gauss that scores activities with a popularity of 100 or more as 1 (origin: "100" with direction: "right"), and lowers the score of less popular activities. A function applied to something other than time needs an origin, because the default origin is the current time. The full example:
from getstream.models import ActivitySelectorConfig, DecayFunctionConfig, RankingConfig
client.feeds.create_feed_group(
id="myid",
activity_selectors=[ActivitySelectorConfig(type="following")],
ranking=RankingConfig(
type="expression",
score="simple_gauss(time)*popularity_gauss(popularity)",
functions={
"simple_gauss": DecayFunctionConfig(
base="decay_gauss",
scale="5d",
offset="1d",
decay="0.3",
),
"popularity_gauss": DecayFunctionConfig(
base="decay_gauss",
origin="100",
direction="right",
scale="100",
offset="5",
decay="0.5",
),
},
),
)Ranking by Selector Source
When using multiple activity selectors, each activity is tagged with a selector_source field indicating which selector provided it. This enables you to prioritize activities from specific sources in your ranking expressions.
from getstream.models import ActivitySelectorConfig, RankingConfig
# Example 1: Simple boost - following activities get 2x multiplier
client.feeds.create_feed_group(
id="selector_timeline",
activity_selectors=[
ActivitySelectorConfig(type="following"),
ActivitySelectorConfig(type="popular", min_popularity=1),
],
ranking=RankingConfig(
type="expression",
score='selector_source == "following" ? popularity * 2 : popularity',
# Required when using selector_source in expressions
defaults={"selector_source": ""},
),
)
# Example 2: Multiple selectors with different bonuses
client.feeds.create_feed_group(
id="multi_selector_timeline",
activity_selectors=[
ActivitySelectorConfig(type="following"),
ActivitySelectorConfig(type="interest"),
ActivitySelectorConfig(type="popular", min_popularity=1),
],
ranking=RankingConfig(
type="expression",
score=(
'(selector_source == "following" ? 10 : 0) + '
'(selector_source == "interest" ? 5 : 0) + '
"popularity"
),
defaults={"selector_source": ""},
),
)Important notes:
- Defaults required: You must provide
selector_sourcein thedefaultsmap (typically as an empty string"") - Duplicate activities: If an activity matches multiple selectors, it keeps a single
selector_source.followingtakes precedence over the other selectors - Valid sources:
following,popular,interest,query,proximity,current_feed,follow_suggestion