annis_web/client/
search.rs

1use axum::http::StatusCode;
2use futures::TryStreamExt;
3use graphannis::corpusstorage::{QueryLanguage, ResultOrder};
4use serde::Serialize;
5use std::{io::ErrorKind, mem::size_of};
6use tokio::io::AsyncBufReadExt;
7use tokio_util::io::StreamReader;
8use tracing::error;
9use transient_btree_index::{BtreeConfig, BtreeIndex};
10
11use crate::{
12    errors::{AppError, BadRequestError},
13    state::{GlobalAppState, SessionArg},
14    Result,
15};
16
17#[derive(Serialize, Clone)]
18pub struct FindQuery {
19    pub query: String,
20    pub corpora: Vec<String>,
21    pub query_language: QueryLanguage,
22    pub limit: Option<u64>,
23    pub order: ResultOrder,
24}
25
26/// Find all matches for a given query
27pub async fn find(
28    session: &SessionArg,
29    query: &FindQuery,
30    state: &GlobalAppState,
31) -> Result<BtreeIndex<u64, Vec<String>>> {
32    let url = state.service_url.join("search/find")?;
33    let client = state.create_client(session)?;
34
35    let request = client
36        .request(reqwest::Method::POST, url.clone())
37        .json(&query)
38        .build()?;
39
40    let response = client.execute(request).await?;
41
42    if response.status().is_success() {
43        let response = response.bytes_stream();
44
45        // Each line is a match, go through the body of the response and collect the matches
46        let mut result = BtreeIndex::with_capacity(
47            BtreeConfig::default().fixed_key_size(size_of::<u64>()),
48            1024,
49        )?;
50        let mut lines = StreamReader::new(response.map_err(|e| -> std::io::Error {
51            error!("Could not get next matches for find query. {}", e);
52            ErrorKind::ConnectionAborted.into()
53        }))
54        .lines();
55        let mut i = 0;
56        while let Some(l) = lines.next_line().await? {
57            result.insert(i, graphannis::util::node_names_from_match(&l))?;
58            i += 1;
59        }
60        Ok(result)
61    } else if response.status() == StatusCode::BAD_REQUEST {
62        let original_error: BadRequestError = response.json().await?;
63        Err(AppError::BackendBadRequest(original_error))
64    } else {
65        Err(AppError::Backend {
66            status_code: response.status(),
67            url,
68        })
69    }
70}