hotpot_db/
lib.rs

1use rusqlite::{params, Connection, NO_PARAMS};
2use serde::Serialize;
3use serde_json::json;
4use std::any::type_name;
5use std::collections::HashMap;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8#[derive(Debug)]
9pub struct HotPot {
10    pub conn: Connection,
11    pub collections: HashMap<String, Collection>,
12}
13
14#[derive(Debug, Clone)]
15pub struct Collection {
16    pub name: String,
17}
18
19#[derive(Debug)]
20pub struct NewEntry {
21    time_created: i64,
22    data: String,
23}
24
25#[derive(Debug, Clone)]
26pub struct Entry {
27    pub id: i64,
28    pub time_created: i64,
29    pub data: String,
30}
31
32#[derive(Debug)]
33pub enum Error {
34    General,
35}
36
37#[derive(Debug, Clone)]
38pub enum QueryKind {
39    Contains,
40    Object,
41}
42
43#[derive(Debug, Clone)]
44pub struct Query {
45    pub query_type: QueryKind,
46    pub collection: String,
47    pub comparison: String,
48    pub key: Option<String>,
49    pub string_value: Option<String>,
50    pub bool_value: Option<bool>,
51    pub float_value: Option<f32>,
52    pub int_value: Option<i32>,
53}
54
55#[derive(Debug)]
56pub struct QueryBuilder {
57    pub query_type: Option<QueryKind>,
58    pub collection: Option<String>,
59    pub comparison: Option<String>,
60    pub key: Option<String>,
61    pub string_value: Option<String>,
62    pub bool_value: Option<bool>,
63    pub float_value: Option<f32>,
64    pub int_value: Option<i32>,
65}
66
67#[derive(Debug)]
68pub struct Index {
69    pub collection: String,
70    pub key: String,
71    pub value: String,
72    // create index wr_country on wine_reviews (json_extract(data, '$.country'));
73    //
74    // sqlite> insert into a values (1, '{"hello":"world"}');
75    // sqlite> create index idx_a on a (json_extract(j, '$.hello'));
76    // sqlite> explain query plan select * from a where json_extract(j, '$.hello') = 'world';
77    // QUERY PLAN
78    // `--SEARCH TABLE a USING INDEX idx_a (<expr>=?)
79    // sqlite> explain query plan select * from a where json_extract(j, '$.foo') = 'world';
80    // QUERY PLAN
81    // `--SCAN TABLE a
82}
83
84#[derive(Debug)]
85pub struct IndexBuilder {
86    pub collection: Option<String>,
87    pub key: Option<String>,
88    pub value: Option<String>,
89}
90
91fn get_ms_time() -> i64 {
92    let start = SystemTime::now();
93    let since_the_epoch = start
94        .duration_since(UNIX_EPOCH)
95        .expect("Time went backwards");
96    since_the_epoch.as_millis() as i64
97}
98
99fn type_of<T>(_: T) -> &'static str {
100    type_name::<T>()
101}
102
103impl QueryBuilder {
104    pub fn new() -> QueryBuilder {
105        QueryBuilder {
106            query_type: None,
107            collection: None,
108            comparison: None,
109            key: None,
110            string_value: None,
111            bool_value: None,
112            float_value: None,
113            int_value: None,
114        }
115    }
116
117    pub fn kind(mut self, typ: QueryKind) -> QueryBuilder {
118        self.query_type = Some(typ);
119        self
120    }
121
122    pub fn collection(mut self, collection: &str) -> QueryBuilder {
123        self.collection = Some(String::from(collection));
124        self
125    }
126
127    pub fn comparison(mut self, comparison: &str) -> QueryBuilder {
128        self.comparison = Some(String::from(comparison));
129        self
130    }
131
132    pub fn string(mut self, value: &str) -> QueryBuilder {
133        self.string_value = Some(String::from(value));
134        self
135    }
136
137    pub fn bool(mut self, value: bool) -> QueryBuilder {
138        self.bool_value = Some(value.clone());
139        self
140    }
141
142    pub fn float(mut self, value: f32) -> QueryBuilder {
143        self.float_value = Some(value.clone());
144        self
145    }
146
147    pub fn int(mut self, value: i32) -> QueryBuilder {
148        self.int_value = Some(value.clone());
149        self
150    }
151
152    pub fn key(mut self, key: &str) -> QueryBuilder {
153        self.key = Some(String::from(key));
154        self
155    }
156
157    pub fn finish(self) -> Query {
158        Query {
159            query_type: self.query_type.unwrap(),
160            collection: self.collection.unwrap(),
161            comparison: self.comparison.unwrap(),
162            key: self.key,
163            string_value: self.string_value,
164            bool_value: self.bool_value,
165            float_value: self.float_value,
166            int_value: self.int_value,
167        }
168    }
169}
170
171impl HotPot {
172    pub fn new() -> HotPot {
173        let mut hp = HotPot {
174            conn: Connection::open("database.hpdb").unwrap(),
175            collections: HashMap::new(),
176        };
177        let collections = hp.list_collections();
178        match collections {
179            Ok(collection_names) => {
180                for name in collection_names {
181                    hp.collections.insert(
182                        String::from(name.clone()),
183                        Collection {
184                            name: String::from(name),
185                        },
186                    );
187                }
188            }
189            Err(_) => (),
190        }
191        hp
192    }
193
194    pub fn list_collections(&mut self) -> rusqlite::Result<Vec<String>> {
195        let mut stmt = self.conn.prepare(
196            "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'",
197        )?;
198        let rows = stmt.query_map(NO_PARAMS, |row| Ok(row.get(0)?))?;
199        let mut names = Vec::new();
200        for name_result in rows {
201            names.push(name_result?);
202        }
203        Ok(names)
204    }
205
206    pub fn create_collection(&mut self, name: &str) -> Result<bool, Error> {
207        &self
208            .conn
209            .execute(
210                &format!(
211                    "
212        CREATE TABLE {} (
213            id              INTEGER PRIMARY KEY,
214            time_created    INTEGER NOT NULL,
215            data            BLOB
216        )",
217                    name
218                ),
219                params![],
220            )
221            .map_err(|_| Error::General);
222        &self.collections.insert(
223            String::from(name),
224            Collection {
225                name: String::from(name),
226            },
227        );
228        Ok(true)
229    }
230
231    pub fn execute(&self, query: Query) -> Result<Vec<Entry>, Error> {
232        // pub fn execute(&self, query: Query) {
233        let mut results = Vec::new();
234        let c = &self
235            .collections
236            .get(&query.collection)
237            .expect("collection does not exist");
238        match query.query_type {
239            QueryKind::Contains => {
240                if !query.string_value.is_none() {
241                    results = c
242                        .query_arrays_contains::<String>(
243                            &self.conn,
244                            &query.collection,
245                            &query.comparison,
246                            &query.string_value.unwrap(),
247                        )
248                        .unwrap_or(Vec::new());
249                }
250                if !query.int_value.is_none() {
251                    results = c
252                        .query_arrays_contains::<i32>(
253                            &self.conn,
254                            &query.collection,
255                            &query.comparison,
256                            &query.int_value.unwrap(),
257                        )
258                        .unwrap_or(Vec::new());
259                }
260                if !query.bool_value.is_none() {
261                    results = c
262                        .query_arrays_contains::<bool>(
263                            &self.conn,
264                            &query.collection,
265                            &query.comparison,
266                            &query.bool_value.unwrap(),
267                        )
268                        .unwrap_or(Vec::new());
269                }
270                if !query.float_value.is_none() {
271                    results = c
272                        .query_arrays_contains::<f32>(
273                            &self.conn,
274                            &query.collection,
275                            &query.comparison,
276                            &query.float_value.unwrap(),
277                        )
278                        .unwrap_or(Vec::new());
279                }
280            }
281            QueryKind::Object => {
282                if !query.string_value.is_none() {
283                    results = c
284                        .query_object_with_key_value::<String>(
285                            &self.conn,
286                            &query.collection,
287                            &query.comparison,
288                            &query.key.clone().unwrap(),
289                            &query.string_value.unwrap(),
290                        )
291                        .unwrap_or(Vec::new());
292                }
293                if !query.int_value.is_none() {
294                    results = c
295                        .query_object_with_key_value::<i32>(
296                            &self.conn,
297                            &query.collection,
298                            &query.comparison,
299                            &query.key.clone().unwrap(),
300                            &query.int_value.unwrap(),
301                        )
302                        .unwrap_or(Vec::new());
303                }
304                if !query.bool_value.is_none() {
305                    results = c
306                        .query_object_with_key_value::<bool>(
307                            &self.conn,
308                            &query.collection,
309                            &query.comparison,
310                            &query.key.clone().unwrap(),
311                            &query.bool_value.unwrap(),
312                        )
313                        .unwrap_or(Vec::new());
314                }
315                if !query.float_value.is_none() {
316                    // println!("{:?}", &query.float_value);
317                    results = c
318                        .query_object_with_key_value::<f32>(
319                            &self.conn,
320                            &query.collection,
321                            &query.comparison,
322                            &query.key.clone().unwrap(),
323                            &query.float_value.unwrap(),
324                        )
325                        .unwrap_or(Vec::new());
326                }
327            }
328        }
329        Ok(results)
330    }
331
332    pub fn add_object_to_collection(&mut self, cname: &str, val: String) -> Result<bool, Error> {
333        let c = &self.collections.get(cname).unwrap();
334        let _did_insert = c.add_object(&self.conn, cname, val);
335        Ok(true)
336    }
337    pub fn add_index_to_collection(
338        &mut self,
339        collection_name: &str,
340        key: &str,
341        index_name: &str,
342    ) -> Result<bool, Error> {
343        let c = &self.collections.get(collection_name).unwrap();
344        let _did_insert = c.add_index(&self.conn, collection_name, key, index_name);
345        Ok(true)
346    }
347
348    pub fn insert<T: Serialize>(&mut self, cname: &str, svalue: &T) -> Result<bool, Error> {
349        // let json_to_store = serde_json::to_string(&person).unwrap();
350        let val: String = json!(svalue).to_string();
351
352        let c = &self.collections.get(cname).unwrap();
353        let _did_insert = c.add_object(&self.conn, cname, val);
354        Ok(true)
355    }
356}
357
358impl Collection {
359    pub fn add_object(
360        &self,
361        conn: &Connection,
362        cname: &str,
363        value: String,
364    ) -> rusqlite::Result<()> {
365        let me = NewEntry {
366            time_created: get_ms_time(),
367            data: value,
368        };
369        conn.execute(
370            &format!(
371                "INSERT INTO {} (time_created, data)
372                  VALUES (?1, ?2)",
373                cname
374            ),
375            params![me.time_created, me.data.to_string()],
376        )?;
377        Ok(())
378    }
379
380    pub fn add_index(
381        &self,
382        conn: &Connection,
383        collection_name: &str,
384        key: &str,
385        index_name: &str,
386    ) -> rusqlite::Result<()> {
387        let mut stmt = conn.prepare(&format!(
388            "CREATE INDEX {} ON {} (json_extract(data, '$.{}'))",
389            index_name, collection_name, key
390        ))?;
391        let res = stmt.execute(params![])?;
392        Ok(())
393    }
394    pub fn query_arrays_contains<T: std::fmt::Display>(
395        &self,
396        conn: &Connection,
397        cname: &str,
398        comparison: &str,
399        value: &T,
400    ) -> rusqlite::Result<Vec<Entry>> {
401        match type_of(value) {
402            "&alloc::string::String" => {
403                let mut stmt = conn.prepare(&format!(
404                    "SELECT * from {}, json_each(data) WHERE json_each.value {} '{}'",
405                    cname, comparison, value
406                ))?;
407
408                let person_iter = stmt.query_map(params![], |row| {
409                    Ok(Entry {
410                        id: row.get(0).unwrap(),
411                        time_created: row.get(1).unwrap(),
412                        data: row.get(2).unwrap(),
413                    })
414                })?;
415                let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
416                Ok(results)
417            }
418            _ => {
419                let mut stmt = conn.prepare(&format!(
420                    "SELECT * from {}, json_each(data) WHERE json_each.value {} {}",
421                    cname, comparison, value
422                ))?;
423
424                let person_iter = stmt.query_map(params![], |row| {
425                    Ok(Entry {
426                        id: row.get(0).unwrap(),
427                        time_created: row.get(1).unwrap(),
428                        data: row.get(2).unwrap(),
429                    })
430                })?;
431                let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
432                Ok(results)
433            }
434        }
435    }
436
437    pub fn query_object_with_key_value<T: std::fmt::Display>(
438        &self,
439        conn: &Connection,
440        cname: &str,
441        comparison: &str,
442        key: &str,
443        value: &T,
444    ) -> rusqlite::Result<Vec<Entry>> {
445        match type_of(value) {
446            "&alloc::string::String" => {
447                let query = format!(
448                    "SELECT * FROM {}, json_tree(data, '$.{}') WHERE json_tree.value {} '{}'",
449                    cname, key, comparison, value
450                );
451                // println!("{}", query);
452                let mut stmt = conn.prepare(&query)?;
453                let person_iter = stmt.query_map(params![], |row| {
454                    Ok(Entry {
455                        id: row.get(0).unwrap(),
456                        time_created: row.get(1).unwrap(),
457                        data: row.get(2).unwrap(),
458                    })
459                })?;
460                let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
461                Ok(results)
462            }
463            _ => {
464                let query = format!(
465                    "SELECT * FROM {}, json_tree(data, '$.{}') WHERE json_tree.value {} {}",
466                    cname, key, comparison, value
467                );
468                // println!("{}", query);
469                let mut stmt = conn.prepare(&query)?;
470                let person_iter = stmt.query_map(params![], |row| {
471                    Ok(Entry {
472                        id: row.get(0).unwrap(),
473                        time_created: row.get(1).unwrap(),
474                        data: row.get(2).unwrap(),
475                    })
476                })?;
477                let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
478                Ok(results)
479            }
480        }
481    }
482}