cave/store: gather 5 images per tag

This commit is contained in:
Astro 2022-11-25 02:43:28 +01:00
parent f03a83e9cd
commit 3e2efe9cf1
2 changed files with 52 additions and 6 deletions

View File

@ -51,17 +51,29 @@ impl Mention {
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct MediaAttachment {
#[serde(rename = "type")]
pub media_type: String,
pub remote_url: Option<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Post {
pub created_at: String,
pub uri: String,
#[serde(default = "String::new")]
pub content: String,
pub account: Account,
#[serde(default)]
pub tags: Vec<Tag>,
pub application: Option<Application>,
pub sensitive: Option<bool>,
#[serde(default)]
pub mentions: Vec<Mention>,
pub language: Option<String>,
#[serde(default)]
pub media_attachments: Vec<MediaAttachment>,
}
impl Post {

View File

@ -9,6 +9,7 @@ const POST_EXPIRE: usize = 86400;
const TAG_EXPIRE: u64 = 30 * 24;
pub const TREND_POOL_SIZE: usize = 20;
pub const IMAGES_PER_TAG: usize = 5;
pub type Error = RedisError;
@ -168,7 +169,7 @@ impl Store {
let language = post.lang();
let mut cmd = redis::pipe();
let mut store_tags = |spellings, tag_key, user_key| {
let store_tags = |cmd: &mut redis::Pipeline, spellings, tag_key, user_key| {
// by spelling
for spelling in spellings {
cmd.hincr(
@ -186,29 +187,62 @@ impl Store {
if let Some(user_id) = &user_id {
// users by tag/hour
cmd.sadd(&user_key, &user_id).ignore()
.expire(&user_key, TAG_EXPIRE as usize * 86400);
.expire(&user_key, TAG_EXPIRE as usize * 86400)
.ignore();
}
};
let images = if post.sensitive == Some(false) {
post.media_attachments.iter()
.filter(|a| a.media_type == "image")
.filter_map(|a| a.remote_url.as_ref())
.take(2)
.collect::<Vec<&String>>()
} else {
// ignore disturbing porn images from sensitive posts
vec![]
};
let mut image_keys = vec![];
for (name, spellings) in post.tags_set() {
log::debug!("tag {}", name);
// global
store_tags(
store_tags(&mut cmd,
spellings.clone(),
format!("g:{}", name),
format!("u::{}:{}", hour, name),
);
// by language
if let Some(language) = &language {
store_tags(
store_tags(&mut cmd,
spellings,
format!("l:{}:{}", language, name),
format!("u:{}:{}:{}", language, hour, name),
);
}
for image in &images {
let image_key = format!("i:{}", name);
cmd.sadd(&image_key, image)
.ignore()
.expire(&image_key, TAG_EXPIRE as usize * 86400)
.ignore()
.scard(&image_key);
image_keys.push(image_key);
}
}
match cmd.query_async(self).await {
Ok(()) => {}
match cmd.query_async::<_, Vec<usize>>(self).await {
Ok(image_key_sizes) => {
assert_eq!(image_keys.len(), image_key_sizes.len());
let mut cmd = redis::pipe();
for (image_key, size) in image_keys.into_iter().zip(image_key_sizes.into_iter()) {
let excess = size.saturating_sub(IMAGES_PER_TAG);
if excess > 0 {
cmd.spop(image_key).arg(excess)
.ignore();
}
}
let _ = cmd.query_async::<_, ()>(self).await;
}
Err(e) => {
log::error!("redis error: {:?}", e);
}