picker order by2
This commit is contained in:
@@ -25,6 +25,7 @@ const DEFAULT_RESULT_LIMIT: usize = 60;
|
||||
const HARD_RESULT_LIMIT: usize = 200;
|
||||
const DEFAULT_LIST_LIMIT: usize = 5;
|
||||
const SEARCH_SCORE_RELATIVE_FLOOR: f32 = 0.25;
|
||||
const SEARCH_SCORE_GROUP_WIDTH: f32 = 0.25;
|
||||
|
||||
pub struct SearcherService {
|
||||
pub pool: PgPool,
|
||||
@@ -624,6 +625,33 @@ fn order_clause(column: &ResolvedOrderColumn, direction: SearchOrderDirection) -
|
||||
}
|
||||
}
|
||||
|
||||
fn ranked_order_clause(
|
||||
column: &ResolvedOrderColumn,
|
||||
direction: SearchOrderDirection,
|
||||
) -> String {
|
||||
let direction = order_direction_sql(direction);
|
||||
match column {
|
||||
ResolvedOrderColumn::Position => {
|
||||
format!("candidate.candidate_group DESC, positioned.picker_position {}", direction)
|
||||
}
|
||||
ResolvedOrderColumn::Column(column) => {
|
||||
let quoted_column = format!("\"{}\"", column.replace('"', "\"\""));
|
||||
format!(
|
||||
"candidate.candidate_group DESC, positioned.{} {} NULLS LAST, positioned.id ASC",
|
||||
quoted_column, direction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn relevance_group(score: f32, best_score: f32) -> i32 {
|
||||
if best_score <= 0.0 || !best_score.is_finite() || !score.is_finite() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
((score.max(0.0) / best_score / SEARCH_SCORE_GROUP_WIDTH).floor() as i32).clamp(0, 3)
|
||||
}
|
||||
|
||||
async fn fetch_ordered_rows(
|
||||
pool: &PgPool,
|
||||
profile_name: &str,
|
||||
@@ -847,10 +875,15 @@ async fn fetch_ordered_candidate_rows(
|
||||
SELECT t.*, ROW_NUMBER() OVER (ORDER BY id ASC) AS picker_position \
|
||||
FROM {} t WHERE deleted = FALSE\
|
||||
) \
|
||||
SELECT id, to_jsonb(positioned) - 'picker_position' AS data, picker_position \
|
||||
FROM positioned WHERE id = ANY($1) ORDER BY {} LIMIT $2 OFFSET $3",
|
||||
SELECT positioned.id, to_jsonb(positioned) - 'picker_position' AS data, \
|
||||
picker_position, candidate_score \
|
||||
FROM positioned \
|
||||
JOIN UNNEST($1::BIGINT[], $2::REAL[], $3::INTEGER[]) \
|
||||
AS candidate(candidate_id, candidate_score, candidate_group) \
|
||||
ON candidate_id = positioned.id \
|
||||
ORDER BY {} LIMIT $4 OFFSET $5",
|
||||
qualify_profile_table(profile_name, table_name),
|
||||
order_clause(&resolved_order, order.direction),
|
||||
ranked_order_clause(&resolved_order, order.direction),
|
||||
);
|
||||
let ids = candidates
|
||||
.iter()
|
||||
@@ -858,10 +891,21 @@ async fn fetch_ordered_candidate_rows(
|
||||
.collect::<Vec<_>>();
|
||||
let scores = candidates
|
||||
.iter()
|
||||
.map(|(score, id, _)| (*id, *score))
|
||||
.collect::<HashMap<_, _>>();
|
||||
.map(|(score, _, _)| *score)
|
||||
.collect::<Vec<_>>();
|
||||
let best_score = scores
|
||||
.iter()
|
||||
.copied()
|
||||
.max_by(f32::total_cmp)
|
||||
.unwrap_or_default();
|
||||
let groups = scores
|
||||
.iter()
|
||||
.map(|score| relevance_group(*score, best_score))
|
||||
.collect::<Vec<_>>();
|
||||
let rows = sqlx::query(AssertSqlSafe(sql))
|
||||
.bind(&ids)
|
||||
.bind(&scores)
|
||||
.bind(&groups)
|
||||
.bind(limit as i64)
|
||||
.bind(offset as i64)
|
||||
.fetch_all(pool)
|
||||
@@ -874,11 +918,12 @@ async fn fetch_ordered_candidate_rows(
|
||||
let id: i64 = row.try_get("id").unwrap_or_default();
|
||||
let json_data: serde_json::Value = row.try_get("data").unwrap_or_default();
|
||||
let position: i64 = row.try_get("picker_position").unwrap_or_default();
|
||||
let score: f32 = row.try_get("candidate_score").unwrap_or_default();
|
||||
let json_data = remap_json_to_display_names(json_data, &physical_to_display);
|
||||
let display_value = row_display_value(&json_data, &display_column);
|
||||
Hit {
|
||||
id,
|
||||
score: scores.get(&id).copied().unwrap_or_default(),
|
||||
score,
|
||||
content_json: json_data.to_string(),
|
||||
table_name: table_name.to_string(),
|
||||
row_display_value: display_value,
|
||||
@@ -955,4 +1000,26 @@ mod tests {
|
||||
"\"1\" ASC NULLS LAST, id ASC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ranked_order_preserves_relevance_before_column_order() {
|
||||
assert_eq!(
|
||||
ranked_order_clause(
|
||||
&ResolvedOrderColumn::Column("created_at".to_string()),
|
||||
SearchOrderDirection::Desc,
|
||||
),
|
||||
"candidate.candidate_group DESC, positioned.\"created_at\" DESC NULLS LAST, positioned.id ASC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relevance_groups_nearby_high_scores_and_separates_low_scores() {
|
||||
let best_score = 8.7;
|
||||
|
||||
assert_eq!(relevance_group(8.7, best_score), 3);
|
||||
assert_eq!(relevance_group(8.6, best_score), 3);
|
||||
assert_eq!(relevance_group(8.4, best_score), 3);
|
||||
assert_eq!(relevance_group(7.0, best_score), 3);
|
||||
assert_eq!(relevance_group(3.0, best_score), 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user