Skip to content

Commit

Permalink
Search for news based on ranking (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
RealHinome authored Aug 24, 2024
1 parent eb411d5 commit 97f9a6c
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 19 deletions.
10 changes: 8 additions & 2 deletions graphql/api/src/helpers/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use chrono::Utc;
use crawler::RssNews;
use search::Search;
use std::sync::Arc;
use tokio::sync::RwLock;
use url::Url;

#[derive(Debug, Default)]
Expand Down Expand Up @@ -42,7 +43,7 @@ fn find_media(from_url: &str) -> Media {
/// Handling incoming messages from MPSC channel.
pub async fn process_article(
article: RssNews,
searcher: &Arc<Search>,
searcher: &Arc<RwLock<Search>>,
ranker: &mut Ranker,
) -> Result<(), Box<dyn std::error::Error>> {
let summary = summary::get_summary(&article.content).await?;
Expand Down Expand Up @@ -94,7 +95,12 @@ pub async fn process_article(

ranker.add_entry(&news.title).await?;

searcher.add_entry(news).await.map_err(|e| e.into())
searcher
.read()
.await
.add_entry(news)
.await
.map_err(|e| e.into())
} else {
Err("No media found with this URL".into())
}
Expand Down
4 changes: 2 additions & 2 deletions graphql/api/src/helpers/ranking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ impl Ranker {
}

/// Get a single ranking from multiple sources.
pub async fn get_rank(&self) -> Result<Vec<String>, Error> {
pub async fn get_rank(&self, length: u32) -> Result<Vec<String>, Error> {
let mut result = Vec::new();

if let Some(squid) = &self.squid {
result.append(
&mut squid.write().await.leaderboard().await.map_err(
&mut squid.write().await.leaderboard(length).await.map_err(
|error| {
Error::new(
Unspecified,
Expand Down
15 changes: 13 additions & 2 deletions graphql/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use search::{Attributes, Search};
use std::{sync::Arc, time::Duration};
use strum::IntoEnumIterator;
use tokio::sync::mpsc;
use tokio::sync::RwLock;
use tracing::{debug, error, info, Level};
use tracing_subscriber::fmt;
use url::Url;
Expand Down Expand Up @@ -120,15 +121,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
crawler.crawl()?;

// Create meilisearch client.
let searcher = Arc::new(
let searcher = Arc::new(RwLock::new(
Search::new(
std::env::var("MEILISEARCH_URL")
.unwrap_or("http://localhost:7700".into()),
std::env::var("MEILISEARCH_URL").ok(),
)?
.index("news".into())
.await,
);
));

// Add country field as a filterable attribute.
searcher
.write()
.await
.index
.as_ref()
.unwrap()
.set_filterable_attributes(&["source.country"])
.await?;

// Create ranking platform.
let ranker = Ranker::new().await?;
Expand Down
3 changes: 2 additions & 1 deletion graphql/api/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ use mcq::QuestionQuery;
use news::NewsQuery;
use search::Search;
use std::sync::Arc;
use tokio::sync::RwLock;

pub type Date = DateTime<Utc>;

/// Define the context for the GraphQL schema.
#[derive(Clone, Debug)]
pub struct Context {
/// [`search::Search`] meilisearch client.
pub meilisearch: Arc<Search>,
pub meilisearch: Arc<RwLock<Search>>,
/// Custom [`Ranker`] supporting multiple sources.
pub ranker: Ranker,
}
Expand Down
59 changes: 50 additions & 9 deletions graphql/api/src/schema/news.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::models::{image::Image, news::News, source::Media};
use crate::schema::Date;
use crate::Context;
use juniper::{graphql_object, FieldResult};
use juniper::{graphql_object, graphql_value, FieldError, FieldResult};

/// Implement GraphQL on News structure.
#[graphql_object(context = Context, description = "A media article.")]
Expand Down Expand Up @@ -49,18 +49,59 @@ pub struct NewsQuery;
/// Implement the GraphQL object for the news query.
#[graphql_object(context = Context)]
impl NewsQuery {
/// Get the most relevant news of the day.
/// Get the *three* most relevant news of the day.
async fn get_top_news(
ctx: &Context,
#[graphql(description = "ISO 3166-1 alpha-2 country code.")]
country: String,
#[graphql(description = "Maximum number of articles sent.")] limit: i32,
) -> FieldResult<Vec<News>> {
let rank = ctx.ranker.get_rank(limit.try_into()?).await?;
let mut most_revelant_news = Vec::new();

for word in rank {
let news = ctx
.meilisearch
.write()
.await
.index
.as_ref()
.ok_or_else(|| FieldError::new("Index not found", graphql_value!({ "internal_error": "Meilisearch index is not selected" })))?
.search()
.with_query(&word)
.with_limit(1)
.with_filter(&format!("source.country={:?}", country))
.with_attributes_to_search_on(&["title"])
.execute::<News>()
.await?;

most_revelant_news.push(news.hits[0].result.clone())
}

Ok(most_revelant_news)
}

/// Get news of the day.
async fn get_news(
ctx: &Context,
#[graphql(description = "ISO 3166-1 alpha-2 country code.")]
_country: String,
#[graphql(description = "Maximum number of articles sent.")]
_limit: i32,
country: String,
#[graphql(description = "Maximum number of articles sent.")] limit: i32,
) -> FieldResult<Vec<News>> {
let _rank = ctx.ranker.get_rank().await?;
let news = ctx
.meilisearch
.write()
.await
.index
.as_ref()
.ok_or_else(|| FieldError::new("Index not found", graphql_value!({ "internal_error": "Meilisearch index is not selected" })))?
.search()
.with_query("*")
.with_limit(limit.try_into()?)
.with_filter(&format!("source.country={:?}", country))
.execute::<News>()
.await?;

Ok(vec![News {
..Default::default()
}])
Ok(news.hits.iter().map(|r| r.result.clone()).collect())
}
}
7 changes: 5 additions & 2 deletions graphql/rank/src/squid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ impl Squid {
}

/// Get top 10 most used words in news articles.
pub async fn leaderboard(&mut self) -> Result<Vec<String>, Status> {
let request = tonic::Request::new(LeaderboardRequest { length: 10 });
pub async fn leaderboard(
&mut self,
length: u32,
) -> Result<Vec<String>, Status> {
let request = tonic::Request::new(LeaderboardRequest { length });

let leaderboard = self
.client
Expand Down
3 changes: 2 additions & 1 deletion graphql/search/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ pub trait Attributes {
#[derive(Clone, Debug)]
pub struct Search {
client: Client,
index: Option<Index>,
/// Meilisearch index.
pub index: Option<Index>,
ttl_row: Option<String>,
}

Expand Down

0 comments on commit 97f9a6c

Please sign in to comment.