Reranking
This page covers reranking queries for re-sorting lists of candidate items. For query fundamentals, see Query Basics.
A reranking query takes a list of candidate items and re-sorts them. Use reranking when you have candidates from an external source (e.g., a search engine, a catalog filter, or business logic) and want to reorder them.
Reranking is different from a full rank query - it only scores and reorders the provided candidates rather than retrieving new ones.
For reranking, you typically want to use a more heavy-weight scoring model
like click_through_rate or similar models that can accurately
predict user engagement on a smaller set of candidates.
Text reranking strategies
Reranking commonly uses:
- Trained scoring models like
click_through_rate. - Zero-shot text rerankers like
colbert_v2()andcross_encoder(). - Rank fusion patterns like RRF and linear interpolation.
The reference documentation for these scoring expressions lives in ShapedQL:
Example:
SELECT *
FROM ids($candidate_item_ids)
ORDER BY score(expression='colbert_v2(item, $params.query)')
LIMIT 20
Rerank by item IDs
Prerequisites
- An engine configured with item data
- A trained scoring model (e.g.,
click_through_rate) - A list of candidate item IDs to rerank
- Optionally, a user ID for personalized reranking
Query example
Use the ids retriever to rerank a list of known items with a scoring model:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM ids($candidate_item_ids)
ORDER BY score(expression='click_through_rate', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
LIMIT 10
from shaped import RankQueryBuilder, CandidateIds
# Rerank a list of candidate items
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
CandidateIds(
item_ids='$candidate_item_ids'
)
)
.score(
value_model='click_through_rate',
input_user_id='$user_id',
input_interactions_item_ids='$interaction_item_ids'
)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'candidate_item_ids': ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Rerank a list of candidate items
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.candidateIds({
itemIds: '$candidate_item_ids'
})
)
.score({
valueModel: 'click_through_rate',
inputUserId: '$user_id',
inputInteractionsItemIds: '$interaction_item_ids'
})
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// candidate_item_ids: ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "candidate_ids",
"item_ids": "$parameters.candidate_item_ids"
}
],
"score": {
"value_model": "click_through_rate",
"input_user_id": "$parameters.user_id",
"input_interactions_item_ids": "$parameters.interaction_item_ids"
},
"limit": 10
},
"parameters": {
"candidate_item_ids": ["item_1", "item_2", "item_3", "item_4", "item_5"],
"user_id": "user123",
"interaction_item_ids": ["item789", "item012"]
}
}
Reranking with model ensembles
Combine multiple scoring models when reranking to balance different signals:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM ids($candidate_item_ids)
ORDER BY score(expression='0.6 * click_through_rate + 0.4 * conversion_rate', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
LIMIT 10
from shaped import RankQueryBuilder, CandidateIds
# Rerank with model ensemble
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
CandidateIds(
item_ids='$candidate_item_ids'
)
)
.score(
value_model='0.6 * click_through_rate + 0.4 * conversion_rate',
input_user_id='$user_id',
input_interactions_item_ids='$interaction_item_ids'
)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'candidate_item_ids': ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Rerank with model ensemble
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.candidateIds({
itemIds: '$candidate_item_ids'
})
)
.score({
valueModel: '0.6 * click_through_rate + 0.4 * conversion_rate',
inputUserId: '$user_id',
inputInteractionsItemIds: '$interaction_item_ids'
})
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// candidate_item_ids: ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "candidate_ids",
"item_ids": "$parameters.candidate_item_ids"
}
],
"score": {
"value_model": "0.6 * click_through_rate + 0.4 * conversion_rate",
"input_user_id": "$parameters.user_id",
"input_interactions_item_ids": "$parameters.interaction_item_ids"
},
"limit": 10
},
"parameters": {
"candidate_item_ids": ["item_1", "item_2", "item_3", "item_4", "item_5"],
"user_id": "user123",
"interaction_item_ids": ["item789", "item012"]
}
}
Reranking with diversity
You can also add diversity reordering to ensure variety in the results:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM ids($candidate_item_ids)
ORDER BY score(expression='click_through_rate', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
REORDER BY diversity(0.3)
LIMIT 10
from shaped import RankQueryBuilder, CandidateIds
# Rerank with diversity
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
CandidateIds(
item_ids='$candidate_item_ids'
)
)
.score(
value_model='click_through_rate',
input_user_id='$user_id',
input_interactions_item_ids='$interaction_item_ids'
)
.reorder(diversity=0.3)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'candidate_item_ids': ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Rerank with diversity
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.candidateIds({
itemIds: '$candidate_item_ids'
})
)
.score({
valueModel: 'click_through_rate',
inputUserId: '$user_id',
inputInteractionsItemIds: '$interaction_item_ids'
})
.reorder({ diversity: 0.3 })
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// candidate_item_ids: ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "candidate_ids",
"item_ids": "$parameters.candidate_item_ids"
}
],
"score": {
"value_model": "click_through_rate",
"input_user_id": "$parameters.user_id",
"input_interactions_item_ids": "$parameters.interaction_item_ids"
},
"reorder": {
"diversity": 0.3
},
"limit": 10
},
"parameters": {
"candidate_item_ids": ["item_1", "item_2", "item_3", "item_4", "item_5"],
"user_id": "user123",
"interaction_item_ids": ["item789", "item012"]
}
}
Using item attributes in reranking
Incorporate item attributes like price, rating, or review count into reranking to boost or penalize items based on business logic:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM ids($candidate_item_ids)
ORDER BY score(expression='click_through_rate - 0.05 * item.price + 0.2 * item.rating + 0.15 * item.review_count', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
LIMIT 10
from shaped import RankQueryBuilder, CandidateIds
# Rerank with item attributes
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
CandidateIds(
item_ids='$candidate_item_ids'
)
)
.score(
value_model='click_through_rate - 0.05 * item.price + 0.2 * item.rating + 0.15 * item.review_count',
input_user_id='$user_id',
input_interactions_item_ids='$interaction_item_ids'
)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'candidate_item_ids': ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Rerank with item attributes
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.candidateIds({
itemIds: '$candidate_item_ids'
})
)
.score({
valueModel: 'click_through_rate - 0.05 * item.price + 0.2 * item.rating + 0.15 * item.review_count',
inputUserId: '$user_id',
inputInteractionsItemIds: '$interaction_item_ids'
})
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// candidate_item_ids: ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "candidate_ids",
"item_ids": "$parameters.candidate_item_ids"
}
],
"score": {
"value_model": "click_through_rate - 0.05 * item.price + 0.2 * item.rating + 0.15 * item.review_count",
"input_user_id": "$parameters.user_id",
"input_interactions_item_ids": "$parameters.interaction_item_ids"
},
"limit": 10
},
"parameters": {
"candidate_item_ids": ["item_1", "item_2", "item_3", "item_4", "item_5"],
"user_id": "user123",
"interaction_item_ids": ["item789", "item012"]
}
}
Rerank by item attributes
Prerequisites
- An engine configured with item data
- A trained scoring model (e.g.,
click_through_rate) - A list of candidate item attribute dictionaries
- Optionally, a user ID for personalized reranking
Query example
Use the candidate_attributes retriever when you need to rerank items that
aren't in your catalog - for example, items from an external API or newly
created items:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM candidate_attributes($item_attributes)
ORDER BY score(expression='click_through_rate', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
LIMIT 10
from shaped import RankQueryBuilder, CandidateAttributes
# Rerank items by attributes
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
CandidateAttributes(
item_attributes=[
{'title': 'Product A', 'category': 'electronics'},
{'title': 'Product B', 'category': 'clothing'}
]
)
)
.score(
value_model='click_through_rate',
input_user_id='$user_id',
input_interactions_item_ids='$interaction_item_ids'
)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Rerank items by attributes
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.candidateAttributes({
itemAttributes: [
{ title: 'Product A', category: 'electronics' },
{ title: 'Product B', category: 'clothing' }
]
})
)
.score({
valueModel: 'click_through_rate',
inputUserId: '$user_id',
inputInteractionsItemIds: '$interaction_item_ids'
})
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"type": "candidate_attributes",
"item_attributes": [
{ "title": "Product A", "category": "electronics" },
{ "title": "Product B", "category": "clothing" }
]
}
],
"score": {
"value_model": "click_through_rate",
"input_user_id": "$parameters.user_id",
"input_interactions_item_ids": "$parameters.interaction_item_ids"
},
"limit": 10
},
"parameters": {
"user_id": "user123"
}
}
Combining reranking with retrieval scores
If your candidates come from a retrieval step (e.g., search or similarity), you can blend retrieval scores with model predictions:
- ShapedQL
- Python SDK
- TypeScript SDK
- JSON
SELECT *
FROM text_search(query='$query_text', mode='vector',
text_embedding_ref='text_embedding', limit=50,
name='search')
WHERE item_id IN ($candidate_item_ids)
ORDER BY score(expression='0.5 * retrieval.search + 0.5 * click_through_rate', input_user_id='$user_id', input_interactions_item_ids='$interaction_item_ids')
LIMIT 10
from shaped import RankQueryBuilder, TextSearch
# Combine retrieval scores with model predictions
query = (
RankQueryBuilder()
.from_entity('item')
.retrieve(
TextSearch(
input_text_query='$query_text',
mode={'type': 'vector', 'text_embedding_ref': 'text_embedding'},
limit=50,
where="item_id IN ($candidate_item_ids)",
name='search'
)
)
.score(
value_model='0.5 * retrieval.search + 0.5 * click_through_rate',
input_user_id='$user_id'
)
.limit(10)
.build()
)
# Example usage with client
# response = client.rank(
# query=query,
# parameters={
# 'query_text': 'laptop',
# 'candidate_item_ids': ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
# 'user_id': 'user123'
# }
# )
import { RankQueryBuilder } from '@shaped-ai/api';
// Combine retrieval scores with model predictions
const query = new RankQueryBuilder()
.from('item')
.retrieve(step =>
step.textSearch({
inputTextQuery: '$query_text',
mode: { type: 'vector', textEmbeddingRef: 'text_embedding' },
limit: 50,
where: "item_id IN ($candidate_item_ids)",
name: 'search'
})
)
.score({
valueModel: '0.5 * retrieval.search + 0.5 * click_through_rate',
inputUserId: '$user_id'
})
.limit(10)
.build();
// Example usage with client
// const response = await client.rank({
// query,
// parameters: {
// query_text: 'laptop',
// candidate_item_ids: ['item_1', 'item_2', 'item_3', 'item_4', 'item_5'],
// user_id: 'user123'
// }
// });
{
"query": {
"type": "rank",
"from": "item",
"retrieve": [
{
"name": "search",
"type": "text_search",
"input_text_query": "$parameters.query_text",
"mode": { "type": "vector", "text_embedding_ref": "text_embedding" },
"limit": 50,
"where": "item_id IN ($parameters.candidate_item_ids)"
}
],
"score": {
"value_model": "0.5 * retrieval.search + 0.5 * click_through_rate",
"input_user_id": "$parameters.user_id"
},
"limit": 10
},
"parameters": {
"query_text": "laptop",
"candidate_item_ids": ["item_1", "item_2", "item_3", "item_4", "item_5"],
"user_id": "user123"
}
}
When to use each approach
| Approach | Use when |
|---|---|
| Rerank by IDs | Items exist in your catalog and have stored features |
| Rerank by attributes | Items are external, temporary, or newly created without catalog entries |