Feeds
This page covers feed queries for returning ranked lists of recommended items. For query fundamentals, see Query Basics.
A feed query returns a ranked list of recommended items. Feeds can be chronological, trending, or personalized to a specific user. Common applications include homepages, "For You" carousels, and discovery experiences.
Personalized feeds use a rank pipeline that:
- Retrieves candidate items
- Optionally filters out unwanted items (e.g., already seen)
- Scores candidates using a model
- Reorders results with diversity and exploration
- Returns ranked results
Chronological feeds
Chronological feeds return items ordered by creation time, showing the most recent items first. This is useful for news feeds, social media timelines, or any feed where recency is important.
Prerequisites
- An engine with item data configured
- A timestamp column (e.g.,
created_at) on items
Query example
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='created_at DESC', limit=100)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "created_at", "ascending": false }],
"limit": 100
}
],
"limit": 20
}
}
You can also use the derived chronological rank column if you have an interaction table:
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='_derived_chronological_rank ASC', limit=100)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "_derived_chronological_rank", "ascending": true }],
"limit": 100
}
],
"limit": 20
}
}
Trending feeds
Trending feeds return items ordered by popularity metrics, showing items that are currently popular or gaining traction. This is useful for discovery pages or "trending now" sections.
Prerequisites
- An engine with item data configured
- Popularity metrics (e.g., views, likes, engagement scores)
Query example
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='views DESC, likes DESC, created_at DESC', limit=100)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [
{ "name": "views", "ascending": false },
{ "name": "likes", "ascending": false },
{ "name": "created_at", "ascending": false }
],
"limit": 100
}
],
"limit": 20
}
}
You can also use the derived popular rank column if you have an interaction table:
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='_derived_popular_rank ASC', limit=100)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 100
}
],
"limit": 20
}
}
Trending with time decay
Use a time decay formula to surface items that are both popular and recent, giving more weight to recent engagement:
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='_derived_popular_rank ASC', limit=1000)
ORDER BY (item.score - 1) / ((((now_seconds() - item.published_at) / 3600) + 2) ** 1.8)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 1000
}
],
"score": {
"value_model": "(item.score - 1) / ((((now_seconds() - item.published_at) / 3600) + 2) ** 1.8)"
},
"limit": 20
}
}
Personalized feed with multiple retrievers
A comprehensive personalized feed combines multiple retrieval strategies, scoring models, diversity reordering, and exploration. A good baseline includes:
- A content-based retriever (finds items similar to user's past interactions)
- A collaborative retriever (finds items liked by similar users)
- A trending list (ensures fresh, popular content)
- A lightgbm scoring model (ranks candidates by predicted engagement)
Prerequisites
- An engine with item data configured
- A trained collaborative embedding (e.g., ALS)
- A trained content embedding (e.g., text embedding)
- A trained scoring model (e.g.,
lightgbm) - A
user_idto personalize for
Query example
- ShapedQL
- JSON
SELECT *
FROM similarity(embedding_ref='item_content_embedding',
encoder='interaction_round_robin',
input_user_id='$user_id', limit=50),
similarity(embedding_ref='als_embedding',
encoder='precomputed_user',
input_user_id='$user_id', limit=50),
column_order(columns='_derived_popular_rank ASC', limit=50)
WHERE item_id NOT IN (SELECT item_id FROM interactions
WHERE user_id = '$user_id')
ORDER BY lightgbm
REORDER BY diversity(0.3), exploration(0.2)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "similarity",
"embedding_ref": "item_content_embedding",
"query_encoder": {
"type": "interaction_round_robin",
"input_user_id": "$parameters.user_id"
},
"limit": 50
},
{
"type": "similarity",
"embedding_ref": "als_embedding",
"query_encoder": {
"type": "precomputed_user",
"input_user_id": "$parameters.user_id"
},
"limit": 50
},
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 50
}
],
"filter": {
"predicate": "item_id NOT IN (SELECT item_id FROM interactions WHERE user_id = '$parameters.user_id')"
},
"score": {
"value_model": "lightgbm",
"input_user_id": "$parameters.user_id"
},
"reorder": {
"diversity": 0.3,
"exploration": 0.2
},
"limit": 20
},
"parameters": {
"user_id": "user123"
}
}
This query:
- Retrieves candidates from three sources:
- Content-based similarity (items similar to what the user has interacted with)
- Collaborative similarity (items liked by similar users)
- Trending items (popular items for freshness)
- Filters out items the user has already interacted with
- Scores candidates using a lightgbm model to predict engagement
- Reorders with diversity (30%) to ensure variety and exploration (20%) to surface new items
- Returns the top 20 personalized items
Combining multiple models in personalized feeds
Use an ensemble of models to balance different signals when ranking feed items:
- ShapedQL
- JSON
SELECT *
FROM similarity(embedding_ref='item_content_embedding',
encoder='interaction_round_robin',
input_user_id='$user_id', limit=50),
similarity(embedding_ref='als_embedding',
encoder='precomputed_user',
input_user_id='$user_id', limit=50),
column_order(columns='_derived_popular_rank ASC', limit=50)
WHERE item_id NOT IN (SELECT item_id FROM interactions
WHERE user_id = '$user_id')
ORDER BY 0.6 * lightgbm + 0.4 * bert4rec
REORDER BY diversity(0.3), exploration(0.2)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "similarity",
"embedding_ref": "item_content_embedding",
"query_encoder": {
"type": "interaction_round_robin",
"input_user_id": "$parameters.user_id"
},
"limit": 50
},
{
"type": "similarity",
"embedding_ref": "als_embedding",
"query_encoder": {
"type": "precomputed_user",
"input_user_id": "$parameters.user_id"
},
"limit": 50
},
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 50
}
],
"filter": {
"predicate": "item_id NOT IN (SELECT item_id FROM interactions WHERE user_id = '$parameters.user_id')"
},
"score": {
"value_model": "0.6 * lightgbm + 0.4 * bert4rec",
"input_user_id": "$parameters.user_id"
},
"reorder": {
"diversity": 0.3,
"exploration": 0.2
},
"limit": 20
},
"parameters": {
"user_id": "user123"
}
}
Personalized trendy feed
Combine popularity, personalization, and time decay to create a feed that surfaces items that are both trending and relevant to the user:
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='_derived_popular_rank ASC', limit=1000)
WHERE item_id NOT IN (SELECT item_id FROM interactions
WHERE user_id = '$user_id')
ORDER BY ((item.score / 1000) +
cosine_similarity(
text_encoding(item, embedding_ref='text_embedding'),
pooled_text_encoding(user.recent_interactions,
embedding_ref='text_embedding'))) /
((((now_seconds() - item.published_at) / 3600) + 2) ** 1.8)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 1000
}
],
"filter": {
"predicate": "item_id NOT IN (SELECT item_id FROM interactions WHERE user_id = '$parameters.user_id')"
},
"score": {
"value_model": "((item.score / 1000) + cosine_similarity(text_encoding(item, embedding_ref='text_embedding'), pooled_text_encoding(user.recent_interactions, embedding_ref='text_embedding'))) / ((((now_seconds() - item.published_at) / 3600) + 2) ** 1.8)",
"input_user_id": "$parameters.user_id"
},
"limit": 20
},
"parameters": {
"user_id": "user123"
}
}
Using user attributes in feed scoring
Incorporate user attributes (age, location, membership tier) into feed scoring to personalize results based on user profile:
- ShapedQL
- JSON
SELECT *
FROM similarity(embedding_ref='als_embedding',
encoder='precomputed_user',
input_user_id='$user_id', limit=50),
column_order(columns='_derived_popular_rank ASC', limit=50)
WHERE item_id NOT IN (SELECT item_id FROM interactions
WHERE user_id = '$user_id')
ORDER BY lightgbm + 0.1 * user.age - 0.05 * item.price +
0.2 * item.rating + 0.15 * user.membership_tier
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "similarity",
"embedding_ref": "als_embedding",
"query_encoder": {
"type": "precomputed_user",
"input_user_id": "$parameters.user_id"
},
"limit": 50
},
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 50
}
],
"filter": {
"predicate": "item_id NOT IN (SELECT item_id FROM interactions WHERE user_id = '$parameters.user_id')"
},
"score": {
"value_model": "lightgbm + 0.1 * user.age - 0.05 * item.price + 0.2 * item.rating + 0.15 * user.membership_tier",
"input_user_id": "$parameters.user_id"
},
"limit": 20
},
"parameters": {
"user_id": "user123"
}
}
Anonymous feed
For users without stored history, you can return popular items. This is useful for new users or anonymous visitors:
- ShapedQL
- JSON
SELECT *
FROM column_order(columns='_derived_popular_rank ASC', limit=100)
LIMIT 20
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "column_order",
"columns": [{ "name": "_derived_popular_rank", "ascending": true }],
"limit": 100
}
],
"limit": 20
}
}