1use 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#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct DatabaseEventResult {
138 pub status: SaveEventStatus,
140 pub to_discard: HashSet<EventId>,
142}
143
144enum InternalQueryResult<'a> {
145 All,
146 Set(BTreeSet<&'a DatabaseEvent>),
147}
148
149#[derive(Debug, Clone, Default)]
151struct InternalDatabaseHelper {
152 events: BTreeCappedSet<DatabaseEvent>,
154 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 pub fn bulk_load(&mut self, events: BTreeSet<Event>) -> HashSet<EventId> {
177 let now: Timestamp = Timestamp::now();
178 events
179 .into_iter()
180 .rev() .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 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() .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 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 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 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 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 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 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 self.deleted_coordinates
292 .entry(coordinate.clone())
293 .and_modify(|t| {
294 if created_at > *t {
296 *t = created_at
297 }
298 })
299 .or_insert(created_at);
300
301 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 self.discard_events(&to_discard);
324
325 if status.is_success() {
327 let e: DatabaseEvent = Arc::new(event.clone()); 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 pub fn index_event(&mut self, event: &Event) -> DatabaseEventResult {
418 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 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 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 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 #[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 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 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 pub fn has_event_id_been_deleted(&self, event_id: &EventId) -> bool {
623 self.deleted_ids.contains(event_id)
624 }
625
626 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 let capacity: Capacity = self.events.capacity();
656
657 *self = Self::default();
659
660 self.events.change_capacity(capacity);
662 }
663}
664
665pub struct QueryTransaction {
667 guard: OwnedRwLockReadGuard<InternalDatabaseHelper>,
668}
669
670#[derive(Debug, Clone, Default)]
672pub struct DatabaseHelper {
673 inner: Arc<RwLock<InternalDatabaseHelper>>,
674}
675
676impl DatabaseHelper {
677 #[inline]
679 pub fn unbounded() -> Self {
680 Self::default()
681 }
682
683 #[inline]
685 pub fn bounded(max: usize) -> Self {
686 Self {
687 inner: Arc::new(RwLock::new(InternalDatabaseHelper::bounded(max))),
688 }
689 }
690
691 #[inline]
693 pub async fn qtxn(&self) -> QueryTransaction {
694 QueryTransaction {
695 guard: self.inner.clone().read_owned().await,
696 }
697 }
698
699 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 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 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 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 pub async fn has_event(&self, id: &EventId) -> bool {
729 let inner = self.inner.read().await;
730 inner.has_event(id)
731 }
732
733 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 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 pub async fn count(&self, filter: Filter) -> usize {
752 let inner = self.inner.read().await;
753 inner.count(filter)
754 }
755
756 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 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 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 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 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"; const SECRET_KEY_B: &str = "nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85"; 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 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 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 let expected_output = vec![
840 Event::from_json(EVENTS[13]).unwrap(),
841 Event::from_json(EVENTS[12]).unwrap(),
842 Event::from_json(EVENTS[8]).unwrap(),
846 Event::from_json(EVENTS[6]).unwrap(),
848 Event::from_json(EVENTS[5]).unwrap(),
849 Event::from_json(EVENTS[4]).unwrap(),
850 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 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 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 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 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 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 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 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 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 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}