alpha
Login
or
Join now
microcosm.blue
/
microcosm-rs
Star
0
Fork
3
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm
Star
0
Fork
3
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
first pass on a search endpoint
author
phil
date
1 year ago
(Jun 6, 2025, 9:42 PM -0400)
commit
ec33fd71
ec33fd7183db4a38ca158e88644917036f7462cc
parent
354e39ed
354e39ed1ba4fadbcc774fcbe7226bc5d03ff8bb
+67
3 changed files
Expand all
Collapse all
Unified
Split
ufos
src
server
mod.rs
storage.rs
storage_fjall.rs
+33
ufos/src/server/mod.rs
Reviewed
···
589
589
OkCors(CollectionTimeseriesResponse { range, series }).into()
590
590
}
591
591
592
592
+
#[derive(Debug, Deserialize, JsonSchema)]
593
593
+
struct SearchQuery {
594
594
+
/// Query
595
595
+
///
596
596
+
/// at least two alphanumeric (+hyphen) characters must be present
597
597
+
q: String,
598
598
+
}
599
599
+
#[derive(Debug, Serialize, JsonSchema)]
600
600
+
struct SearchResponse {
601
601
+
matches: Vec<NsidCount>,
602
602
+
}
603
603
+
/// Search lexicons
604
604
+
#[endpoint {
605
605
+
method = GET,
606
606
+
path = "/search"
607
607
+
}]
608
608
+
async fn search_collections(
609
609
+
ctx: RequestContext<Context>,
610
610
+
query: Query<SearchQuery>,
611
611
+
) -> OkCorsResponse<SearchResponse> {
612
612
+
let Context { storage, .. } = ctx.context();
613
613
+
let q = query.into_inner();
614
614
+
// TODO: query validation
615
615
+
// TODO: also handle multi-space stuff (ufos-app tries to on client)
616
616
+
let terms: Vec<String> = q.q.split(' ').map(Into::into).collect();
617
617
+
let matches = storage
618
618
+
.search_collections(terms)
619
619
+
.await
620
620
+
.map_err(|e| HttpError::for_internal_error(format!("oh ugh: {e:?}")))?;
621
621
+
OkCors(SearchResponse { matches }).into()
622
622
+
}
623
623
+
592
624
pub async fn serve(storage: impl StoreReader + 'static) -> Result<(), String> {
593
625
let log = ConfigLogging::StderrTerminal {
594
626
level: ConfigLoggingLevel::Info,
···
606
638
api.register(get_collections).unwrap();
607
639
api.register(get_prefix).unwrap();
608
640
api.register(get_timeseries).unwrap();
641
641
+
api.register(search_collections).unwrap();
609
642
610
643
let context = Context {
611
644
spec: Arc::new(
+2
ufos/src/storage.rs
Reviewed
···
137
137
limit: usize,
138
138
expand_each_collection: bool,
139
139
) -> StorageResult<Vec<UFOsRecord>>;
140
140
+
141
141
+
async fn search_collections(&self, terms: Vec<String>) -> StorageResult<Vec<NsidCount>>;
140
142
}
+32
ufos/src/storage_fjall.rs
Reviewed
···
982
982
}
983
983
Ok(merged)
984
984
}
985
985
+
986
986
+
fn search_collections(&self, terms: Vec<String>) -> StorageResult<Vec<NsidCount>> {
987
987
+
let start = AllTimeRollupKey::start()?;
988
988
+
let end = AllTimeRollupKey::end()?;
989
989
+
let mut matches = Vec::new();
990
990
+
let limit = 16; // TODO: param
991
991
+
for kv in self.rollups.range((start, end)) {
992
992
+
let (key_bytes, val_bytes) = kv?;
993
993
+
let key = db_complete::<AllTimeRollupKey>(&key_bytes)?;
994
994
+
let nsid = key.collection().as_str().to_string();
995
995
+
for term in &terms {
996
996
+
if nsid.contains(term) {
997
997
+
let counts = db_complete::<CountsValue>(&val_bytes)?;
998
998
+
matches.push(NsidCount {
999
999
+
nsid: nsid.clone(),
1000
1000
+
creates: counts.counts().creates,
1001
1001
+
dids_estimate: counts.dids().estimate() as u64,
1002
1002
+
});
1003
1003
+
break;
1004
1004
+
}
1005
1005
+
}
1006
1006
+
if matches.len() >= limit {
1007
1007
+
break;
1008
1008
+
}
1009
1009
+
}
1010
1010
+
// TODO: indicate incomplete results
1011
1011
+
Ok(matches)
1012
1012
+
}
985
1013
}
986
1014
987
1015
#[async_trait]
···
1061
1089
FjallReader::get_records_by_collections(&s, collections, limit, expand_each_collection)
1062
1090
})
1063
1091
.await?
1092
1092
+
}
1093
1093
+
async fn search_collections(&self, terms: Vec<String>) -> StorageResult<Vec<NsidCount>> {
1094
1094
+
let s = self.clone();
1095
1095
+
tokio::task::spawn_blocking(move || FjallReader::search_collections(&s, terms)).await?
1064
1096
}
1065
1097
}
1066
1098