use axum::http::StatusCode;
use futures::TryStreamExt;
use graphannis::corpusstorage::{QueryLanguage, ResultOrder};
use serde::Serialize;
use std::{io::ErrorKind, mem::size_of};
use tokio::io::AsyncBufReadExt;
use tokio_util::io::StreamReader;
use tracing::error;
use transient_btree_index::{BtreeConfig, BtreeIndex};
use crate::{
errors::{AppError, BadRequestError},
state::{GlobalAppState, SessionArg},
Result,
};
#[derive(Serialize, Clone)]
pub struct FindQuery {
pub query: String,
pub corpora: Vec<String>,
pub query_language: QueryLanguage,
pub limit: Option<u64>,
pub order: ResultOrder,
}
pub async fn find(
session: &SessionArg,
query: &FindQuery,
state: &GlobalAppState,
) -> Result<BtreeIndex<u64, Vec<String>>> {
let url = state.service_url.join("search/find")?;
let client = state.create_client(session)?;
let request = client
.request(reqwest::Method::POST, url.clone())
.json(&query)
.build()?;
let response = client.execute(request).await?;
if response.status().is_success() {
let response = response.bytes_stream();
let mut result = BtreeIndex::with_capacity(
BtreeConfig::default().fixed_key_size(size_of::<u64>()),
1024,
)?;
let mut lines = StreamReader::new(response.map_err(|e| -> std::io::Error {
error!("Could not get next matches for find query. {}", e);
ErrorKind::ConnectionAborted.into()
}))
.lines();
let mut i = 0;
while let Some(l) = lines.next_line().await? {
result.insert(i, graphannis::util::node_names_from_match(&l))?;
i += 1;
}
Ok(result)
} else if response.status() == StatusCode::BAD_REQUEST {
let original_error: BadRequestError = response.json().await?;
Err(AppError::BackendBadRequest(original_error))
} else {
Err(AppError::Backend {
status_code: response.status(),
url,
})
}
}