nostr_database/
helper.rs

1// Copyright (c) 2022-2023 Yuki Kishimoto
2// Copyright (c) 2023-2025 Rust Nostr Developers
3// Distributed under the MIT software license
4
5//! Nostr Event Store Helper
6//!
7//! Used for the in-memory database.
8
9use std::collections::{BTreeSet, HashMap, HashSet};
10use std::iter;
11use std::ops::Deref;
12use std::sync::Arc;
13
14use nostr::filter::MatchEventOptions;
15use nostr::nips::nip01::{Coordinate, CoordinateBorrow};
16use nostr::{Alphabet, Event, EventId, Filter, Kind, PublicKey, SingleLetterTag, Timestamp};
17use tokio::sync::{OwnedRwLockReadGuard, RwLock};
18
19use crate::collections::tree::{BTreeCappedSet, Capacity, InsertResult, OverCapacityPolicy};
20use crate::{Events, RejectedReason, SaveEventStatus};
21
22type DatabaseEvent = Arc<Event>;
23
24struct QueryByAuthorParams {
25    author: PublicKey,
26    since: Option<Timestamp>,
27    until: Option<Timestamp>,
28}
29
30struct QueryByKindAndAuthorParams {
31    kind: Kind,
32    author: PublicKey,
33    since: Option<Timestamp>,
34    until: Option<Timestamp>,
35}
36
37impl QueryByKindAndAuthorParams {
38    #[inline]
39    pub fn new(kind: Kind, author: PublicKey) -> Self {
40        Self {
41            kind,
42            author,
43            since: None,
44            until: None,
45        }
46    }
47}
48
49struct QueryByParamReplaceable {
50    kind: Kind,
51    author: PublicKey,
52    identifier: String,
53    since: Option<Timestamp>,
54    until: Option<Timestamp>,
55}
56
57impl QueryByParamReplaceable {
58    #[inline]
59    pub fn new(kind: Kind, author: PublicKey, identifier: String) -> Self {
60        Self {
61            kind,
62            author,
63            identifier,
64            since: None,
65            until: None,
66        }
67    }
68}
69
70enum QueryPattern {
71    Author(QueryByAuthorParams),
72    KindAuthor(QueryByKindAndAuthorParams),
73    ParamReplaceable(QueryByParamReplaceable),
74    Generic(Box<Filter>),
75}
76
77impl From<Filter> for QueryPattern {
78    fn from(filter: Filter) -> Self {
79        let (kinds_len, first_kind): (usize, Option<Kind>) = filter
80            .kinds
81            .as_ref()
82            .map(|set| (set.len(), set.iter().next().copied()))
83            .unwrap_or_default();
84        let (authors_len, first_author): (usize, Option<PublicKey>) = filter
85            .authors
86            .as_ref()
87            .map(|set| (set.len(), set.iter().next().copied()))
88            .unwrap_or_default();
89        let ids_len: usize = filter.ids.as_ref().map(|set| set.len()).unwrap_or_default();
90        let generic_tags_len: usize = filter.generic_tags.len();
91        let identifier = filter
92            .generic_tags
93            .get(&SingleLetterTag::lowercase(Alphabet::D))
94            .and_then(|v| v.iter().next().cloned());
95
96        match (
97            kinds_len,
98            first_kind,
99            authors_len,
100            first_author,
101            ids_len,
102            generic_tags_len,
103            identifier,
104            filter.search.as_ref(),
105        ) {
106            (0, None, 1, Some(author), 0, 0, None, None) => Self::Author(QueryByAuthorParams {
107                author,
108                since: filter.since,
109                until: filter.until,
110            }),
111            (1, Some(kind), 1, Some(author), 0, 0, None, None) => {
112                Self::KindAuthor(QueryByKindAndAuthorParams {
113                    kind,
114                    author,
115                    since: filter.since,
116                    until: filter.until,
117                })
118            }
119            (1, Some(kind), 1, Some(author), 0, _, Some(identifier), None)
120                if kind.is_addressable() =>
121            {
122                Self::ParamReplaceable(QueryByParamReplaceable {
123                    kind,
124                    author,
125                    identifier,
126                    since: filter.since,
127                    until: filter.until,
128                })
129            }
130            _ => Self::Generic(Box::new(filter)),
131        }
132    }
133}
134
135/// Database Event Result
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct DatabaseEventResult {
138    /// Status
139    pub status: SaveEventStatus,
140    /// List of events that should be removed from database
141    pub to_discard: HashSet<EventId>,
142}
143
144enum InternalQueryResult<'a> {
145    All,
146    Set(BTreeSet<&'a DatabaseEvent>),
147}
148
149/// Database helper
150#[derive(Debug, Clone, Default)]
151struct InternalDatabaseHelper {
152    /// Sorted events
153    events: BTreeCappedSet<DatabaseEvent>,
154    /// Events by ID
155    ids: HashMap<EventId, DatabaseEvent>,
156    author_index: HashMap<PublicKey, BTreeSet<DatabaseEvent>>,
157    kind_author_index: HashMap<(Kind, PublicKey), BTreeSet<DatabaseEvent>>,
158    param_replaceable_index: HashMap<(Kind, PublicKey, String), DatabaseEvent>,
159    deleted_ids: HashSet<EventId>,
160    deleted_coordinates: HashMap<Coordinate, Timestamp>,
161}
162
163impl InternalDatabaseHelper {
164    pub fn bounded(size: usize) -> Self {
165        let mut helper: InternalDatabaseHelper = InternalDatabaseHelper::default();
166        helper.events.change_capacity(Capacity::Bounded {
167            max: size,
168            policy: OverCapacityPolicy::Last,
169        });
170        helper
171    }
172
173    // Bulk load
174    //
175    // NOT CHANGE `events` ARG! Processing events in ASC it's much more performant
176    pub fn bulk_load(&mut self, events: BTreeSet<Event>) -> HashSet<EventId> {
177        let now: Timestamp = Timestamp::now();
178        events
179            .into_iter()
180            .rev() // Lookup ID: EVENT_ORD_IMPL
181            .filter(|e| !e.kind.is_ephemeral())
182            .map(|event| self.internal_index_event(&event, &now))
183            .flat_map(|res| res.to_discard)
184            .collect()
185    }
186
187    /// Bulk import
188    pub fn bulk_import(&mut self, events: BTreeSet<Event>) -> impl Iterator<Item = Event> + '_ {
189        let now: Timestamp = Timestamp::now();
190        events
191            .into_iter()
192            .rev() // Lookup ID: EVENT_ORD_IMPL
193            .filter(|e| !e.is_expired() && !e.kind.is_ephemeral())
194            .filter(move |event| self.internal_index_event(event, &now).status.is_success())
195    }
196
197    fn internal_index_event(&mut self, event: &Event, now: &Timestamp) -> DatabaseEventResult {
198        // Check if was already added
199        if self.ids.contains_key(&event.id) {
200            return DatabaseEventResult {
201                status: SaveEventStatus::Rejected(RejectedReason::Duplicate),
202                to_discard: HashSet::new(),
203            };
204        }
205
206        // Check if was deleted or is expired
207        if self.deleted_ids.contains(&event.id) {
208            let mut to_discard: HashSet<EventId> = HashSet::with_capacity(1);
209            to_discard.insert(event.id);
210            return DatabaseEventResult {
211                status: SaveEventStatus::Rejected(RejectedReason::Deleted),
212                to_discard,
213            };
214        }
215
216        if event.is_expired_at(now) {
217            let mut to_discard: HashSet<EventId> = HashSet::with_capacity(1);
218            to_discard.insert(event.id);
219            return DatabaseEventResult {
220                status: SaveEventStatus::Rejected(RejectedReason::Expired),
221                to_discard,
222            };
223        }
224
225        let mut to_discard: HashSet<EventId> = HashSet::new();
226
227        // Compose others fields
228        let author: PublicKey = event.pubkey;
229        let created_at: Timestamp = event.created_at;
230        let kind: Kind = event.kind;
231
232        let mut status: SaveEventStatus = SaveEventStatus::Success;
233
234        if kind.is_replaceable() {
235            let params: QueryByKindAndAuthorParams = QueryByKindAndAuthorParams::new(kind, author);
236            for ev in self.internal_query_by_kind_and_author(params) {
237                if ev.created_at > created_at || ev.id == event.id {
238                    status = SaveEventStatus::Rejected(RejectedReason::Replaced);
239                } else {
240                    to_discard.insert(ev.id);
241                }
242            }
243        } else if kind.is_addressable() {
244            match event.tags.identifier() {
245                Some(identifier) => {
246                    let coordinate: Coordinate =
247                        Coordinate::new(kind, author).identifier(identifier);
248
249                    // Check if coordinate was deleted
250                    if self.has_coordinate_been_deleted(&coordinate, now) {
251                        status = SaveEventStatus::Rejected(RejectedReason::Deleted);
252                    } else {
253                        let params: QueryByParamReplaceable =
254                            QueryByParamReplaceable::new(kind, author, identifier.to_string());
255                        if let Some(ev) = self.internal_query_param_replaceable(params) {
256                            if ev.created_at > created_at || ev.id == event.id {
257                                status = SaveEventStatus::Rejected(RejectedReason::Replaced);
258                            } else {
259                                to_discard.insert(ev.id);
260                            }
261                        }
262                    }
263                }
264                None => status = SaveEventStatus::Rejected(RejectedReason::Other),
265            }
266        } else if kind == Kind::EventDeletion {
267            // Check `e` tags
268            for id in event.tags.event_ids() {
269                if let Some(ev) = self.ids.get(id) {
270                    if ev.pubkey != author {
271                        to_discard.insert(event.id);
272                        status = SaveEventStatus::Rejected(RejectedReason::InvalidDelete);
273                        break;
274                    }
275
276                    if ev.created_at <= created_at {
277                        to_discard.insert(ev.id);
278                    }
279                }
280            }
281
282            // Check `a` tags
283            for coordinate in event.tags.coordinates() {
284                if coordinate.public_key != author {
285                    to_discard.insert(event.id);
286                    status = SaveEventStatus::Rejected(RejectedReason::InvalidDelete);
287                    break;
288                }
289
290                // Save deleted coordinate at certain timestamp
291                self.deleted_coordinates
292                    .entry(coordinate.clone())
293                    .and_modify(|t| {
294                        // Update only if newer
295                        if created_at > *t {
296                            *t = created_at
297                        }
298                    })
299                    .or_insert(created_at);
300
301                // Not check if ev.pubkey match the author because assume that query
302                // returned only the events owned by author
303                if !coordinate.identifier.is_empty() {
304                    let mut params: QueryByParamReplaceable = QueryByParamReplaceable::new(
305                        coordinate.kind,
306                        coordinate.public_key,
307                        coordinate.identifier.clone(),
308                    );
309                    params.until = Some(created_at);
310                    if let Some(ev) = self.internal_query_param_replaceable(params) {
311                        to_discard.insert(ev.id);
312                    }
313                } else {
314                    let mut params: QueryByKindAndAuthorParams =
315                        QueryByKindAndAuthorParams::new(coordinate.kind, coordinate.public_key);
316                    params.until = Some(created_at);
317                    to_discard.extend(self.internal_query_by_kind_and_author(params).map(|e| e.id));
318                }
319            }
320        }
321
322        // Remove events
323        self.discard_events(&to_discard);
324
325        // Insert event
326        if status.is_success() {
327            let e: DatabaseEvent = Arc::new(event.clone()); // TODO: avoid clone?
328
329            let InsertResult { inserted, pop } = self.events.insert(e.clone());
330
331            if inserted {
332                self.ids.insert(e.id, e.clone());
333                self.author_index
334                    .entry(author)
335                    .or_default()
336                    .insert(e.clone());
337
338                if kind.is_addressable() {
339                    if let Some(identifier) = e.tags.identifier() {
340                        self.param_replaceable_index
341                            .insert((kind, author, identifier.to_string()), e.clone());
342                    }
343                }
344
345                if kind.is_replaceable() {
346                    let mut set = BTreeSet::new();
347                    set.insert(e);
348                    self.kind_author_index.insert((kind, author), set);
349                } else {
350                    self.kind_author_index
351                        .entry((kind, author))
352                        .or_default()
353                        .insert(e);
354                }
355            } else {
356                to_discard.insert(e.id);
357            }
358
359            if let Some(event) = pop {
360                to_discard.insert(event.id);
361                self.discard_event(event);
362            }
363        }
364
365        DatabaseEventResult { status, to_discard }
366    }
367
368    fn discard_events(&mut self, ids: &HashSet<EventId>) {
369        for id in ids.iter() {
370            if let Some(ev) = self.ids.remove(id) {
371                self.events.remove(&ev);
372
373                if let Some(set) = self.author_index.get_mut(&ev.pubkey) {
374                    set.remove(&ev);
375                }
376
377                if ev.kind.is_addressable() {
378                    if let Some(identifier) = ev.tags.identifier() {
379                        self.param_replaceable_index.remove(&(
380                            ev.kind,
381                            ev.pubkey,
382                            identifier.to_string(),
383                        ));
384                    }
385                }
386
387                if let Some(set) = self.kind_author_index.get_mut(&(ev.kind, ev.pubkey)) {
388                    set.remove(&ev);
389                }
390            }
391            self.deleted_ids.insert(*id);
392        }
393    }
394
395    fn discard_event(&mut self, ev: DatabaseEvent) {
396        self.ids.remove(&ev.id);
397
398        if let Some(set) = self.author_index.get_mut(&ev.pubkey) {
399            set.remove(&ev);
400        }
401
402        if ev.kind.is_addressable() {
403            if let Some(identifier) = ev.tags.identifier() {
404                self.param_replaceable_index
405                    .remove(&(ev.kind, ev.pubkey, identifier.to_string()));
406            }
407        }
408
409        if let Some(set) = self.kind_author_index.get_mut(&(ev.kind, ev.pubkey)) {
410            set.remove(&ev);
411        }
412    }
413
414    /// Import [Event]
415    ///
416    /// **This method assume that [`Event`] was already verified**
417    pub fn index_event(&mut self, event: &Event) -> DatabaseEventResult {
418        // Check if it's ephemeral
419        if event.kind.is_ephemeral() {
420            return DatabaseEventResult {
421                status: SaveEventStatus::Rejected(RejectedReason::Ephemeral),
422                to_discard: HashSet::new(),
423            };
424        }
425        let now = Timestamp::now();
426        self.internal_index_event(event, &now)
427    }
428
429    /// Query by public key
430    fn internal_query_by_author<'a>(
431        &'a self,
432        params: QueryByAuthorParams,
433    ) -> Box<dyn Iterator<Item = &'a DatabaseEvent> + 'a> {
434        let QueryByAuthorParams {
435            author,
436            since,
437            until,
438        } = params;
439        match self.author_index.get(&author) {
440            Some(set) => Box::new(set.iter().filter(move |ev| {
441                if self.deleted_ids.contains(&ev.id) {
442                    return false;
443                }
444
445                if let Some(since) = since {
446                    if ev.created_at < since {
447                        return false;
448                    }
449                }
450
451                if let Some(until) = until {
452                    if ev.created_at > until {
453                        return false;
454                    }
455                }
456
457                true
458            })),
459            None => Box::new(iter::empty()),
460        }
461    }
462
463    /// Query by [`Kind`] and [`PublicKeyPrefix`]
464    fn internal_query_by_kind_and_author<'a>(
465        &'a self,
466        params: QueryByKindAndAuthorParams,
467    ) -> Box<dyn Iterator<Item = &'a DatabaseEvent> + 'a> {
468        let QueryByKindAndAuthorParams {
469            kind,
470            author,
471            since,
472            until,
473        } = params;
474        match self.kind_author_index.get(&(kind, author)) {
475            Some(set) => Box::new(set.iter().filter(move |ev| {
476                if self.deleted_ids.contains(&ev.id) {
477                    return false;
478                }
479
480                if let Some(since) = since {
481                    if ev.created_at < since {
482                        return false;
483                    }
484                }
485
486                if let Some(until) = until {
487                    if ev.created_at > until {
488                        return false;
489                    }
490                }
491
492                true
493            })),
494            None => Box::new(iter::empty()),
495        }
496    }
497
498    /// Query by param. replaceable
499    fn internal_query_param_replaceable(
500        &self,
501        params: QueryByParamReplaceable,
502    ) -> Option<&DatabaseEvent> {
503        let QueryByParamReplaceable {
504            kind,
505            author,
506            identifier,
507            since,
508            until,
509        } = params;
510
511        if !kind.is_addressable() {
512            return None;
513        }
514
515        let ev = self
516            .param_replaceable_index
517            .get(&(kind, author, identifier))?;
518
519        if self.deleted_ids.contains(&ev.id) {
520            return None;
521        }
522
523        if let Some(since) = since {
524            if ev.created_at < since {
525                return None;
526            }
527        }
528
529        if let Some(until) = until {
530            if ev.created_at > until {
531                return None;
532            }
533        }
534
535        Some(ev)
536    }
537
538    /// Generic query
539    #[inline]
540    fn internal_generic_query(&self, filter: Filter) -> impl Iterator<Item = &DatabaseEvent> {
541        self.events.iter().filter(move |event| {
542            !self.deleted_ids.contains(&event.id)
543                && filter.match_event(event, MatchEventOptions::new())
544        })
545    }
546
547    fn internal_query(&self, filter: Filter) -> InternalQueryResult {
548        if filter.is_empty() {
549            return InternalQueryResult::All;
550        }
551
552        if let (Some(since), Some(until)) = (filter.since, filter.until) {
553            if since > until {
554                return InternalQueryResult::Set(BTreeSet::new());
555            }
556        }
557
558        let mut matching_ids: BTreeSet<&DatabaseEvent> = BTreeSet::new();
559        let limit: Option<usize> = filter.limit;
560
561        let evs: Box<dyn Iterator<Item = &DatabaseEvent>> = match QueryPattern::from(filter) {
562            QueryPattern::Author(params) => self.internal_query_by_author(params),
563            QueryPattern::KindAuthor(params) => self.internal_query_by_kind_and_author(params),
564            QueryPattern::ParamReplaceable(params) => {
565                match self.internal_query_param_replaceable(params) {
566                    Some(ev) => Box::new(iter::once(ev)),
567                    None => Box::new(iter::empty()),
568                }
569            }
570            QueryPattern::Generic(filter) => Box::new(self.internal_generic_query(*filter)),
571        };
572
573        if let Some(limit) = limit {
574            matching_ids.extend(evs.take(limit))
575        } else {
576            matching_ids.extend(evs)
577        }
578
579        InternalQueryResult::Set(matching_ids)
580    }
581
582    #[inline]
583    pub fn event_by_id(&self, id: &EventId) -> Option<&Event> {
584        self.ids.get(id).map(|e| e.deref())
585    }
586
587    #[inline]
588    pub fn has_event(&self, id: &EventId) -> bool {
589        self.ids.contains_key(id)
590    }
591
592    /// Query
593    pub fn query<'a>(&'a self, filter: Filter) -> Box<dyn Iterator<Item = &'a Event> + 'a> {
594        match self.internal_query(filter) {
595            InternalQueryResult::All => Box::new(self.events.iter().map(|ev| ev.as_ref())),
596            InternalQueryResult::Set(set) => Box::new(set.into_iter().map(|ev| ev.as_ref())),
597        }
598    }
599
600    /// Count events
601    pub fn count(&self, filter: Filter) -> usize {
602        match self.internal_query(filter) {
603            InternalQueryResult::All => self.events.len(),
604            InternalQueryResult::Set(set) => set.len(),
605        }
606    }
607
608    pub fn negentropy_items(&self, filter: Filter) -> Vec<(EventId, Timestamp)> {
609        match self.internal_query(filter) {
610            InternalQueryResult::All => self
611                .events
612                .iter()
613                .map(|ev| (ev.id, ev.created_at))
614                .collect(),
615            InternalQueryResult::Set(set) => {
616                set.into_iter().map(|ev| (ev.id, ev.created_at)).collect()
617            }
618        }
619    }
620
621    /// Check if an event with [`EventId`] has been deleted
622    pub fn has_event_id_been_deleted(&self, event_id: &EventId) -> bool {
623        self.deleted_ids.contains(event_id)
624    }
625
626    /// Check if event with [`Coordinate`] has been deleted before [`Timestamp`]
627    pub fn has_coordinate_been_deleted(
628        &self,
629        coordinate: &Coordinate,
630        timestamp: &Timestamp,
631    ) -> bool {
632        if let Some(t) = self.deleted_coordinates.get(coordinate) {
633            t >= timestamp
634        } else {
635            false
636        }
637    }
638
639    pub fn delete(&mut self, filter: Filter) -> Option<HashSet<EventId>> {
640        match self.internal_query(filter) {
641            InternalQueryResult::All => {
642                self.clear();
643                None
644            }
645            InternalQueryResult::Set(set) => {
646                let ids: HashSet<EventId> = set.into_iter().map(|ev| ev.id).collect();
647                self.discard_events(&ids);
648                Some(ids)
649            }
650        }
651    }
652
653    pub fn clear(&mut self) {
654        // Get current capacity
655        let capacity: Capacity = self.events.capacity();
656
657        // Reset helper to default
658        *self = Self::default();
659
660        // Change capacity
661        self.events.change_capacity(capacity);
662    }
663}
664
665/// Database helper transaction
666pub struct QueryTransaction {
667    guard: OwnedRwLockReadGuard<InternalDatabaseHelper>,
668}
669
670/// Database Indexes
671#[derive(Debug, Clone, Default)]
672pub struct DatabaseHelper {
673    inner: Arc<RwLock<InternalDatabaseHelper>>,
674}
675
676impl DatabaseHelper {
677    /// Unbounded database helper
678    #[inline]
679    pub fn unbounded() -> Self {
680        Self::default()
681    }
682
683    /// Bounded database helper
684    #[inline]
685    pub fn bounded(max: usize) -> Self {
686        Self {
687            inner: Arc::new(RwLock::new(InternalDatabaseHelper::bounded(max))),
688        }
689    }
690
691    /// Query transaction
692    #[inline]
693    pub async fn qtxn(&self) -> QueryTransaction {
694        QueryTransaction {
695            guard: self.inner.clone().read_owned().await,
696        }
697    }
698
699    /// Bulk index
700    pub async fn bulk_load(&self, events: BTreeSet<Event>) -> HashSet<EventId> {
701        let mut inner = self.inner.write().await;
702        inner.bulk_load(events)
703    }
704
705    /// Bulk import
706    ///
707    /// Take a set of [Event], index them and return **only** the ones that must be stored into the database
708    pub async fn bulk_import(&self, events: BTreeSet<Event>) -> BTreeSet<Event> {
709        let mut inner = self.inner.write().await;
710        inner.bulk_import(events).collect()
711    }
712
713    /// Index [`Event`]
714    ///
715    /// **This method assumes that [`Event`] was already verified**
716    pub async fn index_event(&self, event: &Event) -> DatabaseEventResult {
717        let mut inner = self.inner.write().await;
718        inner.index_event(event)
719    }
720
721    /// Get [Event] by ID
722    pub async fn event_by_id(&self, id: &EventId) -> Option<Event> {
723        let inner = self.inner.read().await;
724        inner.event_by_id(id).cloned()
725    }
726
727    /// Check if event exists
728    pub async fn has_event(&self, id: &EventId) -> bool {
729        let inner = self.inner.read().await;
730        inner.has_event(id)
731    }
732
733    /// Query
734    pub async fn query(&self, filter: Filter) -> Events {
735        let inner = self.inner.read().await;
736        let mut events = Events::new(&filter);
737        events.extend(inner.query(filter).cloned());
738        events
739    }
740
741    /// Query
742    pub fn fast_query<'a>(
743        &self,
744        txn: &'a QueryTransaction,
745        filter: Filter,
746    ) -> Box<dyn Iterator<Item = &'a Event> + 'a> {
747        txn.guard.query(filter)
748    }
749
750    /// Count events
751    pub async fn count(&self, filter: Filter) -> usize {
752        let inner = self.inner.read().await;
753        inner.count(filter)
754    }
755
756    /// Get negentropy items
757    pub async fn negentropy_items(&self, filter: Filter) -> Vec<(EventId, Timestamp)> {
758        let inner = self.inner.read().await;
759        inner.negentropy_items(filter)
760    }
761
762    /// Check if an event with [`EventId`] has been deleted
763    pub async fn has_event_id_been_deleted(&self, event_id: &EventId) -> bool {
764        let inner = self.inner.read().await;
765        inner.has_event_id_been_deleted(event_id)
766    }
767
768    /// Check if event with [`Coordinate`] has been deleted before [`Timestamp`]
769    pub async fn has_coordinate_been_deleted<'a>(
770        &self,
771        coordinate: &'a CoordinateBorrow<'a>,
772        timestamp: &Timestamp,
773    ) -> bool {
774        let inner = self.inner.read().await;
775        inner.has_coordinate_been_deleted(&coordinate.into_owned(), timestamp)
776    }
777
778    /// Delete all events that match [Filter]
779    ///
780    /// If return `None`, means that all events must be deleted from DB
781    pub async fn delete(&self, filter: Filter) -> Option<HashSet<EventId>> {
782        let mut inner = self.inner.write().await;
783        inner.delete(filter)
784    }
785
786    /// Clear helper
787    pub async fn clear(&self) {
788        let mut inner = self.inner.write().await;
789        inner.clear();
790    }
791}
792
793#[cfg(test)]
794mod tests {
795    use nostr::{FromBech32, JsonUtil, Keys, SecretKey};
796
797    use super::*;
798
799    const SECRET_KEY_A: &str = "nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99"; // aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4
800    const SECRET_KEY_B: &str = "nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85"; // 79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3
801
802    const EVENTS: [&str; 14] = [
803        r#"{"id":"b7b1fb52ad8461a03e949820ae29a9ea07e35bcd79c95c4b59b0254944f62805","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644581,"kind":1,"tags":[],"content":"Text note","sig":"ed73a8a4e7c26cd797a7b875c634d9ecb6958c57733305fed23b978109d0411d21b3e182cb67c8ad750884e30ca383b509382ae6187b36e76ee76e6a142c4284"}"#,
804        r#"{"id":"7296747d91c53f1d71778ef3e12d18b66d494a41f688ef244d518abf37c959b6","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644586,"kind":32121,"tags":[["d","id-1"]],"content":"Empty 1","sig":"8848989a8e808f7315e950f871b231c1dff7752048f8957d4a541881d2005506c30e85c7dd74dab022b3e01329c88e69c9d5d55d961759272a738d150b7dbefc"}"#,
805        r#"{"id":"ec6ea04ba483871062d79f78927df7979f67545b53f552e47626cb1105590442","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644591,"kind":32122,"tags":[["d","id-1"]],"content":"Empty 2","sig":"89946113a97484850fe35fefdb9120df847b305de1216dae566616fe453565e8707a4da7e68843b560fa22a932f81fc8db2b5a2acb4dcfd3caba9a91320aac92"}"#,
806        r#"{"id":"63b8b829aa31a2de870c3a713541658fcc0187be93af2032ec2ca039befd3f70","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644596,"kind":32122,"tags":[["d","id-2"]],"content":"","sig":"607b1a67bef57e48d17df4e145718d10b9df51831d1272c149f2ab5ad4993ae723f10a81be2403ae21b2793c8ed4c129e8b031e8b240c6c90c9e6d32f62d26ff"}"#,
807        r#"{"id":"6fe9119c7db13ae13e8ecfcdd2e5bf98e2940ba56a2ce0c3e8fba3d88cd8e69d","pubkey":"79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3","created_at":1704644601,"kind":32122,"tags":[["d","id-3"]],"content":"","sig":"d07146547a726fc9b4ec8d67bbbe690347d43dadfe5d9890a428626d38c617c52e6945f2b7144c4e0c51d1e2b0be020614a5cadc9c0256b2e28069b70d9fc26e"}"#,
808        r#"{"id":"a82f6ebfc709f4e7c7971e6bf738e30a3bc112cfdb21336054711e6779fd49ef","pubkey":"79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3","created_at":1704644606,"kind":32122,"tags":[["d","id-1"]],"content":"","sig":"96d3349b42ed637712b4d07f037457ab6e9180d58857df77eb5fa27ff1fd68445c72122ec53870831ada8a4d9a0b484435f80d3ff21a862238da7a723a0d073c"}"#,
809        r#"{"id":"8ab0cb1beceeb68f080ec11a3920b8cc491ecc7ec5250405e88691d733185832","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644611,"kind":32122,"tags":[["d","id-1"]],"content":"Test","sig":"49153b482d7110e2538eb48005f1149622247479b1c0057d902df931d5cea105869deeae908e4e3b903e3140632dc780b3f10344805eab77bb54fb79c4e4359d"}"#,
810        r#"{"id":"63dc49a8f3278a2de8dc0138939de56d392b8eb7a18c627e4d78789e2b0b09f2","pubkey":"79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3","created_at":1704644616,"kind":5,"tags":[["a","32122:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:"]],"content":"","sig":"977e54e5d57d1fbb83615d3a870037d9eb5182a679ca8357523bbf032580689cf481f76c88c7027034cfaf567ba9d9fe25fc8cd334139a0117ad5cf9fe325eef"}"#,
811        r#"{"id":"6975ace0f3d66967f330d4758fbbf45517d41130e2639b54ca5142f37757c9eb","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704644621,"kind":5,"tags":[["a","32122:aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4:id-2"]],"content":"","sig":"9bb09e4759899d86e447c3fa1be83905fe2eda74a5068a909965ac14fcdabaed64edaeb732154dab734ca41f2fc4d63687870e6f8e56e3d9e180e4a2dd6fb2d2"}"#,
812        r#"{"id":"33f5b4e6a38e107638c20f4536db35191d4b8651ba5a2cefec983b9ec2d65084","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704645586,"kind":0,"tags":[],"content":"{\"name\":\"Key A\"}","sig":"285d090f45a6adcae717b33771149f7840a8c27fb29025d63f1ab8d95614034a54e9f4f29cee9527c4c93321a7ebff287387b7a19ba8e6f764512a40e7120429"}"#,
813        r#"{"id":"90a761aec9b5b60b399a76826141f529db17466deac85696a17e4a243aa271f9","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704645606,"kind":0,"tags":[],"content":"{\"name\":\"key-a\",\"display_name\":\"Key A\",\"lud16\":\"keya@ln.address\"}","sig":"ec8f49d4c722b7ccae102d49befff08e62db775e5da43ef51b25c47dfdd6a09dc7519310a3a63cbdb6ec6b3250e6f19518eb47be604edeb598d16cdc071d3dbc"}"#,
814        r#"{"id":"a295422c636d3532875b75739e8dae3cdb4dd2679c6e4994c9a39c7ebf8bc620","pubkey":"79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3","created_at":1704646569,"kind":5,"tags":[["e","90a761aec9b5b60b399a76826141f529db17466deac85696a17e4a243aa271f9"]],"content":"","sig":"d4dc8368a4ad27eef63cacf667345aadd9617001537497108234fc1686d546c949cbb58e007a4d4b632c65ea135af4fbd7a089cc60ab89b6901f5c3fc6a47b29"}"#,
815        r#"{"id":"999e3e270100d7e1eaa98fcfab4a98274872c1f2dfdab024f32e42a5a12d5b5e","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1704646606,"kind":5,"tags":[["e","90a761aec9b5b60b399a76826141f529db17466deac85696a17e4a243aa271f9"]],"content":"","sig":"4f3a33fd52784cea7ca8428fd35d94d65049712e9aa11a70b1a16a1fcd761c7b7e27afac325728b1c00dfa11e33e78b2efd0430a7e4b28f4ede5b579b3f32614"}"#,
816        r#"{"id":"99a022e6d61c4e39c147d08a2be943b664e8030c0049325555ac1766429c2832","pubkey":"79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3","created_at":1705241093,"kind":30333,"tags":[["d","multi-id"],["p","aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4"]],"content":"Multi-tags","sig":"0abfb2b696a7ed7c9e8e3bf7743686190f3f1b3d4045b72833ab6187c254f7ed278d289d52dfac3de28be861c1471421d9b1bfb5877413cbc81c84f63207a826"}"#,
817    ];
818
819    const REPLACEABLE_EVENT_1: &str = r#"{"id":"f06d755821e56fe9e25373d6bd142979ebdca0063bb0f10a95a95baf41bb5419","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1707478309,"kind":0,"tags":[],"content":"{\"name\":\"Test 1\"}","sig":"a095d9cf4f26794e6421445c0d1c4ada8273ad79a9809aaa20c566fc8d679b57f09889121050853c47be9222106abad0215705a80723f002fd47616ff6ba7bb9"}"#;
820    const REPLACEABLE_EVENT_2: &str = r#"{"id":"e0899bedc802a836c331282eddf712600fea8e00123b541e25a81aa6a4669b4a","pubkey":"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4","created_at":1707478348,"kind":0,"tags":[],"content":"{\"name\":\"Test 2\"}","sig":"ca1192ac72530010a895b4d76943bf373696a6969911c486c835995122cd59a46988026e8c0ad8322bc3f5942ecd633fc903e93c0460c9a186243ab1f1597a9c"}"#;
821
822    #[tokio::test]
823    async fn test_database_indexes() {
824        // Keys
825        let keys_a = Keys::new(SecretKey::from_bech32(SECRET_KEY_A).unwrap());
826        let keys_b = Keys::new(SecretKey::from_bech32(SECRET_KEY_B).unwrap());
827
828        let indexes = DatabaseHelper::unbounded();
829
830        // Build indexes
831        let mut events: BTreeSet<Event> = BTreeSet::new();
832        for event in EVENTS.into_iter() {
833            let event = Event::from_json(event).unwrap();
834            events.insert(event);
835        }
836        indexes.bulk_load(events).await;
837
838        // Test expected output
839        let expected_output = vec![
840            Event::from_json(EVENTS[13]).unwrap(),
841            Event::from_json(EVENTS[12]).unwrap(),
842            // Event 11 is invalid deletion
843            // Event 10 deleted by event 12
844            // Event 9 replaced by event 10
845            Event::from_json(EVENTS[8]).unwrap(),
846            // Event 7 is invalid deletion
847            Event::from_json(EVENTS[6]).unwrap(),
848            Event::from_json(EVENTS[5]).unwrap(),
849            Event::from_json(EVENTS[4]).unwrap(),
850            // Event 3 deleted by Event 8
851            // Event 2 replaced by Event 6
852            Event::from_json(EVENTS[1]).unwrap(),
853            Event::from_json(EVENTS[0]).unwrap(),
854        ];
855        assert_eq!(indexes.query(Filter::new()).await.to_vec(), expected_output);
856        assert_eq!(indexes.count(Filter::new()).await, 8);
857
858        // Test get previously deleted replaceable event (check if was deleted by indexes)
859        assert!(indexes
860            .query(
861                Filter::new()
862                    .kind(Kind::Metadata)
863                    .author(keys_a.public_key())
864            )
865            .await
866            .is_empty());
867
868        // Test get previously deleted param. replaceable event (check if was deleted by indexes)
869        assert!(indexes
870            .query(
871                Filter::new()
872                    .kind(Kind::Custom(32122))
873                    .author(keys_a.public_key())
874                    .identifier("id-2")
875            )
876            .await
877            .is_empty());
878
879        // Test get param replaceable events WITHOUT using indexes (identifier not passed)
880        assert_eq!(
881            indexes
882                .query(
883                    Filter::new()
884                        .kind(Kind::Custom(32122))
885                        .author(keys_b.public_key())
886                )
887                .await
888                .to_vec(),
889            vec![
890                Event::from_json(EVENTS[5]).unwrap(),
891                Event::from_json(EVENTS[4]).unwrap(),
892            ]
893        );
894
895        // Test get param replaceable events using indexes
896        assert_eq!(
897            indexes
898                .query(
899                    Filter::new()
900                        .kind(Kind::Custom(32122))
901                        .author(keys_b.public_key())
902                        .identifier("id-3")
903                )
904                .await
905                .to_vec(),
906            vec![Event::from_json(EVENTS[4]).unwrap()]
907        );
908
909        assert_eq!(
910            indexes
911                .query(Filter::new().author(keys_a.public_key()))
912                .await
913                .to_vec(),
914            vec![
915                Event::from_json(EVENTS[12]).unwrap(),
916                Event::from_json(EVENTS[8]).unwrap(),
917                Event::from_json(EVENTS[6]).unwrap(),
918                Event::from_json(EVENTS[1]).unwrap(),
919                Event::from_json(EVENTS[0]).unwrap(),
920            ]
921        );
922
923        assert_eq!(
924            indexes
925                .query(
926                    Filter::new()
927                        .author(keys_a.public_key())
928                        .kinds([Kind::TextNote, Kind::Custom(32121)])
929                )
930                .await
931                .to_vec(),
932            vec![
933                Event::from_json(EVENTS[1]).unwrap(),
934                Event::from_json(EVENTS[0]).unwrap(),
935            ]
936        );
937
938        assert_eq!(
939            indexes
940                .query(
941                    Filter::new()
942                        .authors([keys_a.public_key(), keys_b.public_key()])
943                        .kinds([Kind::TextNote, Kind::Custom(32121)])
944                )
945                .await
946                .to_vec(),
947            vec![
948                Event::from_json(EVENTS[1]).unwrap(),
949                Event::from_json(EVENTS[0]).unwrap(),
950            ]
951        );
952
953        // Test get param replaceable events using identifier
954        assert_eq!(
955            indexes
956                .query(Filter::new().identifier("id-1"))
957                .await
958                .to_vec(),
959            vec![
960                Event::from_json(EVENTS[6]).unwrap(),
961                Event::from_json(EVENTS[5]).unwrap(),
962                Event::from_json(EVENTS[1]).unwrap(),
963            ]
964        );
965
966        // Test get param replaceable events with multiple tags using identifier
967        assert_eq!(
968            indexes
969                .query(Filter::new().identifier("multi-id"))
970                .await
971                .to_vec(),
972            vec![Event::from_json(EVENTS[13]).unwrap()]
973        );
974        // As above but by using kind and pubkey
975        assert_eq!(
976            indexes
977                .query(
978                    Filter::new()
979                        .pubkey(keys_a.public_key())
980                        .kind(Kind::Custom(30333))
981                        .limit(1)
982                )
983                .await
984                .to_vec(),
985            vec![Event::from_json(EVENTS[13]).unwrap()]
986        );
987
988        // Test add new replaceable event (metadata)
989        let first_ev_metadata = Event::from_json(REPLACEABLE_EVENT_1).unwrap();
990        let res = indexes.index_event(&first_ev_metadata).await;
991        assert!(res.status.is_success());
992        assert!(res.to_discard.is_empty());
993        assert_eq!(
994            indexes
995                .query(
996                    Filter::new()
997                        .kind(Kind::Metadata)
998                        .author(keys_a.public_key())
999                )
1000                .await
1001                .to_vec(),
1002            vec![first_ev_metadata.clone()]
1003        );
1004
1005        // Test add replace metadata
1006        let ev = Event::from_json(REPLACEABLE_EVENT_2).unwrap();
1007        let res = indexes.index_event(&ev).await;
1008        assert!(res.status.is_success());
1009        assert!(res.to_discard.contains(&first_ev_metadata.id));
1010        assert_eq!(
1011            indexes
1012                .query(
1013                    Filter::new()
1014                        .kind(Kind::Metadata)
1015                        .author(keys_a.public_key())
1016                )
1017                .await
1018                .to_vec(),
1019            vec![ev]
1020        );
1021    }
1022}