atagen

vineyard

public
Star0
Watch0
ab6cd8e63410ba088ca5b4d4edb18049022ed8eb
1
ssh://brewery@pls.lobotomise.me:33/atagen/vineyard.git

Blame: src/query.rs

25d14e49ab68atagen2026-09-24 11:07 UTC1
use anyhow::{Context as _, Result};
25d14e49ab68atagen2026-09-24 11:07 UTC2
use nostr::filter::Filter;
25d14e49ab68atagen2026-09-24 11:07 UTC3
use rusqlite::types::Value;
25d14e49ab68atagen2026-09-24 11:07 UTC4
25d14e49ab68atagen2026-09-24 11:07 UTC5
const DEFAULT_LIMIT: usize = 10_000;
25d14e49ab68atagen2026-09-24 11:07 UTC6
25d14e49ab68atagen2026-09-24 11:07 UTC7
const SELECT_EVENTS: &str = "
25d14e49ab68atagen2026-09-24 11:07 UTC8
SELECT e.id, e.pubkey, e.created_at, e.kind, e.content, e.tags, e.sig
25d14e49ab68atagen2026-09-24 11:07 UTC9
FROM events AS e
25d14e49ab68atagen2026-09-24 11:07 UTC10
WHERE 1 = 1
25d14e49ab68atagen2026-09-24 11:07 UTC11
";
25d14e49ab68atagen2026-09-24 11:07 UTC12
25d14e49ab68atagen2026-09-24 11:07 UTC13
const TAG_VALUES: &str = "
25d14e49ab68atagen2026-09-24 11:07 UTC14
 AND EXISTS (
25d14e49ab68atagen2026-09-24 11:07 UTC15
    SELECT 1
25d14e49ab68atagen2026-09-24 11:07 UTC16
    FROM event_tags AS tag
25d14e49ab68atagen2026-09-24 11:07 UTC17
    WHERE tag.event_id = e.id
25d14e49ab68atagen2026-09-24 11:07 UTC18
      AND tag.tag_name = ?
25d14e49ab68atagen2026-09-24 11:07 UTC19
      AND tag.tag_value IN (
25d14e49ab68atagen2026-09-24 11:07 UTC20
";
25d14e49ab68atagen2026-09-24 11:07 UTC21
25d14e49ab68atagen2026-09-24 11:07 UTC22
pub struct EventQuery {
25d14e49ab68atagen2026-09-24 11:07 UTC23
    pub(crate) sql: String,
25d14e49ab68atagen2026-09-24 11:07 UTC24
    pub(crate) parameters: Vec<Value>,
25d14e49ab68atagen2026-09-24 11:07 UTC25
    pub(crate) limit: usize,
25d14e49ab68atagen2026-09-24 11:07 UTC26
}
25d14e49ab68atagen2026-09-24 11:07 UTC27
25d14e49ab68atagen2026-09-24 11:07 UTC28
impl EventQuery {
25d14e49ab68atagen2026-09-24 11:07 UTC29
    pub(crate) fn new(filter: &Filter) -> Result<Self> {
25d14e49ab68atagen2026-09-24 11:07 UTC30
        if filter.search.is_some() {
25d14e49ab68atagen2026-09-24 11:07 UTC31
            anyhow::bail!("search is not supported");
25d14e49ab68atagen2026-09-24 11:07 UTC32
        }
25d14e49ab68atagen2026-09-24 11:07 UTC33
25d14e49ab68atagen2026-09-24 11:07 UTC34
        let mut query = Self {
25d14e49ab68atagen2026-09-24 11:07 UTC35
            sql: SELECT_EVENTS.to_owned(),
25d14e49ab68atagen2026-09-24 11:07 UTC36
            parameters: Vec::new(),
25d14e49ab68atagen2026-09-24 11:07 UTC37
            limit: filter.limit.unwrap_or(DEFAULT_LIMIT),
25d14e49ab68atagen2026-09-24 11:07 UTC38
        };
25d14e49ab68atagen2026-09-24 11:07 UTC39
25d14e49ab68atagen2026-09-24 11:07 UTC40
        if let Some(ids) = filter.ids.as_ref() {
25d14e49ab68atagen2026-09-24 11:07 UTC41
            query.add_values(
25d14e49ab68atagen2026-09-24 11:07 UTC42
                "e.id",
25d14e49ab68atagen2026-09-24 11:07 UTC43
                ids.iter()
25d14e49ab68atagen2026-09-24 11:07 UTC44
                    .map(|id| Value::Blob(id.as_bytes().to_vec()))
25d14e49ab68atagen2026-09-24 11:07 UTC45
                    .collect(),
25d14e49ab68atagen2026-09-24 11:07 UTC46
            );
25d14e49ab68atagen2026-09-24 11:07 UTC47
        }
25d14e49ab68atagen2026-09-24 11:07 UTC48
        if let Some(authors) = filter.authors.as_ref() {
25d14e49ab68atagen2026-09-24 11:07 UTC49
            query.add_values(
25d14e49ab68atagen2026-09-24 11:07 UTC50
                "e.pubkey",
25d14e49ab68atagen2026-09-24 11:07 UTC51
                authors
25d14e49ab68atagen2026-09-24 11:07 UTC52
                    .iter()
25d14e49ab68atagen2026-09-24 11:07 UTC53
                    .map(|author| Value::Blob(author.as_bytes().to_vec()))
25d14e49ab68atagen2026-09-24 11:07 UTC54
                    .collect(),
25d14e49ab68atagen2026-09-24 11:07 UTC55
            );
25d14e49ab68atagen2026-09-24 11:07 UTC56
        }
25d14e49ab68atagen2026-09-24 11:07 UTC57
        if let Some(kinds) = filter.kinds.as_ref() {
25d14e49ab68atagen2026-09-24 11:07 UTC58
            query.add_values(
25d14e49ab68atagen2026-09-24 11:07 UTC59
                "e.kind",
25d14e49ab68atagen2026-09-24 11:07 UTC60
                kinds
25d14e49ab68atagen2026-09-24 11:07 UTC61
                    .iter()
25d14e49ab68atagen2026-09-24 11:07 UTC62
                    .map(|kind| Value::Integer(i64::from(kind.as_u16())))
25d14e49ab68atagen2026-09-24 11:07 UTC63
                    .collect(),
25d14e49ab68atagen2026-09-24 11:07 UTC64
            );
25d14e49ab68atagen2026-09-24 11:07 UTC65
        }
25d14e49ab68atagen2026-09-24 11:07 UTC66
        if let Some(since) = filter.since {
25d14e49ab68atagen2026-09-24 11:07 UTC67
            query.add_condition(
25d14e49ab68atagen2026-09-24 11:07 UTC68
                "e.created_at >= ?",
25d14e49ab68atagen2026-09-24 11:07 UTC69
                Value::Integer(timestamp(since.as_secs())?),
25d14e49ab68atagen2026-09-24 11:07 UTC70
            );
25d14e49ab68atagen2026-09-24 11:07 UTC71
        }
25d14e49ab68atagen2026-09-24 11:07 UTC72
        if let Some(until) = filter.until {
25d14e49ab68atagen2026-09-24 11:07 UTC73
            query.add_condition(
25d14e49ab68atagen2026-09-24 11:07 UTC74
                "e.created_at <= ?",
25d14e49ab68atagen2026-09-24 11:07 UTC75
                Value::Integer(timestamp(until.as_secs())?),
25d14e49ab68atagen2026-09-24 11:07 UTC76
            );
25d14e49ab68atagen2026-09-24 11:07 UTC77
        }
25d14e49ab68atagen2026-09-24 11:07 UTC78
        for (tag, values) in &filter.generic_tags {
25d14e49ab68atagen2026-09-24 11:07 UTC79
            if values.is_empty() {
25d14e49ab68atagen2026-09-24 11:07 UTC80
                query.exclude_all();
25d14e49ab68atagen2026-09-24 11:07 UTC81
                break;
25d14e49ab68atagen2026-09-24 11:07 UTC82
            }
25d14e49ab68atagen2026-09-24 11:07 UTC83
            query.sql.push_str(TAG_VALUES);
25d14e49ab68atagen2026-09-24 11:07 UTC84
            query.parameters.push(Value::Text(tag.to_string()));
25d14e49ab68atagen2026-09-24 11:07 UTC85
            query.add_placeholders(values.len());
25d14e49ab68atagen2026-09-24 11:07 UTC86
            query.sql.push_str("))");
25d14e49ab68atagen2026-09-24 11:07 UTC87
            query
25d14e49ab68atagen2026-09-24 11:07 UTC88
                .parameters
25d14e49ab68atagen2026-09-24 11:07 UTC89
                .extend(values.iter().cloned().map(Value::Text));
25d14e49ab68atagen2026-09-24 11:07 UTC90
        }
25d14e49ab68atagen2026-09-24 11:07 UTC91
25d14e49ab68atagen2026-09-24 11:07 UTC92
        query.sql.push_str(" ORDER BY e.created_at DESC, e.id");
25d14e49ab68atagen2026-09-24 11:07 UTC93
        Ok(query)
25d14e49ab68atagen2026-09-24 11:07 UTC94
    }
25d14e49ab68atagen2026-09-24 11:07 UTC95
25d14e49ab68atagen2026-09-24 11:07 UTC96
    fn add_values(&mut self, column: &str, values: Vec<Value>) {
25d14e49ab68atagen2026-09-24 11:07 UTC97
        if values.is_empty() {
25d14e49ab68atagen2026-09-24 11:07 UTC98
            self.exclude_all();
25d14e49ab68atagen2026-09-24 11:07 UTC99
            return;
25d14e49ab68atagen2026-09-24 11:07 UTC100
        }
25d14e49ab68atagen2026-09-24 11:07 UTC101
        self.sql.push_str(" AND ");
25d14e49ab68atagen2026-09-24 11:07 UTC102
        self.sql.push_str(column);
25d14e49ab68atagen2026-09-24 11:07 UTC103
        self.sql.push_str(" IN (");
25d14e49ab68atagen2026-09-24 11:07 UTC104
        self.add_placeholders(values.len());
25d14e49ab68atagen2026-09-24 11:07 UTC105
        self.sql.push(')');
25d14e49ab68atagen2026-09-24 11:07 UTC106
        self.parameters.extend(values);
25d14e49ab68atagen2026-09-24 11:07 UTC107
    }
25d14e49ab68atagen2026-09-24 11:07 UTC108
25d14e49ab68atagen2026-09-24 11:07 UTC109
    fn add_condition(&mut self, condition: &str, value: Value) {
25d14e49ab68atagen2026-09-24 11:07 UTC110
        self.sql.push_str(" AND ");
25d14e49ab68atagen2026-09-24 11:07 UTC111
        self.sql.push_str(condition);
25d14e49ab68atagen2026-09-24 11:07 UTC112
        self.parameters.push(value);
25d14e49ab68atagen2026-09-24 11:07 UTC113
    }
25d14e49ab68atagen2026-09-24 11:07 UTC114
25d14e49ab68atagen2026-09-24 11:07 UTC115
    fn add_placeholders(&mut self, count: usize) {
25d14e49ab68atagen2026-09-24 11:07 UTC116
        for index in 0..count {
25d14e49ab68atagen2026-09-24 11:07 UTC117
            if index > 0 {
25d14e49ab68atagen2026-09-24 11:07 UTC118
                self.sql.push_str(", ");
25d14e49ab68atagen2026-09-24 11:07 UTC119
            }
25d14e49ab68atagen2026-09-24 11:07 UTC120
            self.sql.push('?');
25d14e49ab68atagen2026-09-24 11:07 UTC121
        }
25d14e49ab68atagen2026-09-24 11:07 UTC122
    }
25d14e49ab68atagen2026-09-24 11:07 UTC123
25d14e49ab68atagen2026-09-24 11:07 UTC124
    fn exclude_all(&mut self) {
25d14e49ab68atagen2026-09-24 11:07 UTC125
        self.sql.push_str(" AND 0");
25d14e49ab68atagen2026-09-24 11:07 UTC126
    }
25d14e49ab68atagen2026-09-24 11:07 UTC127
}
25d14e49ab68atagen2026-09-24 11:07 UTC128
25d14e49ab68atagen2026-09-24 11:07 UTC129
fn timestamp(value: u64) -> Result<i64> {
25d14e49ab68atagen2026-09-24 11:07 UTC130
    value.try_into().context("timestamp exceeds sqlite range")
25d14e49ab68atagen2026-09-24 11:07 UTC131
}