Skip to main content

v_common/search/
common.rs

1use v_individual_model::onto::onto_index::OntoIndex;
2use crate::storage::async_storage::get_individual_from_db;
3use crate::storage::async_storage::AStorage;
4use crate::v_api::common_type::ResultCode;
5use futures::lock::Mutex;
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8use strum_macros::EnumString;
9
10#[derive(Serialize, Deserialize, Debug)]
11pub struct QueryResult {
12    pub result: Vec<String>,
13    pub count: i64,
14    pub estimated: i64,
15    pub processed: i64,
16    pub cursor: i64,
17    pub total_time: i64,
18    pub query_time: i64,
19    pub authorize_time: i64,
20    pub result_code: ResultCode,
21}
22
23impl Default for QueryResult {
24    fn default() -> Self {
25        QueryResult {
26            result: vec![],
27            count: 0,
28            estimated: 0,
29            processed: 0,
30            cursor: 0,
31            total_time: 0,
32            query_time: 0,
33            authorize_time: 0,
34            result_code: ResultCode::NotReady,
35        }
36    }
37}
38
39#[derive(Debug, PartialEq, EnumString)]
40pub enum ResultFormat {
41    #[strum(ascii_case_insensitive)]
42    Rows,
43    #[strum(ascii_case_insensitive)]
44    Cols,
45    #[strum(ascii_case_insensitive)]
46    Full,
47}
48
49#[derive(Debug, PartialEq, EnumString)]
50pub enum AuthorizationLevel {
51    #[strum(ascii_case_insensitive)]
52    Query,
53    #[strum(ascii_case_insensitive)]
54    Cell,
55    #[strum(ascii_case_insensitive, serialize = "row-column")]
56    RowColumn,
57}
58
59#[derive(Serialize, Deserialize, Debug)]
60pub struct FTQuery {
61    pub ticket: String,
62    pub user: String,
63    pub query: String,
64    pub sort: String,
65    pub databases: String,
66    pub reopen: bool,
67    pub top: i32,
68    pub limit: i32,
69    pub from: i32,
70}
71
72impl FTQuery {
73    pub fn new_with_user(user: &str, query: &str) -> FTQuery {
74        FTQuery {
75            ticket: "".to_owned(),
76            user: user.to_owned(),
77            query: query.to_owned(),
78            sort: "".to_owned(),
79            databases: "".to_owned(),
80            reopen: false,
81            top: 10000,
82            limit: 10000,
83            from: 0,
84        }
85    }
86
87    pub fn new_with_ticket(ticket: &str, query: &str) -> FTQuery {
88        FTQuery {
89            ticket: ticket.to_owned(),
90            user: "".to_owned(),
91            query: query.to_owned(),
92            sort: "".to_owned(),
93            databases: "".to_owned(),
94            reopen: false,
95            top: 10000,
96            limit: 10000,
97            from: 0,
98        }
99    }
100
101    pub fn as_string(&self) -> String {
102        let mut s = String::new();
103
104        s.push_str("[\"");
105        if self.ticket.is_empty() {
106            if !self.user.is_empty() {
107                s.push_str("\"UU=");
108                s.push_str(&self.user);
109            }
110        } else {
111            s.push_str(&self.ticket);
112        }
113
114        s.push_str("\",\"");
115        s.push_str(&self.query.replace('"', "\\\""));
116        s.push_str("\",\"");
117        s.push_str(&self.sort);
118        s.push_str("\",\"");
119        s.push_str(&self.databases);
120        s.push_str("\",");
121        s.push_str(&self.reopen.to_string());
122        s.push(',');
123        s.push_str(&self.top.to_string());
124        s.push(',');
125        s.push_str(&self.limit.to_string());
126        s.push(',');
127        s.push_str(&self.from.to_string());
128        s.push(']');
129
130        s
131    }
132}
133
134////////////////////////////////////////////////////////////////////////
135
136pub struct PrefixesCache {
137    pub full2short_r: evmap::ReadHandle<String, String>,
138    pub full2short_w: Arc<Mutex<evmap::WriteHandle<String, String>>>,
139    pub short2full_r: evmap::ReadHandle<String, String>,
140    pub short2full_w: Arc<Mutex<evmap::WriteHandle<String, String>>>,
141}
142
143pub fn split_full_prefix(v: &str) -> (&str, &str) {
144    let pos = if let Some(n) = v.rfind('/') {
145        n
146    } else {
147        v.rfind('#').unwrap_or_default()
148    };
149
150    v.split_at(pos + 1)
151}
152
153pub fn split_short_prefix(v: &str) -> Option<(&str, &str)> {
154    if let Some(pos) = v.rfind(':') {
155        let lr = v.split_at(pos);
156        if let Some(l) = lr.1.strip_prefix(':') {
157            return Some((lr.0, l));
158        }
159    }
160    None
161}
162
163pub fn get_short_prefix(full_prefix: &str, prefixes_cache: &PrefixesCache) -> String {
164    if let Some(v) = prefixes_cache.full2short_r.get(full_prefix) {
165        if let Some(t) = v.get_one() {
166            return t.to_string();
167        }
168    }
169
170    full_prefix.to_owned()
171}
172
173pub fn get_full_prefix(short_prefix: &str, prefixes_cache: &PrefixesCache) -> String {
174    if let Some(v) = prefixes_cache.short2full_r.get(short_prefix) {
175        if let Some(t) = v.get_one() {
176            return t.to_string();
177        }
178    }
179
180    short_prefix.to_owned()
181}
182
183pub async fn load_prefixes(storage: &AStorage, prefixes_cache: &PrefixesCache) {
184    let onto_index = OntoIndex::load();
185
186    let mut f2s = prefixes_cache.full2short_w.lock().await;
187    let mut s2f = prefixes_cache.short2full_w.lock().await;
188
189    for id in onto_index.data.keys() {
190        if let Ok((mut rindv, _res)) = get_individual_from_db(id, "", storage, None).await {
191            rindv.parse_all();
192
193            if rindv.any_exists("rdf:type", &["owl:Ontology"]) {
194                if let Some(full_url) = rindv.get_first_literal("v-s:fullUrl") {
195                    debug!("prefix : {} -> {}", rindv.get_id(), full_url);
196                    let short_prefix = rindv.get_id().trim_end_matches(':');
197
198                    f2s.insert(full_url.to_owned(), short_prefix.to_owned());
199                    s2f.insert(short_prefix.to_owned(), full_url.to_owned());
200                }
201            }
202        } else {
203            error!("failed to read individual {}", id);
204        }
205        f2s.refresh();
206        s2f.refresh();
207    }
208}
209
210use regex::Regex;
211
212lazy_static! {
213    static ref REG_URI: Regex = Regex::new(r"^[a-z][a-z0-9]*:([a-zA-Z0-9-_])*$").expect("Invalid regex pattern");
214}
215
216pub fn is_identifier(str: &str) -> bool {
217    REG_URI.is_match(str)
218}
219
220pub fn replace_word(text: &str, a: &str, b: &str) -> String {
221    let a_lower = a.to_lowercase();
222    let re = Regex::new(r"[\w']+").unwrap();
223    let mut replaced_text = String::new();
224    let mut last_end = 0;
225
226    for word_match in re.find_iter(text) {
227        let (start, end) = (word_match.start(), word_match.end());
228        let word = &text[start..end];
229        let word_lower = word.to_lowercase();
230
231        if word_lower == a_lower {
232            replaced_text.push_str(&text[last_end..start]);
233            replaced_text.push_str(b);
234        } else {
235            replaced_text.push_str(&text[last_end..end]);
236        }
237
238        last_end = end;
239    }
240
241    replaced_text.push_str(&text[last_end..]);
242
243    replaced_text
244}