expand query_entries for fuzzier queries; fix filename lookup

feat/vaults
Tomáš Mládek 2020-09-29 00:30:00 +02:00
parent 22846feb17
commit 2307c7bd57
4 changed files with 92 additions and 49 deletions

View File

@ -3,12 +3,13 @@ use crate::hash::{decode, hash, Hash, Hashable};
use crate::models;
use crate::util::LoggerSink;
use anyhow::{anyhow, Result};
use diesel::debug_query;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager};
use diesel::sqlite::{Sqlite, SqliteConnection};
use log::debug;
use log::{debug, trace};
use serde::export::Formatter;
use serde_json::json;
use serde_json::{json, Value};
use std::convert::TryFrom;
use std::fs;
use std::io::{Cursor, Write};
@ -186,20 +187,6 @@ pub fn file_set_valid<C: Connection<Backend = Sqlite>>(
.execute(connection)?)
}
pub fn lookup_by_filename<C: Connection<Backend = Sqlite>>(
connection: &C,
query: String,
) -> Result<Vec<models::File>> {
use crate::schema::files::dsl::*;
let matches = files
.filter(path.like(format!("%{}%", query)))
.filter(valid.eq(true))
.load::<models::File>(connection)?;
Ok(matches)
}
pub fn retrieve_object<C: Connection<Backend = Sqlite>>(
connection: &C,
object_address: Address,
@ -259,10 +246,17 @@ pub fn remove_object<C: Connection<Backend = Sqlite>>(
Ok(diesel::delete(matches).execute(connection)?)
}
pub enum QueryComponent<T> {
Exact(T),
ILike(T),
In(Vec<T>),
Any,
}
pub struct EntryQuery {
pub target: Option<Address>,
pub key: Option<String>,
pub value: Option<EntryValue>,
pub target: QueryComponent<Address>,
pub key: QueryComponent<String>,
pub value: QueryComponent<EntryValue>,
}
pub fn query_entries<C: Connection<Backend = Sqlite>>(
@ -273,17 +267,39 @@ pub fn query_entries<C: Connection<Backend = Sqlite>>(
let mut query = data.into_boxed();
if let Some(q_target) = entry_query.target {
query = query.filter(target.eq(q_target.encode()?));
}
query = match entry_query.target {
QueryComponent::Exact(q_target) => query.filter(target.eq(q_target.encode()?)),
QueryComponent::In(q_targets) => {
let targets: Result<Vec<_>, _> = q_targets.into_iter().map(|t| t.encode()).collect();
query.filter(target.eq_any(targets?))
}
QueryComponent::ILike(_) => return Err(anyhow!("Cannot query Address alike.")),
QueryComponent::Any => query,
};
if let Some(q_key) = entry_query.key {
query = query.filter(key.eq(q_key));
}
query = match entry_query.key {
QueryComponent::Exact(q_key) => query.filter(key.eq(q_key)),
QueryComponent::In(q_keys) => query.filter(key.eq_any(q_keys)),
QueryComponent::ILike(q_key) => query.filter(key.like(format!("%{}%", q_key))),
QueryComponent::Any => query,
};
if let Some(q_value) = entry_query.value {
query = query.filter(value.eq(q_value.to_str()?));
}
query = match entry_query.value {
QueryComponent::Exact(q_value) => query.filter(value.eq(q_value.to_str()?)),
QueryComponent::In(q_values) => {
let values: Result<Vec<_>, _> = q_values.into_iter().map(|v| v.to_str()).collect();
query.filter(value.eq_any(values?))
}
QueryComponent::ILike(EntryValue::Value(Value::String(q_value_string))) => {
query.filter(value.like(format!("%{}%", q_value_string)))
}
QueryComponent::ILike(_) => {
return Err(anyhow!("Only string Values can be queried alike."))
}
QueryComponent::Any => query,
};
trace!("Querying: {}", debug_query(&query));
let matches = query.load::<models::Entry>(connection)?;

View File

@ -1,7 +1,8 @@
use crate::addressing::Address;
use crate::database::{
bulk_retrieve_objects, file_set_valid, insert_entry, insert_file, query_entries,
retrieve_all_files, DbPool, Entry, EntryQuery, EntryValue, InnerEntry, DATABASE_FILENAME,
retrieve_all_files, DbPool, Entry, EntryQuery, EntryValue, InnerEntry, QueryComponent,
DATABASE_FILENAME,
};
use crate::hash::Hashable;
use crate::models;
@ -133,18 +134,18 @@ pub fn list_roots<C: Connection<Backend = Sqlite>>(connection: &C) -> Result<Vec
let all_directories: Vec<Entry> = query_entries(
connection,
EntryQuery {
target: None,
key: Some(DIR_KEY.to_string()),
value: None,
target: QueryComponent::Any,
key: QueryComponent::Exact(DIR_KEY.to_string()),
value: QueryComponent::Any,
},
)?;
let directories_with_parents: Vec<Address> = query_entries(
connection,
EntryQuery {
target: None,
key: Some(DIR_HAS_KEY.to_string()),
value: None,
target: QueryComponent::Any,
key: QueryComponent::Exact(DIR_HAS_KEY.to_string()),
value: QueryComponent::Any,
},
)?
.extract_addresses();
@ -171,9 +172,9 @@ pub async fn list_directory<C: Connection<Backend = Sqlite>>(
query_entries(
connection,
EntryQuery {
target: Some(last.clone()),
key: Some(DIR_HAS_KEY.to_string()),
value: None,
target: QueryComponent::Exact(last.clone()),
key: QueryComponent::Exact(DIR_HAS_KEY.to_string()),
value: QueryComponent::Any,
},
)?
.extract_addresses()
@ -201,9 +202,9 @@ pub fn fetch_or_create_dir<C: Connection<Backend = Sqlite>>(
let directories: Vec<Address> = query_entries(
connection,
EntryQuery {
target: None,
key: Some(String::from(DIR_KEY)),
value: Some(dir_value.clone()),
target: QueryComponent::Any,
key: QueryComponent::Exact(String::from(DIR_KEY)),
value: QueryComponent::Exact(dir_value.clone()),
},
)?
.into_iter()
@ -215,9 +216,9 @@ pub fn fetch_or_create_dir<C: Connection<Backend = Sqlite>>(
let parent_has: Vec<Address> = query_entries(
connection,
EntryQuery {
target: Some(address),
key: Some(String::from(DIR_HAS_KEY)),
value: None,
target: QueryComponent::Exact(address),
key: QueryComponent::Exact(String::from(DIR_HAS_KEY)),
value: QueryComponent::Any,
},
)?
.extract_addresses();
@ -452,6 +453,26 @@ fn _process_directory_entry<P: AsRef<Path>>(
})
}
pub fn lookup_by_filename<C: Connection<Backend = Sqlite>>(
connection: &C,
filename: String,
) -> Result<Vec<Entry>> {
let dir_value = EntryValue::Value(Value::String(filename));
let entity_addresses = query_entries(
connection,
EntryQuery {
target: QueryComponent::Any,
key: QueryComponent::In(vec![DIR_KEY.to_string(), FILENAME_KEY.to_string()]), // ?
value: QueryComponent::ILike(dir_value),
},
)?;
bulk_retrieve_objects(
connection,
entity_addresses.into_iter().map(|e| e.target).collect(),
)
}
#[cfg(test)]
mod tests {
use crate::filesystem::{UDirectory, UPath};

View File

@ -1,6 +1,6 @@
use crate::addressing::Address;
use crate::database::{lookup_by_filename, retrieve_file, retrieve_object, DbPool, Entry};
use crate::filesystem::{list_directory, UPath};
use crate::database::{retrieve_file, retrieve_object, DbPool, Entry};
use crate::filesystem::{list_directory, lookup_by_filename, UPath};
use crate::hash::{decode, encode};
use actix_files::NamedFile;
use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, ErrorNotFound};
@ -90,8 +90,14 @@ pub async fn get_lookup(
web::Query(info): web::Query<LookupRequest>,
) -> Result<HttpResponse, Error> {
let connection = state.db_pool.get().map_err(ErrorInternalServerError)?;
let response = lookup_by_filename(&connection, info.query);
Ok(HttpResponse::Ok().json(response.map_err(error::ErrorInternalServerError)?))
let response = lookup_by_filename(&connection, info.query).map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(
response
.iter()
.map(Entry::as_json)
.collect::<serde_json::Value>(),
))
}
#[post("/api/refresh")]

View File

@ -4,7 +4,7 @@
<sl-icon name="search" slot="prefix"></sl-icon>
</sl-input>
<ul>
<li v-for="result in results" :key="result.id">{{ result.path }}</li>
<li v-for="result in results" :key="result.identity">{{ result }}</li>
</ul>
</div>
</template>