Warmup: trådbasert henting — hele tråder lastes komplett
messages-modus: hent de N nyeste trådene (sortert etter siste aktivitet), inkludert alle svar. Ingen orphan-replies. days-modus: finn alle tråder med minst én melding innenfor tidsvinduet, last hele tråden (også eldre trådstartere). all-modus: uendret, henter alt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a6ca0f602d
commit
2ed50d51a9
1 changed files with 112 additions and 35 deletions
|
|
@ -72,49 +72,25 @@ pub async fn run(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bygg WHERE-clause basert på warmup-modus
|
// Trådbasert henting: finn kvalifiserende tråder, hent alle meldinger i disse
|
||||||
let (where_clause, limit) = match ch.config.warmup_mode.as_str() {
|
let rows = match ch.config.warmup_mode.as_str() {
|
||||||
|
"all" => {
|
||||||
|
// Alt — ingen filtrering
|
||||||
|
fetch_messages_all(pool, &ch.id).await?
|
||||||
|
},
|
||||||
"messages" => {
|
"messages" => {
|
||||||
|
// Siste N tråder (sortert etter nyeste melding i tråden)
|
||||||
let n = ch.config.warmup_value.unwrap_or(default_limit);
|
let n = ch.config.warmup_value.unwrap_or(default_limit);
|
||||||
(String::new(), n)
|
fetch_messages_by_threads(pool, &ch.id, n).await?
|
||||||
},
|
},
|
||||||
"days" => {
|
"days" => {
|
||||||
|
// Alle tråder med minst én melding i tidsvinduet
|
||||||
let days = ch.config.warmup_value.unwrap_or(30);
|
let days = ch.config.warmup_value.unwrap_or(30);
|
||||||
(format!("AND m.created_at >= now() - interval '{} days'", days), i64::MAX)
|
fetch_messages_by_days(pool, &ch.id, days).await?
|
||||||
},
|
},
|
||||||
// "all" og alt annet
|
_ => fetch_messages_all(pool, &ch.id).await?,
|
||||||
_ => (String::new(), i64::MAX),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let query = format!(
|
|
||||||
r#"
|
|
||||||
SELECT
|
|
||||||
m.id::text,
|
|
||||||
m.channel_id::text,
|
|
||||||
n.workspace_id::text,
|
|
||||||
COALESCE(m.author_id, ''),
|
|
||||||
COALESCE(u.name, 'Ukjent'),
|
|
||||||
COALESCE(m.body, ''),
|
|
||||||
COALESCE(m.message_type, 'text'),
|
|
||||||
m.reply_to::text,
|
|
||||||
m.created_at::text
|
|
||||||
FROM messages m
|
|
||||||
JOIN nodes n ON n.id = m.id
|
|
||||||
LEFT JOIN users u ON u.authentik_id = m.author_id
|
|
||||||
WHERE m.channel_id = $1::uuid {}
|
|
||||||
ORDER BY m.created_at DESC
|
|
||||||
LIMIT $2
|
|
||||||
"#,
|
|
||||||
where_clause
|
|
||||||
);
|
|
||||||
|
|
||||||
let rows: Vec<(String, String, String, String, String, String, String, Option<String>, String)> =
|
|
||||||
sqlx::query_as(&query)
|
|
||||||
.bind(&ch.id)
|
|
||||||
.bind(limit)
|
|
||||||
.fetch_all(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
info!(channel = %ch.name, mode = %ch.config.warmup_mode, "Ingen meldinger å laste");
|
info!(channel = %ch.name, mode = %ch.config.warmup_mode, "Ingen meldinger å laste");
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -205,6 +181,107 @@ pub async fn run(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageRow = (String, String, String, String, String, String, String, Option<String>, String);
|
||||||
|
|
||||||
|
const MESSAGE_COLUMNS: &str = r#"
|
||||||
|
m.id::text,
|
||||||
|
m.channel_id::text,
|
||||||
|
n.workspace_id::text,
|
||||||
|
COALESCE(m.author_id, ''),
|
||||||
|
COALESCE(u.name, 'Ukjent'),
|
||||||
|
COALESCE(m.body, ''),
|
||||||
|
COALESCE(m.message_type, 'text'),
|
||||||
|
m.reply_to::text,
|
||||||
|
m.created_at::text
|
||||||
|
"#;
|
||||||
|
|
||||||
|
const MESSAGE_JOINS: &str = r#"
|
||||||
|
JOIN nodes n ON n.id = m.id
|
||||||
|
LEFT JOIN users u ON u.authentik_id = m.author_id
|
||||||
|
"#;
|
||||||
|
|
||||||
|
/// Hent alle meldinger i en kanal.
|
||||||
|
async fn fetch_messages_all(pool: &PgPool, channel_id: &str) -> anyhow::Result<Vec<MessageRow>> {
|
||||||
|
let query = format!(
|
||||||
|
"SELECT {} FROM messages m {} WHERE m.channel_id = $1::uuid ORDER BY m.created_at",
|
||||||
|
MESSAGE_COLUMNS, MESSAGE_JOINS
|
||||||
|
);
|
||||||
|
Ok(sqlx::query_as(&query).bind(channel_id).fetch_all(pool).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hent de N nyeste trådene + alle meldinger i disse.
|
||||||
|
/// En "tråd" = en rotmelding (reply_to IS NULL) med alle svar.
|
||||||
|
/// Sortert etter nyeste melding i tråden.
|
||||||
|
async fn fetch_messages_by_threads(pool: &PgPool, channel_id: &str, limit: i64) -> anyhow::Result<Vec<MessageRow>> {
|
||||||
|
// Finn rot-IDer for de N nyeste trådene.
|
||||||
|
// En tråds "siste aktivitet" er max(created_at) blant rot + alle svar.
|
||||||
|
// Løse meldinger (reply_to peker på noe utenfor kanalen) teller som egen tråd.
|
||||||
|
let query = format!(
|
||||||
|
r#"
|
||||||
|
WITH thread_roots AS (
|
||||||
|
-- Finn rot for hver melding: følg reply_to opp til NULL, eller til utenfor kanalen
|
||||||
|
SELECT DISTINCT COALESCE(
|
||||||
|
(SELECT r.id FROM messages r
|
||||||
|
WHERE r.id = m.reply_to AND r.channel_id = m.channel_id AND r.reply_to IS NULL),
|
||||||
|
CASE WHEN m.reply_to IS NULL THEN m.id END,
|
||||||
|
m.id -- orphan-svar → behandles som egen tråd
|
||||||
|
) AS root_id
|
||||||
|
FROM messages m
|
||||||
|
WHERE m.channel_id = $1::uuid
|
||||||
|
),
|
||||||
|
ranked_threads AS (
|
||||||
|
SELECT tr.root_id,
|
||||||
|
max(m.created_at) AS last_activity
|
||||||
|
FROM thread_roots tr
|
||||||
|
JOIN messages m ON m.channel_id = $1::uuid
|
||||||
|
AND (m.id = tr.root_id OR m.reply_to = tr.root_id)
|
||||||
|
GROUP BY tr.root_id
|
||||||
|
ORDER BY last_activity DESC
|
||||||
|
LIMIT $2
|
||||||
|
)
|
||||||
|
SELECT {}
|
||||||
|
FROM messages m
|
||||||
|
{}
|
||||||
|
WHERE m.channel_id = $1::uuid
|
||||||
|
AND (m.id IN (SELECT root_id FROM ranked_threads)
|
||||||
|
OR m.reply_to IN (SELECT root_id FROM ranked_threads))
|
||||||
|
ORDER BY m.created_at
|
||||||
|
"#,
|
||||||
|
MESSAGE_COLUMNS, MESSAGE_JOINS
|
||||||
|
);
|
||||||
|
Ok(sqlx::query_as(&query).bind(channel_id).bind(limit).fetch_all(pool).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hent alle tråder som har minst én melding innen siste N dager.
|
||||||
|
/// Inkluderer hele tråden (også eldre trådstartere).
|
||||||
|
async fn fetch_messages_by_days(pool: &PgPool, channel_id: &str, days: i64) -> anyhow::Result<Vec<MessageRow>> {
|
||||||
|
let query = format!(
|
||||||
|
r#"
|
||||||
|
WITH qualifying_roots AS (
|
||||||
|
-- Finn trådrøtter for meldinger innenfor tidsvinduet
|
||||||
|
SELECT DISTINCT COALESCE(
|
||||||
|
(SELECT r.id FROM messages r
|
||||||
|
WHERE r.id = m.reply_to AND r.channel_id = m.channel_id AND r.reply_to IS NULL),
|
||||||
|
CASE WHEN m.reply_to IS NULL THEN m.id END,
|
||||||
|
m.id
|
||||||
|
) AS root_id
|
||||||
|
FROM messages m
|
||||||
|
WHERE m.channel_id = $1::uuid
|
||||||
|
AND m.created_at >= now() - make_interval(days => $2)
|
||||||
|
)
|
||||||
|
SELECT {}
|
||||||
|
FROM messages m
|
||||||
|
{}
|
||||||
|
WHERE m.channel_id = $1::uuid
|
||||||
|
AND (m.id IN (SELECT root_id FROM qualifying_roots)
|
||||||
|
OR m.reply_to IN (SELECT root_id FROM qualifying_roots))
|
||||||
|
ORDER BY m.created_at
|
||||||
|
"#,
|
||||||
|
MESSAGE_COLUMNS, MESSAGE_JOINS
|
||||||
|
);
|
||||||
|
Ok(sqlx::query_as(&query).bind(channel_id).bind(days as i32).fetch_all(pool).await?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn call_reducer(
|
async fn call_reducer(
|
||||||
http: &Client,
|
http: &Client,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue