Feeds — Recipes
Six working patterns you can copy. Each is a complete FeedFacetConfig —
drop it under facets.feed in a lens config, add a matching rules.feed
entry, push, done.
"Freshest stuff first, similarity as a tiebreaker."
feed articles
Config Explorer Call Result
json editor
latest : {
candidates : { from : 'recent' , limit : 100 },
rank : {
recency : { weight : 0.8 , halfLife : '1h' , field : 'published_at' },
similarity : { weight : 0.2 , against : 'topics' },
},
pagination : { pageSize : 20 , dedup : { by : 'sourceRowId' } },
}
The classic "for you" feed. Similarity to user context, popularity boost,
diversified by cuisine so one hot category doesn't eat the page.
feed recipes
Config Explorer Call Result
json editor
discover : {
candidates : { from : 'embeddings' , limit : 500 },
rank : {
similarity : { weight : 0.6 , against : 'liked_titles' },
engagement : {
weight : 0.3 ,
lens : 'recipe_likes' ,
relation : 'likes' ,
aggregate : 'recent_count' ,
window : '24h' ,
decay : 'log' ,
},
recency : { weight : 0.1 , halfLife : '7d' },
diversify : { by : 'cuisine' , maxPerGroup : 3 },
},
pagination : { pageSize : 12 , dedup : { by : 'sourceRowId' , within : 'session' } },
}
"What's hot right now." Engagement dominates; recency keeps the window tight.
feed posts
Config Explorer Call Result
json editor
trending : {
candidates : { from : 'recent' , limit : 300 },
rank : {
engagement : {
weight : 0.7 ,
lens : 'post_likes' ,
relation : 'likes' ,
aggregate : 'recent_count' ,
window : '1h' ,
decay : 'log' ,
},
recency : { weight : 0.3 , halfLife : '6h' , field : 'posted_at' },
diversify : { by : 'author_id' , maxPerGroup : 2 },
},
pagination : { pageSize : 20 },
}
"Things like this one." Zero embedding-API cost per click — uses the seed
record's stored vector directly.
feed recipes
Config Explorer Call Result
json editor
relatedTo : {
candidates : { from : 'embeddings' , limit : 200 },
rank : {
similarity : {
weight : 0.75 ,
against : { from : 'context.seedRecordId' , mode : 'recordVector' },
},
engagement : {
weight : 0.2 ,
lens : 'recipe_likes' ,
relation : 'likes' ,
aggregate : 'count' ,
},
diversify : { by : 'cuisine' , maxPerGroup : 2 },
},
pagination : {
pageSize : 6 ,
dedup : { by : 'sourceRowId' },
excludeIds : 'context.seedRecordId' ,
},
}
A curated feed where the editor promotes specific items. Use boost to
pin without hacking scores.
feed articles
Config Explorer Call Result
json editor
curated : {
candidates : { from : 'embeddings' , limit : 200 },
rank : {
similarity : { weight : 0.5 , against : 'topics' },
recency : { weight : 0.2 , halfLife : '7d' , field : 'published_at' },
boost : [
{ when : { featured : true }, add : 0.5 },
],
},
pagination : { pageSize : 10 },
}
"Feeds from people I follow." Narrow the candidate pool via where on
the candidate selector, then rank by recency + engagement.
feed posts
Config Explorer Call Result
json editor
following : {
candidates : {
from : 'recent' ,
limit : 300 ,
where : { author_id : { $in : 'context.followed_ids' } },
},
rank : {
recency : { weight : 0.7 , halfLife : '6h' , field : 'posted_at' },
engagement : {
weight : 0.3 ,
lens : 'post_likes' ,
relation : 'likes' ,
aggregate : 'recent_count' ,
window : '24h' ,
},
},
pagination : { pageSize : 20 },
}
A few rules that hold across all of the above:
similarity + recency + engagement should sum to ~1.0. Boosts stack on top — treat them as ±0.1 to ±0.5 nudges, not dominating terms.
Engagement gets a smaller weight than your intuition wants. Like counts compound; unchecked, the oldest popular content eats the page. Start at 0.2–0.3.
Recency half-life matches content rhythm. '1h' for breaking news, '6h' for social timelines, '7d' for editorial, '30d' for evergreen.
Diversify if one field dominates the candidate pool. by: 'author_id' for social, by: 'cuisine' for food, by: 'section' for news.
Once you've picked weights, run explain on a few
representative records and watch the contributions array. That's your
feedback loop.