expand query_entries for fuzzier queries; fix filename lookup
parent
22846feb17
commit
2307c7bd57
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue