Skip to content

Commit fb3e3f1

Browse files
committed
Upgrade discovery to use meilisearch
1 parent c96d88b commit fb3e3f1

File tree

5 files changed

+67
-168
lines changed

5 files changed

+67
-168
lines changed

Cargo.lock

Lines changed: 0 additions & 89 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/app/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,3 @@ prometheus = "0.13.4"
7575
time = "0.3.36"
7676
dashmap = "6.1.0"
7777
meilisearch-sdk = "0.28.0"
78-
clickhouse = { version = "0.13.1", features = ["rustls-tls"] }

packages/app/compose.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ services:
4242
ports:
4343
- 7700:7700
4444
environment:
45-
MEILISEARCH_NO_ANALYTICS: true
45+
MEILI_NO_ANALYTICS: true
46+
MEILI_MASTER_KEY: admin
4647
clickhouse:
4748
image: clickhouse
4849
ports:

packages/app/src/discovery/engine.rs

Lines changed: 56 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,49 @@
1+
use std::collections::HashMap;
2+
13
use axum::async_trait;
24
use enstate_shared::core::Profile;
35
use enstate_shared::discovery::Discovery;
6+
use ethers::providers::namehash;
7+
use serde::{Deserialize, Serialize};
48

59
pub struct DiscoveryEngine {
6-
client: clickhouse::Client,
10+
client: meilisearch_sdk::client::Client,
11+
}
12+
13+
#[derive(Debug, Serialize, Deserialize)]
14+
pub struct MeiliProfileDocument {
15+
name_hash: String,
16+
name: String,
17+
avatar: Option<String>,
18+
header: Option<String>,
19+
display: String,
20+
addresses: HashMap<String, String>,
21+
fresh: i64,
22+
}
23+
24+
impl From<&Profile> for MeiliProfileDocument {
25+
fn from(profile: &Profile) -> Self {
26+
Self {
27+
name_hash: format!("{:x}", namehash(&profile.name)),
28+
name: profile.name.clone(),
29+
avatar: profile.avatar.clone(),
30+
header: profile.header.clone(),
31+
display: profile.display.clone(),
32+
addresses: profile.chains.iter().map(|(chain, address)| (chain.to_string(), address.to_string())).collect(),
33+
fresh: profile.fresh,
34+
}
35+
}
736
}
837

