Refactor & Connect to mercator_db

* Refactor the code, Update to the Service API REST JSON objects on disk.
 * Connect to the DB data model and engine to execute queries.
 * Remove the syntactic sugar around implicit `inside` operation on
   shapes, as it introduces issues.
This commit is contained in:
2019-08-27 11:39:04 +02:00
parent 6ed76e485e
commit 2b4eb67b9e
17 changed files with 766 additions and 1392 deletions

View File

@@ -1,57 +0,0 @@
use crate::executors::ResultSet;
use crate::symbols::*;
//use ironsea_index::*;
pub fn get_all(_space_id: &String) -> ResultSet {
//space::get_all(space_id)
Err("not yet implemented".to_string())
}
pub fn get_by_bounding_box(_space_id: &String, _bounding_box: &Vec<LiteralPosition>) -> ResultSet {
Err("not yet implemented".to_string())
}
pub fn get_by_position(_space_id: &String, _position: &LiteralPosition) -> ResultSet {
Err("not yet implemented".to_string())
}
pub fn get(_point: &Object, _fields: &Vec<Field>) -> Result<LiteralPosition, String> {
Err("not yet implemented".to_string())
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Object {
id: u32,
}
impl Object {
pub fn length(&self) -> f64 {
0.0
}
pub fn eval(&self, _predicate: &Predicate) -> bool {
false
}
}
impl Position {
pub fn eval(&self, point: &Object) -> LiteralPosition {
match self {
Position::StrCmpICase(_selector, _string) => LiteralPosition(vec![]), //TODO
Position::StrCmp(_selector, _string) => LiteralPosition(vec![]), //TODO
Position::Selector(selector) => selector.eval(point),
Position::Literal(position) => position.clone(),
}
}
}
impl LiteralSelector {
pub fn eval(&self, point: &Object) -> LiteralPosition {
let LiteralSelector(fields) = self;
match get(point, fields) {
Err(_) => LiteralPosition(vec![]),
Ok(p @ LiteralPosition(_)) => p,
}
}
}

View File

@@ -1,86 +0,0 @@
use std::collections::HashMap;
use crate::types::LiteralTypes;
//FIXME: Improve, as this should not be static, but coming from DB.
lazy_static! {
static ref UNIVERSE: String = "Universe".to_string();
static ref SPACES: HashMap<&'static str, Space> = {
let mut m = HashMap::new();
m.insert(
"Universe",
Space {
space_type: LiteralTypes::Vector(vec![
LiteralTypes::Float,
LiteralTypes::Float,
LiteralTypes::Float,
]),
bounding_box: vec![
vec![0, 0, 0],
vec![
std::u32::MAX as i64,
std::u32::MAX as i64,
std::u32::MAX as i64,
],
],
},
);
m
};
}
struct Space {
space_type: LiteralTypes,
bounding_box: Vec<Vec<i64>>,
}
impl Space {
pub fn max_volume(&self) -> f64 {
let mut volume = 1.0;
for max in &self.bounding_box[1] {
volume *= *max as f64;
}
volume
}
}
pub fn name() -> &'static String {
lazy_static! {
static ref UNIVERSE: String = "Universe".to_string();
};
&UNIVERSE
}
#[inline]
pub fn get_type(space_id: &String) -> &LiteralTypes {
lazy_static! {
static ref EMPTY_TYPE: LiteralTypes = LiteralTypes::Vector(Vec::new());
};
match SPACES.get(space_id.as_str()) {
None => &EMPTY_TYPE,
Some(space) => &space.space_type,
}
}
#[inline]
pub fn bounding_box(space_id: &String) -> &Vec<Vec<i64>> {
lazy_static! {
static ref EMPTY_BOX: Vec<Vec<i64>> = Vec::new();
};
match SPACES.get(space_id.as_str()) {
None => &EMPTY_BOX,
Some(space) => &space.bounding_box,
}
}
#[inline]
pub fn max_volume(space_id: &String) -> f64 {
match SPACES.get(space_id.as_str()) {
None => 0.0,
Some(space) => space.max_volume(),
}
}

17
src/evaluators.rs Normal file
View File

@@ -0,0 +1,17 @@
use mercator_db::SpaceObject;
use super::expressions::*;
use super::symbols::*;
impl Evaluator for Predicate {
fn eval(&self, object: &SpaceObject) -> bool {
match self {
Predicate::Not(predicate) => !predicate.eval(object),
Predicate::And(lh, rh) => lh.eval(object) && rh.eval(object),
Predicate::Or(lh, rh) => lh.eval(object) || rh.eval(object),
Predicate::Less(selector, literal) => &selector.value(object) < literal,
Predicate::Greater(selector, literal) => &selector.value(object) > literal,
Predicate::Equal(selector, literal) => &selector.value(object) == literal,
}
}
}

View File

