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
Info:

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_source in the defaults map (typically as an empty string "")
  • Duplicate activities: If an activity matches multiple selectors, it keeps a single selector_source. following takes precedence over the other selectors
  • Valid sources: following, popular, interest, query, proximity, current_feed, follow_suggestion