tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit 79486928acfe79455721c6ac02068b6b6ccaaad7
parent 3df6423d5da20734ef5c3049af5c81aa00c0ec2f
Author: triesap <tyson@radroots.org>
Date:   Fri,  5 Jun 2026 22:48:09 -0700

store-surreal: index event tags

Diffstat:
Mcrates/tangle_store_surreal/src/lib.rs | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 104 insertions(+), 1 deletion(-)

diff --git a/crates/tangle_store_surreal/src/lib.rs b/crates/tangle_store_surreal/src/lib.rs @@ -826,6 +826,62 @@ CREATE type::record('nostr_event', $event_id) CONTENT { response.take(0).map_err(SurrealStoreError::from) } + pub async fn index_event_tags(&self, event: &Event) -> Result<(), SurrealStoreError> { + self.db + .query("DELETE event_tag_index WHERE event_id = $event_id;") + .bind(("event_id", event.id().as_str())) + .await + .map_err(SurrealStoreError::from)? + .check() + .map_err(SurrealStoreError::from)?; + for (ordinal, tag) in event.unsigned().tags().iter().enumerate() { + let Some((name, value)) = tag.indexed_pair() else { + continue; + }; + self.db + .query( + r#" +CREATE event_tag_index CONTENT { + event_id: $event_id, + kind: $kind, + pubkey: $pubkey, + created_at: $created_at, + tag: $tag, + value: $value, + ordinal: $ordinal +}; +"#, + ) + .bind(("event_id", event.id().as_str())) + .bind(("kind", event.unsigned().kind().as_u32())) + .bind(("pubkey", event.unsigned().pubkey().as_str())) + .bind(("created_at", event.unsigned().created_at().as_u64())) + .bind(("tag", name)) + .bind(("value", value)) + .bind(("ordinal", ordinal as u64)) + .await + .map_err(SurrealStoreError::from)? + .check() + .map_err(SurrealStoreError::from)?; + } + Ok(()) + } + + pub async fn tag_index_rows( + &self, + event_id: &EventId, + ) -> Result<Vec<serde_json::Value>, SurrealStoreError> { + let mut response = self + .db + .query("SELECT * FROM event_tag_index WHERE event_id = $event_id ORDER BY ordinal ASC;") + .bind(("event_id", event_id.as_str())) + .await + .map_err(SurrealStoreError::from)? + .check() + .map_err(SurrealStoreError::from)?; + response.take(0).map_err(SurrealStoreError::from) + } + async fn applied_migration( &self, name: &str, @@ -939,7 +995,9 @@ mod tests { SurrealMigration, SurrealMigrationError, SurrealMigrationPlan, SurrealStore, base_migration_plan, migration_tracking_schema, }; - use tangle_protocol::UnixTimestamp; + use tangle_protocol::{ + Event, EventId, Kind, PublicKeyHex, SignatureHex, Tag, UnixTimestamp, UnsignedEvent, + }; use tangle_store::{StoreEventOutcome, StoredEvent}; use tangle_test_support::{build_fixture_event, valid_public_listing_spec}; @@ -1631,4 +1689,49 @@ mod tests { ); assert_eq!(row["tags"].as_array().expect("tags").len(), 10); } + + #[tokio::test] + async fn index_event_tags_persists_single_letter_tag_rows() { + let store = memory_store().await; + store + .apply_plan(&base_migration_plan()) + .await + .expect("apply plan"); + let event = Event::new( + EventId::new(&"c".repeat(EventId::HEX_LENGTH)).expect("id"), + UnsignedEvent::new( + PublicKeyHex::new(&"d".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), + UnixTimestamp::new(1_714_124_600), + Kind::new(1).expect("kind"), + vec![ + Tag::from_parts("e", &["target-event"]).expect("e"), + Tag::from_parts("A", &["upper-tag"]).expect("A"), + Tag::from_parts("topic", &["ignored"]).expect("topic"), + Tag::from_parts("p", &["target-pubkey"]).expect("p"), + ], + "tagged event", + ), + SignatureHex::new(&"e".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), + ); + + store.index_event_tags(&event).await.expect("index"); + store.index_event_tags(&event).await.expect("reindex"); + + let rows = store.tag_index_rows(event.id()).await.expect("rows"); + assert_eq!(rows.len(), 3); + assert_eq!(rows[0]["tag"], "e"); + assert_eq!(rows[0]["value"], "target-event"); + assert_eq!(rows[0]["ordinal"], 0_u64); + assert_eq!(rows[1]["tag"], "A"); + assert_eq!(rows[1]["value"], "upper-tag"); + assert_eq!(rows[1]["ordinal"], 1_u64); + assert_eq!(rows[2]["tag"], "p"); + assert_eq!(rows[2]["value"], "target-pubkey"); + assert_eq!(rows[2]["kind"], 1_u64); + assert_eq!(rows[2]["pubkey"], event.unsigned().pubkey().as_str()); + assert_eq!( + rows[2]["created_at"], + event.unsigned().created_at().as_u64() + ); + } }