@@ -1,314 +1,384 @@
use std::collections::HashSet;
use super::database;
use super::expression::*;
use mercator_db::space;
use mercator_db::Core;
use mercator_db::DataBase;
use mercator_db::SpaceObject;
use super::expressions::*;
use super::symbols::*;
pub type ResultSet = Result<Vec<database::Object>, String>;
impl From<&LiteralPosition> for space::Position {
fn from(literal: &LiteralPosition) -> Self {
let v: Vec<f64> = literal.into();
v.into()
}
}
impl From<&LiteralNumber> for space::Coordinate {
fn from(literal: &LiteralNumber) -> Self {
match literal {
LiteralNumber::Float(f) => (*f).into(),
LiteralNumber::Int(i) => (*i as u64).into(),
}
}
}
fn complement_helper(
db: &DataBase,
core: &Core,
space_id: &str,
inside: Vec<SpaceObject>,
output_space: Option<&str>,
threshold: f64,
) -> mercator_db::ResultSet {
let (low, high) = db.space(space_id)?.bounding_box();
match core.get_by_shape(
db,
&space::Shape::BoundingBox(low, high),
space_id,
output_space,
threshold,
) {
e @ Err(_) => e,
Ok(points) => Ok(points
.into_iter()
.filter(|o| !inside.contains(&o))
.collect::<Vec<_>>()),
}
}
fn distinct(
db: &DataBase,
core_id: &str,
bag: &Bag,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
match bag.execute(db, core_id, output_space, threshold_volume) {
e @ Err(_) => e,
Ok(mut v) => {
let set: HashSet<_> = v.drain(..).collect(); // dedup
v.extend(set.into_iter());
Ok(v)
}
}
}
fn filter(
db: &DataBase,
core_id: &str,
predicate: &Option<Predicate>,
bag: &Bag,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
match predicate {
None => bag.execute(db, core_id, output_space, threshold_volume),
Some(predicate) => match bag.execute(db, core_id, output_space, threshold_volume) {
e @ Err(_) => e,
Ok(results) => Ok(results
.into_iter()
.filter(|o| predicate.eval(&o))
.collect::<Vec<_>>()),
},
}
}
fn complement(
db: &DataBase,
core_id: &str,
core: &Core,
bag: &Bag,
output_space: Option<&str>,
threshold: f64,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
match bag.execute(db, core_id, output_space, threshold_volume) {
// FIXME: The complement of a set is computed within its definition space.
e @ Err(_) => e,
Ok(inside) => complement_helper(
db,
core,
mercator_db::space::Space::universe().name(),
inside,
output_space,
threshold,
),
}
}
fn intersection(
db: &DataBase,
core_id: &str,
rh: &Bag,
lh: &Bag,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
let l = lh.execute(db, core_id, output_space, threshold_volume);
if let Ok(l) = l {
let r = rh.execute(db, core_id, output_space, threshold_volume);
if let Ok(r) = r {
let mut v = vec![];
if rh.predict(db) < lh.predict(db) {
for o in r {
if l.contains(&o) {
v.push(o);
}
}
} else {
for o in l {
if r.contains(&o) {
v.push(o);
}
}
}
Ok(v)
} else {
r
}
} else {
l
}
}
fn union(
db: &DataBase,
core_id: &str,
rh: &Bag,
lh: &Bag,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
let l = lh.execute(db, core_id, output_space, threshold_volume);
if let Ok(mut l) = l {
let r = rh.execute(db, core_id, output_space, threshold_volume);
if let Ok(mut r) = r {
if rh.predict(db) < lh.predict(db) {
l.append(&mut r);
Ok(l)
} else {
r.append(&mut l);
Ok(r)
}
} else {
r
}
} else {
l
}
}
fn bag(
db: &DataBase,
core_id: &str,
bags: &[Bag],
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> mercator_db::ResultSet {
let mut v = vec![];
for bag in bags {
let b = bag.execute(db, core_id, output_space, threshold_volume);
match b {
e @ Err(_) => {
return e;
}
Ok(mut b) => {
v.append(&mut b);
}
}
}
Ok(v)
}
fn inside(
db: &DataBase,
core: &Core,
shape: &Shape,
output_space: Option<&str>,
threshold: f64,
) -> mercator_db::ResultSet {
let parameters = match shape {
Shape::Point(space_id, position) => {
let space = db.space(space_id)?;
let position: Vec<f64> = position.into();
let position = space.encode(&position)?;
Ok((space_id, space::Shape::Point(position)))
}
Shape::HyperRectangle(space_id, bounding_box) => {
if bounding_box.len() != 2 {
Err("The number of position is different from 2, which is unsupported.".to_string())
} else {
let space = db.space(space_id)?;
let low: Vec<f64> = (&bounding_box[0]).into();
let high: Vec<f64> = (&bounding_box[1]).into();
let low = space.encode(&low)?;
let high = space.encode(&high)?;
Ok((space_id, space::Shape::BoundingBox(low, high)))
}
}
Shape::HyperSphere(space_id, position, radius) => {
let space = db.space(space_id)?;
let position: Vec<f64> = position.into();
let position = space.encode(&position)?;
let mut r = vec![];
for _ in 0..position.dimensions() {
r.push(radius.into());
}
let radius = space.encode(&r)?[0];
//FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE?
Ok((space_id, space::Shape::HyperSphere(position, radius)))
}
Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()),
};
match parameters {
Ok((space_id, shape)) => core.get_by_shape(db, &shape, space_id, output_space, threshold),
Err(e) => Err(e),
}
}
fn outside(
db: &DataBase,
core: &Core,
shape: &Shape,
output_space: Option<&str>,
threshold: f64,
) -> mercator_db::ResultSet {
match shape {
Shape::Point(space_id, position) => {
let position: Vec<f64> = position.into();
match core.get_by_positions(db, &[position.into()], space_id, output_space, threshold) {
e @ Err(_) => e,
Ok(inside) => {
complement_helper(db, core, space_id, inside, output_space, threshold)
}
}
}
Shape::HyperRectangle(space_id, bounding_box) => {
// We need to adapt the bounding_box to ensure the
// surface will not hit as part of the inside set, so we
// compute the biggest bounding box contained within the
// given box.
// Smallest increment possible
let mut increment = Vec::with_capacity(bounding_box[0].dimensions());
for _ in 0..bounding_box[0].dimensions() {
increment.push(std::f64::EPSILON);
}
// Add it to the lower bound
let mut low: space::Position = (&bounding_box[0]).into();
low += increment.clone().into();
// Substract it from the upper bound
let mut high: space::Position = (&bounding_box[1]).into();
high -= increment.into();
match core.get_by_shape(
db,
&space::Shape::BoundingBox(low, high),
space_id,
output_space,
threshold,
) {
e @ Err(_) => e,
Ok(inside) => {
complement_helper(db, core, space_id, inside, output_space, threshold)
}
}
}
Shape::HyperSphere(space_id, center, radius) => {
// Smallest decrement possible, to exclude the surface
let mut radius: f64 = radius.into();
radius -= std::f64::EPSILON;
let center: space::Position = center.into();
match core.get_by_shape(
db,
&space::Shape::HyperSphere(center, radius.into()),
space_id,
output_space,
threshold,
) {
e @ Err(_) => e,
Ok(inside) => {
complement_helper(db, core, space_id, inside, output_space, threshold)
}
}
}
Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()),
}
}
impl Executor for Projection {
type ResultSet = self::ResultSet;
type ResultSet = mercator_db::ResultSet;
fn execute(&self) -> ResultSet {
fn execute(
&self,
db: &DataBase,
core_id: &str,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> Self::ResultSet {
match self {
Projection::Nifti(_, _, _bag) => Err("not yet implemented".to_string()),
Projection::JSON(_, _format, bag) => bag.execute(), // FIXME: Add projections here
Projection::Nifti(_, _, _bag) => Err("Proj-Nifti: not yet implemented".to_string()),
Projection::JSON(_, _format, bag) => {
bag.execute(db, core_id, output_space, threshold_volume)
// FIXME: Add projections here
}
}
}
}
impl Executor for Bag {
type ResultSet = self::ResultSet;
type ResultSet = mercator_db::ResultSet;
fn execute(&self) -> ResultSet {
fn get_bounding_box(
position: &LiteralPosition,
radius: &LiteralNumber,
) -> Result<Vec<LiteralPosition>, String> {
let LiteralPosition(position) = position;
let mut low = vec![];
let mut high = vec![];
match radius {
LiteralNumber::Int(r) => {
for x in position {
match x {
LiteralNumber::Int(x) => {
low.push(LiteralNumber::Int(x - r));
high.push(LiteralNumber::Int(x + r));
}
LiteralNumber::Float(x) => {
low.push(LiteralNumber::Float(x - (*r as f64)));
high.push(LiteralNumber::Float(x + (*r as f64)));
}
};
}
}
LiteralNumber::Float(r) => {
for x in position {
match x {
LiteralNumber::Int(_) => {
return Err(format!("The radius provided is a floating point value, which is incompatible with integer coordinates components: radius {:?}, coordinates {:?}", radius, position));
}
LiteralNumber::Float(x) => {
low.push(LiteralNumber::Float(x - r));
high.push(LiteralNumber::Float(x + r));
}
};
}
}
}
Ok(vec![LiteralPosition(low), LiteralPosition(high)])
fn execute(
&self,
db: &DataBase,
core_id: &str,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> Self::ResultSet {
let threshold = match threshold_volume {
None => 0.0,
Some(v) => v,
};
let core = db.core(core_id)?;
match self {
Bag::Distinct(bag) => match bag.execute() {
e @ Err(_) => e,
Ok(mut v) => {
let set: HashSet<_> = v.drain(..).collect(); // dedup
v.extend(set.into_iter());
Ok(v)
}
},
Bag::Filter(predicate, bag) => match predicate {
None => bag.execute(),
Some(predicate) => match bag.execute() {
e @ Err(_) => e,
Ok(source) => {
let mut filtered = Vec::new();
for point in source {
if point.eval(predicate) {
filtered.push(point);
}
}
Ok(filtered)
}
},
},
Bag::Complement(bag) => match bag.execute() {
// The complement of a set is computed within its definition space.
e @ Err(_) => e,
Ok(inside) => {
let mut outside = Vec::new();
match database::get_all(bag.space()) {
e @ Err(_) => e,
Ok(points) => {
for point in points {
if !inside.contains(&point) {
outside.push(point)
}
}
Ok(outside)
}
}
}
},
Bag::Distinct(bag) => distinct(db, core_id, bag, output_space, threshold_volume),
Bag::Filter(predicate, bag) => {
filter(db, core_id, predicate, bag, output_space, threshold_volume)
}
Bag::Complement(bag) => complement(
db,
core_id,
core,
bag,
output_space,
threshold,
threshold_volume,
),
Bag::Intersection(lh, rh) => {
let l = lh.execute();
if let Ok(l) = l {
let r = rh.execute();
if let Ok(r) = r {
let mut v = vec![];
if rh.predict() < lh.predict() {
for o in r {
if l.contains(&o) {
v.push(o);
}
}
} else {
for o in l {
if r.contains(&o) {
v.push(o);
}
}
}
Ok(v)
} else {
r
}
} else {
l
}
intersection(db, core_id, rh, lh, output_space, threshold_volume)
}
Bag::Union(lh, rh) => {
let l = lh.execute();
if let Ok(mut l) = l {
let r = rh.execute();
if let Ok(mut r) = r {
if rh.predict() < lh.predict() {
l.append(&mut r);
Ok(l)
} else {
r.append(&mut l);
Ok(r)
}
} else {
r
}
} else {
l
}
}
Bag::Bag(bags) => {
let mut v = vec![];
for bag in bags {
let b = bag.execute();
match b {
e @ Err(_) => {
return e;
}
Ok(mut b) => {
//TODO: SPACE CONVERSIONS IF NOT THE SAME SPACES?
v.append(&mut b);
}
}
}
Ok(v)
}
Bag::Inside(shape) => match shape {
Shape::Point(space_id, position) => database::get_by_position(space_id, position),
Shape::HyperRectangle(space_id, bounding_box) => {
database::get_by_bounding_box(space_id, bounding_box)
}
Shape::HyperSphere(space_id, position, radius) => {
let length = match radius {
LiteralNumber::Int(x) => *x as f64,
LiteralNumber::Float(x) => *x,
};
match get_bounding_box(position, radius) {
Err(e) => Err(e),
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
e @ Err(_) => e,
Ok(source) => {
let mut filtered = vec![];
for point in source {
// Include the surface of the sphere
if point.length() <= length {
filtered.push(point);
}
}
Ok(filtered)
}
},
}
}
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
},
Bag::Union(lh, rh) => union(db, core_id, rh, lh, output_space, threshold_volume),
Bag::Bag(list) => bag(db, core_id, list, output_space, threshold_volume),
Bag::Inside(shape) => inside(db, core, shape, output_space, threshold),
Bag::Outside(shape) => {
fn outside_set(space_id: &String, inside: Vec<database::Object>) -> ResultSet {
let mut outside = Vec::new();
match database::get_all(space_id) {
e @ Err(_) => e,
Ok(points) => {
for point in points {
if !inside.contains(&point) {
outside.push(point)
}
}
Ok(outside)
}
}
}
match shape {
Shape::Point(space_id, position) => {
match database::get_by_position(space_id, position) {
e @ Err(_) => e,
Ok(inside) => outside_set(space_id, inside),
}
}
Shape::HyperRectangle(space_id, bounding_box) => {
// We need to adapt the bounding_box to ensure the
// surface will not hit as part of the inside set, so we
// compute the biggest bounding box contained within the
// given box.
// Smallest increment possible
let mut low: Vec<LiteralNumber> = vec![];
let LiteralPosition(coordinates) = &bounding_box[0];
for coordinate in coordinates {
match coordinate {
LiteralNumber::Int(x) => low.push(LiteralNumber::Int(x + 1)),
LiteralNumber::Float(x) => {
low.push(LiteralNumber::Float(x + std::f64::EPSILON))
}
};
}
let low = LiteralPosition(low);
// Smallest decrement possible
let mut high: Vec<LiteralNumber> = vec![];
let LiteralPosition(coordinates) = &bounding_box[1];
for coordinate in coordinates {
match coordinate {
LiteralNumber::Int(x) => high.push(LiteralNumber::Int(x - 1)),
LiteralNumber::Float(x) => {
high.push(LiteralNumber::Float(x - std::f64::EPSILON))
}
};
}
let high = LiteralPosition(high);
match database::get_by_bounding_box(space_id, &vec![low, high]) {
e @ Err(_) => e,
Ok(inside) => outside_set(space_id, inside),
}
}
Shape::HyperSphere(space_id, position, radius) => {
let length = match radius {
LiteralNumber::Int(x) => *x as f64,
LiteralNumber::Float(x) => *x,
};
match get_bounding_box(position, radius) {
Err(e) => Err(e),
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
Err(e) => Err(e),
Ok(source) => {
let mut filtered = vec![];
for point in source {
// Exclude the surface of the sphere, so
// that it is included in the
// complement.
if point.length() < length {
filtered.push(point);
}
}
outside_set(space_id, filtered)
}
},
}
}
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
}
//FIXME: This is currently computed as the complement of the values within the shape, except its surface.
// Should this be instead a list of positions within the shape?
//FIXME: Should we use the Shape's Space to get the maximum bounds or the output Space requested?
outside(db, core, shape, output_space, threshold)
}
}
}
}
impl Predicate {
pub fn eval(&self, point: &database::Object) -> bool {
match self {
Predicate::Not(predicate) => !predicate.eval(point),
Predicate::And(lh, rh) => lh.eval(point) && rh.eval(point),
Predicate::Or(lh, rh) => lh.eval(point) || rh.eval(point),
// I don't know how to evaluate these at this point, so let the DB object take care of that.
// Predicate::Less(selector, literal) => &selector.eval(point) < literal,
// Predicate::Greater(selector, literal) => &selector.eval(point) > literal,
// Predicate::Equal(selector, literal) => &selector.eval(point) == literal,
// Redirect to the DB Objet the evaluation of the remaining predicate operators
predicate => point.eval(predicate),
}
}
}

View File

@@ -1,15 +0,0 @@
pub trait Validator {
type ValidationResult;
fn validate(&self) -> Self::ValidationResult;
}
pub trait Predictor {
fn predict(&self) -> f64;
}
pub trait Executor {
type ResultSet;
fn execute(&self) -> Self::ResultSet;
}

28
src/expressions.rs Normal file
View File

@@ -0,0 +1,28 @@
use mercator_db::DataBase;
use mercator_db::SpaceObject;
pub trait Validator {
type ValidationResult;
fn validate(&self) -> Self::ValidationResult;
}
pub trait Predictor {
fn predict(&self, db: &DataBase) -> Result<f64, String>;
}
pub trait Executor {
type ResultSet;
fn execute(
&self,
db: &DataBase,
core_id: &str,
output_space: Option<&str>,
threshold_volume: Option<f64>,
) -> Self::ResultSet;
}
pub trait Evaluator {
fn eval(&self, object: &SpaceObject) -> bool;
}

View File

@@ -1,25 +1,23 @@
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate lalrpop_util;
lalrpop_mod!(pub queries); // synthesized by LALRPOP
lalrpop_mod!(#[allow(clippy::all)] pub queries); // synthesized by LALRPOP
mod database;
mod evaluators;
mod executors;
mod expression;
mod expressions;
mod predictors;
mod validators;
mod symbols;
mod types;
pub use expression::Executor;
pub use expression::Predictor;
pub use expression::Validator;
pub use expressions::Executor;
pub use expressions::Predictor;
pub use expressions::Validator;
pub use queries::FiltersParser;
pub use queries::QueryParser;
pub use validators::ValidationResult;
#[cfg(test)]
mod tests;

View File

@@ -1,12 +1,16 @@
#[macro_use]
extern crate measure_time;
extern crate parser;
use parser::QueryParser;
use parser::{Executor, Predictor, Validator};
use std::io;
use std::process::exit;
use mercator_db::json::storage;
use mercator_db::DataBase;
use parser::Executor;
use parser::FiltersParser;
use parser::Predictor;
use parser::QueryParser;
use parser::Validator;
fn main() {
// If RUST_LOG is unset, set it to INFO, otherwise keep it as-is.
@@ -15,8 +19,41 @@ fn main() {
}
pretty_env_logger::init();
//let parser = queries::FiltersParser::new();
let import;
if std::env::var("MERCATOR_IMPORT_DATA").is_err() {
std::env::set_var("MERCATOR_IMPORT_DATA", "test");
}
match std::env::var("MERCATOR_IMPORT_DATA") {
Ok(val) => import = val,
Err(val) => {
error!("Could not fetch {} : `{}`", "MERCATOR_IMPORT_DATA", val);
exit(1);
}
};
// Convert to binary the JSON data:
if true {
info_time!("Converting to binary JSON data");
storage::convert(&import);
}
// Build a Database Index:
if true {
info_time!("Building database index");
storage::build(&import);
}
// Load a Database:
let db;
{
info_time!("Loading database index");
db = DataBase::load(import).unwrap();
}
let parser = QueryParser::new();
let parser = FiltersParser::new();
loop {
println!();
@@ -27,49 +64,59 @@ fn main() {
Ok(0) => break, // Catch ^D
Ok(1) => continue, // Catch \n
Err(_) => continue,
Ok(_) => (),
}
Ok(_) => {
if input.trim().eq_ignore_ascii_case("quit") {
break;
}
if input.trim().eq_ignore_ascii_case("quit") {
break;
}
info_time!("Interpretation");
let mut parse;
{
info_time!("Parsing");
parse = parser.parse(&input);
}
let input = input.as_str();
{
debug_time!("Interpretation");
let mut parse;
{
trace_time!("Parsing");
parse = parser.parse(input);
}
trace!("Tree: \n{:?}", parse);
if let Err(e) = &parse {
warn!("Parsing failed: \n{:?}", e);
} else {
trace!("Tree: \n{:?}", parse);
}
match parse {
Ok(Some(t)) => {
// QueryParser
//if let Ok(Some(t)) = parse {
// FiltersParser
if let Ok(t) = parse {
let validate;
{
trace_time!("Type check");
info_time!("Type check");
validate = t.validate();
}
info!("Type: \n{:?}", validate);
if let Ok(_) = validate {
if validate.is_ok() {
let predict;
{
trace_time!("Prediction");
predict = t.predict();
info_time!("Prediction");
predict = t.predict(&db);
}
info!("Predict: \n{:?}", predict);
let execute;
{
trace_time!("Exectution");
execute = t.execute();
info_time!("Execution");
execute = t.execute(&db, "test", None, None);
}
if let Ok(r) = execute {
//let r = model::to_spatial_objects(&db, r);
info!("Execution: \n{:#?}", r);
info!("NB results: {:?}", r.len());
} else {
info!("Execution: \n{:?}", execute);
}
info!("Execution: \n{:?}", execute);
}
}
_ => (),
}
}
}

View File

@@ -1,48 +1,48 @@
use super::expression::Predictor;
use mercator_db::DataBase;
use super::expressions::Predictor;
use super::symbols::*;
use super::database::space;
impl Predictor for Projection {
fn predict(&self) -> f64 {
fn predict(&self, db: &DataBase) -> Result<f64, String> {
match self {
Projection::Nifti(_, _, bag) => bag.predict(),
Projection::JSON(_, _, bag) => bag.predict(),
Projection::Nifti(_, _, bag) => bag.predict(db),
Projection::JSON(_, _, bag) => bag.predict(db),
}
}
}
impl Predictor for Bag {
fn predict(&self) -> f64 {
fn predict(&self, db: &DataBase) -> Result<f64, String> {
match self {
Bag::Distinct(bag) => bag.predict(),
Bag::Filter(_, bag) => bag.predict(),
Bag::Complement(bag) => space::max_volume(bag.space()) - bag.predict(),
Bag::Distinct(bag) => bag.predict(db),
Bag::Filter(_, bag) => bag.predict(db),
Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?),
Bag::Intersection(lh, rh) => {
let l = lh.predict();
let r = rh.predict();
let l = lh.predict(db)?;
let r = rh.predict(db)?;
if l < r {
l
Ok(l)
} else {
r
Ok(r)
}
}
Bag::Union(lh, rh) => lh.predict() + rh.predict(),
Bag::Union(lh, rh) => Ok(lh.predict(db)? + rh.predict(db)?),
Bag::Bag(bags) => {
let mut s = 0.0;
for bag in bags {
s += bag.predict();
s += bag.predict(db)?;
}
s
Ok(s)
}
Bag::Inside(shape) => shape.predict(),
Bag::Outside(shape) => space::max_volume(shape.space()) - shape.predict(),
Bag::Inside(shape) => shape.predict(db),
Bag::Outside(shape) => Ok(db.space(shape.space())?.volume() - shape.predict(db)?),
}
}
}
impl Predictor for Shape {
fn predict(&self) -> f64 {
self.volume()
fn predict(&self, _db: &DataBase) -> Result<f64, String> {
Ok(self.volume())
}
}

View File

@@ -1,7 +1,8 @@
use std::str::FromStr;
use mercator_db::space::Space;
use crate::symbols;
use crate::database::space;
grammar;
@@ -27,7 +28,7 @@ NiftiOperator: symbols::Projection = {
")" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
if let Some((sel, _)) = s {
@@ -47,7 +48,7 @@ JsonOperator: symbols::Projection = {
")" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
symbols::Projection::JSON(space_id, f, b)
@@ -148,9 +149,6 @@ Bags: symbols::Bag = {
// Spatial Operators
Inside,
Outside,
// When used directly here, the inside() operation on the shape is
// implied.
Shapes => symbols::Bag::Inside(<>)
};
//*********************************************************************/
@@ -186,26 +184,8 @@ Filter: symbols::Bag = {
// "filter" "(" <p:Predicates> "," <b:Bags> ")" =>
"filter" "(" <b:Bags> ")" =>
symbols::Bag::Filter(None, Box::new(b)),
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" => match b {
Some(b) => symbols::Bag::Filter(Some(p), Box::new(b)),
None => {
let v = space::bounding_box(space::name());
let mut positions = Vec::new();
for point in v {
let mut position = Vec::new();
for c in point {
position.push(symbols::LiteralNumber::Int(*c))
}
positions.push(symbols::LiteralPosition(position));
}
let bb = symbols::Shape::HyperRectangle(
space::name().clone(),
positions
);
symbols::Bag::Filter(Some(p), Box::new(symbols::Bag::Inside(bb)))
}
}
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" =>
symbols::get_filter(p, b)
};
Predicates: symbols::Predicate = {
@@ -311,7 +291,7 @@ HyperRectangle: symbols::Shape = {
"}" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
let mut pos = vec![l, h];
for (_, lh, _, rh) in list.iter() {
@@ -331,7 +311,7 @@ HyperSphere: symbols::Shape = {
"}" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
symbols::Shape::HyperSphere(space_id, c, r)
@@ -342,7 +322,7 @@ Point: symbols::Shape = {
"point" "{" <pos:Position> <rs:( "," <String> )?> "}" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
symbols::Shape::Point(space_id, pos)
@@ -365,7 +345,7 @@ Nifti: symbols::Shape = {
"}" => {
let space_id = match rs {
Some(id) => id,
None => space::name().clone(),
None => Space::universe().name().clone(),
};
symbols::Shape::Nifti(space_id)
}
@@ -383,7 +363,6 @@ ByteProvider = { "uri" "(" String ")" };
// Always returns a vector of numbers, a.k.a a position (a scalar will
// be represented as a vector of one element)
Positions: symbols::Position = {
StrCmpICase,
StrCmp,
Selector => symbols::Position::Selector(<>),
Position => symbols::Position::Literal(<>)
@@ -399,13 +378,6 @@ StrCmp: symbols::Position = {
}
};
// Same, but case insensitive.
StrCmpICase: symbols::Position = {
"str_cmp_ignore_case" "(" <s:Selector> "," <v:String> ")" => {
symbols::Position::StrCmpICase(s, v)
}
};
// FIXME: FIELDS are expected to be exisiting in the data model. Root Object is assumed to be the type of the ressource on which the POST call was done.
Selector: symbols::LiteralSelector = {
( <Field> )+ => symbols::LiteralSelector(<>)

View File

@@ -1,6 +1,8 @@
use std::cmp::Ordering;
use super::database::space;
use mercator_db::space;
use mercator_db::SpaceObject;
pub use super::types::*;
/**********************************************************************/
@@ -63,6 +65,8 @@ pub enum Bag {
Bag(Vec<Bag>),
Inside(Shape),
Outside(Shape),
//FIXME: ADD A SHAPE VARIANT WHICH JUST RETURNS ALL THE POSITIONS OF THAT SHAPE
//Shape(Shape),
}
impl Bag {
@@ -84,7 +88,7 @@ impl Bag {
Bag::Bag(_) => {
// Bags can be defined in different spaces, thus the output is
// always in the universe space.
space::name()
space::Space::universe().name()
}
Bag::Inside(shape) => shape.space(),
Bag::Outside(shape) => shape.space(),
@@ -163,12 +167,12 @@ impl Shape {
volume
}
Shape::HyperSphere(_space, pos, r) => {
Shape::HyperSphere(_space, pos, radius) => {
// Formula from https://en.wikipedia.org/wiki/N-sphere#/media/File:N_SpheresVolumeAndSurfaceArea.png
let LiteralPosition(p) = pos;
let k = p.len(); // Number of dimensions.
let LiteralPosition(position) = pos;
let k = position.len(); // Number of dimensions.
let r = match *r {
let radius = match *radius {
LiteralNumber::Int(x) => x as f64,
LiteralNumber::Float(x) => x,
};
@@ -178,12 +182,12 @@ impl Shape {
// Set starting values for the coefficient
let mut a = 2.0;
let mut i = 1;
if (k % 2) == 0 {
let mut i = if (k % 2) == 0 {
a = pi;
i = 2;
}
2
} else {
1
};
while i < k {
i += 2;
@@ -191,7 +195,7 @@ impl Shape {
a /= i as f64;
}
a * r.powi(i as i32)
a * radius.powi(i as i32)
}
Shape::Nifti(_) => unimplemented!(),
}
@@ -203,12 +207,28 @@ impl Shape {
/**********************************************************************/
#[derive(Clone, Debug)]
pub enum Position {
StrCmpICase(LiteralSelector, String),
StrCmp(LiteralSelector, String),
Selector(LiteralSelector),
Literal(LiteralPosition),
}
impl Position {
pub fn value(&self, object: &SpaceObject) -> LiteralPosition {
match self {
Position::Literal(literal) => literal.clone(),
Position::Selector(selector) => selector.position(object),
Position::StrCmp(selector, literal) => {
let x = match (selector.str(object)).cmp(literal) {
Ordering::Equal => 0,
Ordering::Greater => 1,
Ordering::Less => -1,
};
LiteralPosition(vec![LiteralNumber::Int(x)])
}
}
}
}
/**********************************************************************/
/* Literals / TOKENS */
/**********************************************************************/
@@ -222,6 +242,17 @@ pub enum LiteralNumber {
Float(f64),
}
impl From<&LiteralNumber> for Vec<f64> {
fn from(l: &LiteralNumber) -> Self {
let r = match l {
LiteralNumber::Int(x) => (*x) as f64,
LiteralNumber::Float(x) => *x,
};
vec![r]
}
}
impl PartialEq for LiteralNumber {
fn eq(&self, other: &LiteralNumber) -> bool {
match self {
@@ -270,6 +301,36 @@ impl LiteralPosition {
a
}
pub fn dimensions(&self) -> usize {
self.0.len()
}
}
impl From<&LiteralNumber> for f64 {
fn from(l: &LiteralNumber) -> Self {
match l {
LiteralNumber::Int(x) => (*x) as f64,
LiteralNumber::Float(x) => *x,
}
}
}
impl From<&LiteralPosition> for Vec<f64> {
fn from(l: &LiteralPosition) -> Self {
let LiteralPosition(v) = l;
let mut r = Vec::with_capacity(v.len());
for x in v {
let x = match x {
LiteralNumber::Int(x) => (*x) as f64,
LiteralNumber::Float(x) => *x,
};
r.push(x);
}
r
}
}
impl PartialOrd for LiteralPosition {
@@ -316,4 +377,69 @@ impl LiteralSelector {
// FIXME: Pretend for now that everything is a number, needs to be actually looked up in data model.
LiteralTypes::Int
}
// FIXME: THIS IS SOOO WRONG
pub fn position(&self, object: &SpaceObject) -> LiteralPosition {
println!("LiteralSelector.position(): {:?}", self);
let v: Vec<f64> = object.position.clone().into();
LiteralPosition(v.into_iter().map(LiteralNumber::Float).collect::<Vec<_>>())
}
// FIXME: THIS IS SOOO WRONG
pub fn str(&self, object: &SpaceObject) -> String {
let LiteralSelector(v) = self;
let last = v.last();
if let Some(Field(name, _)) = last {
if name == "id" {
return object.value.id().clone();
} else if name == "type" {
return object.value.type_name();
} else if name == "reference_space" {
return object.space_id.clone();
}
}
println!("LiteralSelector.str(): {:?}", self);
unimplemented!();
}
}
// The logic was getting a bit too complex to be embedded directly into the
// grammar definition.
pub fn get_filter(p: Predicate, b: Option<Bag>) -> Bag {
match b {
Some(b) => Bag::Filter(Some(p), Box::new(b)),
None => {
let (low, high) = space::Space::universe().bounding_box();
let low: Vec<_> = low.into();
let high: Vec<_> = high.into();
let bb = Shape::HyperRectangle(
space::Space::universe().name().clone(),
vec![
LiteralPosition(
low.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
LiteralPosition(
high.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
],
);
Bag::Filter(Some(p), Box::new(Bag::Inside(bb)))
}
}
}
//FIXME: HACK FOR NOW; NEED PROPER TYPE CHECKING!
pub fn get_type() -> LiteralTypes {
LiteralTypes::Vector(vec![
LiteralTypes::Float,
LiteralTypes::Float,
LiteralTypes::Float,
])
}

View File

@@ -1,5 +1,4 @@
use super::database::space;
use super::expression::Validator;
use super::expressions::Validator;
use super::symbols::*;
pub type ValidationResult = Result<LiteralTypes, String>;
@@ -24,7 +23,7 @@ impl Validator for Bag {
type ValidationResult = self::ValidationResult;
fn validate(&self) -> ValidationResult {
fn compare_bag_types(lh: &Box<Bag>, rh: &Box<Bag>) -> ValidationResult {
fn compare_bag_types(lh: &Bag, rh: &Bag) -> ValidationResult {
if lh.space().cmp(rh.space()) != std::cmp::Ordering::Equal {
return Err(format!(
"left and right sets are defined in different reference spaces: '{}' vs '{}'.",
@@ -68,7 +67,7 @@ impl Validator for Bag {
}
}
Ok(space::get_type(space::name()).clone())
Ok(get_type())
}
Bag::Inside(shape) => shape.validate(),
Bag::Outside(shape) => shape.validate(),