annis_web/client/
search.rs1use 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
26pub 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 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}