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