938
impl DiscoveryEngine {
10-
pub fn new(url: &str, user: &str, password: &str) -> Self {
39+
pub fn new(url: &str, key: Option<&str>) -> Self {
1140
Self {
12-
client: clickhouse::Client::default().with_url(url).with_user(user).with_password(password),
41+
client: meilisearch_sdk::client::Client::new(url, key).unwrap(),
1342
}
1443
}
1544

1645
pub async fn create_table_if_not_exists(&self) -> Result<(), ()> {
17-
// Create the profiles table
18-
let profiles_table_query = self.client.query("CREATE TABLE IF NOT EXISTS enstate.profiles (name String, address String, avatar String, header String, display String, contenthash String, resolver String, ccip_urls Array(String), errors Array(String), fresh Int64) ENGINE = ReplacingMergeTree(fresh) ORDER BY name");
19-
20-
match profiles_table_query.execute().await {
21-
Ok(_) => {},
22-
Err(e) => {
23-
tracing::error!("Error creating profiles table: {}", e);
24-
return Err(());
25-
}
26-
};
2746

28-
// Create the stats aggregation table
29-
let stats_table_query = self.client.query("
30-
CREATE TABLE IF NOT EXISTS enstate.profile_stats_agg
31-
(
32-
day Date,
33-
total_profiles UInt64,
34-
profiles_with_avatar UInt64,
35-
profiles_with_header UInt64,
36-
profiles_with_display UInt64,
37-
profiles_with_contenthash UInt64
38-
)
39-
ENGINE = SummingMergeTree
40-
PARTITION BY day
41-
ORDER BY day
42-
");
43-
44-
match stats_table_query.execute().await {
45-
Ok(_) => {},
46-
Err(e) => {
47-
tracing::error!("Error creating profile_stats_agg table: {}", e);
48-
return Err(());
49-
}
50-
};
51-
52-
// Create the materialized view
53-
let materialized_view_query = self.client.query("
54-
CREATE MATERIALIZED VIEW IF NOT EXISTS enstate.mv_profile_stats_agg
55-
TO enstate.profile_stats_agg
56-
AS
57-
SELECT
58-
toDate(fromUnixTimestamp64Milli(fresh)) AS day,
59-
count() AS total_profiles,
60-
countIf(avatar != '') AS profiles_with_avatar,
61-
countIf(header != '') AS profiles_with_header,
62-
countIf(display != '') AS profiles_with_display,
63-
countIf(contenthash != '') AS profiles_with_contenthash
64-
FROM enstate.profiles
65-
GROUP BY day
66-
");
67-
68-
match materialized_view_query.execute().await {
69-
Ok(_) => {},
70-
Err(e) => {
71-
tracing::error!("Error creating materialized view: {}", e);
72-
return Err(());
73-
}
74-
};
7547

7648
Ok(())
7749
}
@@ -88,21 +60,31 @@ impl Discovery for DiscoveryEngine {
8860
.map(|(key, val)| format!("{}: {}", key, val))
8961
.collect();
9062

91-
let query = self.client
92-
.query("INSERT INTO enstate.profiles (name, address, avatar, header, display, contenthash, resolver, ccip_urls, errors, fresh) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
93-
.bind(profile.name.clone())
94-
.bind(profile.address.as_ref().map(|x| x.to_string()).unwrap_or_default())
95-
.bind(profile.avatar.as_deref().unwrap_or_default())
96-
.bind(profile.header.as_deref().unwrap_or_default())
97-
.bind(profile.display.clone())
98-
.bind(profile.contenthash.as_deref().unwrap_or_default())
99-
.bind(profile.resolver.clone())
100-
.bind(ccip_urls)
101-
.bind(errors)
102-
.bind(profile.fresh);
63+
// let query = self.client
64+
// .query("INSERT INTO enstate.profiles (name, address, avatar, header, display, contenthash, resolver, ccip_urls, errors, fresh) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
65+
// .bind(profile.name.clone())
66+
// .bind(profile.address.as_ref().map(|x| x.to_string()).unwrap_or_default())
67+
// .bind(profile.avatar.as_deref().unwrap_or_default())
68+
// .bind(profile.header.as_deref().unwrap_or_default())
69+
// .bind(profile.display.clone())
70+
// .bind(profile.contenthash.as_deref().unwrap_or_default())
71+
// .bind(profile.resolver.clone())
72+
// .bind(ccip_urls)
73+
// .bind(errors)
74+
// .bind(profile.fresh);
75+
76+
let document = MeiliProfileDocument::from(profile);
77+
78+
let documents = vec![document];
79+
80+
let x = self.client.index("profiles");
81+
let x = x.add_documents(&documents, Some("name_hash")).await;
10382

104-
match query.execute().await {
105-
Ok(_) => Ok(()),
83+
match x {
84+
Ok(result) => {
85+
tracing::info!("Inserted profile: {:?}", result);
86+
Ok(())
87+
},
10688
Err(e) => {
10789
tracing::error!("Error inserting profile: {}", e);
10890
Err(())

packages/app/src/state.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::sync::Arc;
33

44
use enstate_shared::cache::{CacheLayer, PassthroughCacheLayer};
55
use enstate_shared::core::ENSService;
6+
use enstate_shared::discovery::Discovery;
67
use enstate_shared::models::{
78
multicoin::cointype::{coins::CoinType, Coins},
89
records::Records,
@@ -90,14 +91,19 @@ impl AppState {
9091
let cache_ttl =
9192
env::var("PROFILE_CACHE_TTL").map_or(Some(600), |cache_ttl| cache_ttl.parse().ok());
9293

93-
let discovery_engine = DiscoveryEngine::new("http://localhost:8123", "admin", "admin");
94+
let meilisearch_url = env::var("MEILI_ENDPOINT").ok();
95+
let meilisearch_key = env::var("MEILI_KEY").ok();
9496

95-
discovery_engine.create_table_if_not_exists().await;
97+
let discovery = meilisearch_url.map(|url| {
98+
let engine = DiscoveryEngine::new(&url, meilisearch_key.as_deref());
99+
100+
Box::new(engine) as Box<dyn Discovery>
101+
});
96102

97103
Self {
98104
rate_limiter: RateLimiter::new(),
99105
service: ENSService {
100-
discovery: Some(Box::new(discovery_engine)),
106+
discovery,
101107
cache,
102108
rpc: Box::new(provider),
103109
opensea_api_key,

0 commit comments

Comments
 (0)