1use 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#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct DatabaseEventResult {
137 pub status: SaveEventStatus,
139 pub to_discard: HashSet<EventId>,
141}
142
143enum InternalQueryResult<'a> {
144 All,
145 Set(BTreeSet<&'a DatabaseEvent>),
146}
147
148#[derive(Debug, Clone, Default)]
150struct InternalDatabaseHelper {
151 events: BTreeCappedSet<DatabaseEvent>,
153 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 pub fn bulk_load(&mut self, events: BTreeSet<Event>) -> HashSet<EventId> {
176 let now: Timestamp = Timestamp::now();
177 events
178 .into_iter()
179 .rev() .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 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() .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 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 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 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 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 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 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 self.deleted_coordinates
291 .entry(coordinate.clone())
292 .and_modify(|t| {
293 if created_at > *t {
295 *t = created_at
296 }
297 })
298 .or_insert(created_at);
299
300 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 self.discard_events(&to_discard);
323
324 if status.is_success() {
326 let e: DatabaseEvent = Arc::new(event.clone()); 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 pub fn index_event(&mut self, event: &Event) -> DatabaseEventResult {
417 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 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 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 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 #[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 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 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 pub fn has_event_id_been_deleted(&self, event_id: &EventId) -> bool {
621 self.deleted_ids.contains(event_id)
622 }
623
624 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 let capacity: Capacity = self.events.capacity();
654
655 *self = Self::default();
657
658 self.events.change_capacity(capacity);
660 }
661}
662
663pub struct QueryTransaction {
665 guard: OwnedRwLockReadGuard<InternalDatabaseHelper>,
666}
667
668#[derive(Debug, Clone, Default)]
670pub struct DatabaseHelper {
671 inner: Arc<RwLock<InternalDatabaseHelper>>,
672}
673
674impl DatabaseHelper {
675 #[inline]
677 pub fn unbounded() -> Self {
678 Self::default()
679 }
680
681 #[inline]
683 pub fn bounded(max: usize) -> Self {
684 Self {
685 inner: Arc::new(RwLock::new(InternalDatabaseHelper::bounded(max))),
686 }
687 }
688
689 #[inline]
691 pub async fn qtxn(&self) -> QueryTransaction {
692 QueryTransaction {
693 guard: self.inner.clone().read_owned().await,
694 }
695 }
696
697 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 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 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 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 pub async fn has_event(&self, id: &EventId) -> bool {
727 let inner = self.inner.read().await;
728 inner.has_event(id)
729 }
730
731 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 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 pub async fn count(&self, filter: Filter) -> usize {
750 let inner = self.inner.read().await;
751 inner.count(filter)
752 }
753
754 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 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 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 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 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"; const SECRET_KEY_B: &str = "nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85"; 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 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 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 let expected_output = vec![
838 Event::from_json(EVENTS[13]).unwrap(),
839 Event::from_json(EVENTS[12]).unwrap(),
840 Event::from_json(EVENTS[8]).unwrap(),
844 Event::from_json(EVENTS[6]).unwrap(),
846 Event::from_json(EVENTS[5]).unwrap(),
847 Event::from_json(EVENTS[4]).unwrap(),
848 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 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 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 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 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 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 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 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 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 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}