Introduce Label for search within id

This allows to define a volume using an indexed object.
This commit is contained in:
2019-11-18 11:21:44 +01:00
parent 0dc31c65c6
commit e2ea5c9ba4
8 changed files with 128 additions and 60 deletions

View File

@@ -19,6 +19,7 @@ bag_expression
// Spatial Operators // Spatial Operators
| inside | inside
| outside | outside
//| shape
; ;
/**********************************************************************/ /**********************************************************************/
@@ -113,6 +114,11 @@ inside
: 'inside' '(' shapes ')' : 'inside' '(' shapes ')'
; ;
/* Returns the set of positions inside the shape, (face included) */
shape
: 'shape' '(' shapes ')'
;
/**********************************************************************/ /**********************************************************************/
/* SHAPES */ /* SHAPES */
/**********************************************************************/ /**********************************************************************/

View File

@@ -18,7 +18,7 @@ projection_operators
* *
* If it is provided, it MUST resolve to a NUMBER. */ * If it is provided, it MUST resolve to a NUMBER. */
nifti_operator nifti_operator
: 'nifti' '(' ( STRING ',' )? ( selector ',' )? bag_expression ')' : 'nifti' '(' ( selector ',' )? bag_expression ( ',' STRING )? ')'
; ;
json_operator json_operator

View File

@@ -80,33 +80,68 @@ fn distinct<'c>(
} }
} }
} }
fn filter_helper<'c>(
predicate: &Predicate,
bag: &Bag,
core_id: &str,
parameters: &CoreQueryParameters<'c>,
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(results) => Ok(results
.into_iter()
.filter_map(|(space, positions)| {
let filtered = positions
.into_iter()
.filter(|(position, properties)| predicate.eval((space, position, properties)))
.collect::<Vec<_>>();
if filtered.is_empty() {
None
} else {
Some((space, filtered))
}
})
.collect::<Vec<_>>()),
}
}
fn filter<'c>( fn filter<'c>(
core_id: &str, core_id: &str,
parameters: &CoreQueryParameters<'c>, parameters: &CoreQueryParameters<'c>,
predicate: &Option<Predicate>, predicate: &Option<Predicate>,
bag: &Bag, bag: &Option<Box<Bag>>,
) -> mercator_db::ResultSet<'c> { ) -> mercator_db::ResultSet<'c> {
match predicate { match predicate {
None => bag.execute(core_id, parameters), None => {
Some(predicate) => match bag.execute(core_id, parameters) { if let Some(bag) = bag {
e @ Err(_) => e, bag.execute(core_id, parameters)
Ok(results) => Ok(results } else {
.into_iter() Err("Filter without predicate nor data set.".to_string())
.filter_map(|(space, positions)| { }
let filtered = positions }
.into_iter() Some(predicate) => match bag {
.filter(|(position, properties)| { None => {
predicate.eval((space, position, properties)) let (low, high) = space::Space::universe().bounding_box();
}) let low: Vec<_> = low.into();
.collect::<Vec<_>>(); let high: Vec<_> = high.into();
if filtered.is_empty() { let shape = Shape::HyperRectangle(
None space::Space::universe().name().clone(),
} else { vec![
Some((space, filtered)) LiteralPosition(
} low.into_iter()
}) .map(LiteralNumber::Float)
.collect::<Vec<_>>()), .collect::<Vec<_>>(),
),
LiteralPosition(
high.into_iter()
.map(LiteralNumber::Float)
.collect::<Vec<_>>(),
),
],
);
filter_helper(predicate, &Bag::Inside(shape), core_id, parameters)
}
Some(bag) => filter_helper(predicate, bag.as_ref(), core_id, parameters),
}, },
} }
} }
@@ -248,6 +283,10 @@ fn inside<'c>(
//FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE? //FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE?
Ok((space_id, space::Shape::HyperSphere(position, radius))) Ok((space_id, space::Shape::HyperSphere(position, radius)))
} }
Shape::Label(_, id) => {
// Not a real shape, so short circuit and return.
return core.get_by_label(parameters, id);
}
Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()), Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()),
}; };
@@ -310,6 +349,7 @@ fn outside<'c>(
Ok(inside) => complement_helper(core, parameters, space_id, inside), Ok(inside) => complement_helper(core, parameters, space_id, inside),
} }
} }
Shape::Label(_, _) => Err("Label: not yet implemented".to_string()),
Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()), Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()),
} }
} }

View File

@@ -94,7 +94,7 @@ fn main() {
} }
if let Ok(r) = execute { if let Ok(r) = execute {
//let r = mercator_db::json::model::to_spatial_objects(&db, r); //let r = mercator_db::json::model::to_spatial_objects(r);
info!("Execution: \n{:#?}", r); info!("Execution: \n{:#?}", r);
info!("NB results: {:?}", r.len()); info!("NB results: {:?}", r.len());
} else { } else {

View File

@@ -1,3 +1,4 @@
use mercator_db::space;
use mercator_db::DataBase; use mercator_db::DataBase;
use super::expressions::Predictor; use super::expressions::Predictor;
@@ -17,7 +18,10 @@ impl Predictor for Bag {
match self { match self {
Bag::ViewPort(bag) => bag.predict(db), Bag::ViewPort(bag) => bag.predict(db),
Bag::Distinct(bag) => bag.predict(db), Bag::Distinct(bag) => bag.predict(db),
Bag::Filter(_, bag) => bag.predict(db), Bag::Filter(_, bag) => match bag {
None => Ok(db.space(space::Space::universe().name())?.volume()),
Some(b) => b.predict(db),
},
Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?), Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?),
Bag::Intersection(lh, rh) => { Bag::Intersection(lh, rh) => {
let l = lh.predict(db)?; let l = lh.predict(db)?;

View File

@@ -153,6 +153,8 @@ Bags: symbols::Bag = {
// Spatial Operators // Spatial Operators
Inside, Inside,
Outside, Outside,
// returns the positions or volume of the shape, instead of the data points in or outside it.
//Shape,
}; };
//*********************************************************************/ //*********************************************************************/
@@ -187,9 +189,13 @@ Union: symbols::Bag = {
Filter: symbols::Bag = { Filter: symbols::Bag = {
// "filter" "(" <p:Predicates> "," <b:Bags> ")" => // "filter" "(" <p:Predicates> "," <b:Bags> ")" =>
"filter" "(" <b:Bags> ")" => "filter" "(" <b:Bags> ")" =>
symbols::Bag::Filter(None, Box::new(b)), symbols::Bag::Filter(None, Some(Box::new(b))),
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" => "filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" =>
symbols::get_filter(p, b) match b {
None => symbols::Bag::Filter(Some(p), None),
Some(b) => symbols::Bag::Filter(Some(p), Some(Box::new(b))),
}
}; };
Predicates: symbols::Predicate = { Predicates: symbols::Predicate = {
@@ -272,6 +278,12 @@ Inside: symbols::Bag = {
symbols::Bag::Inside(<>) symbols::Bag::Inside(<>)
}; };
//FIXME: ADD A SHAPE VARIANT WHICH JUST RETURNS ALL THE POSITIONS OF THAT SHAPE
//Shape: symbols::Bag = {
// <Shapes> =>
// symbols::Bag::Shape(<>)
//}
//*********************************************************************/ //*********************************************************************/
// SHAPES */ // SHAPES */
//*********************************************************************/ //*********************************************************************/
@@ -282,6 +294,7 @@ Shapes: symbols::Shape = {
Point, Point,
HyperRectangle, HyperRectangle,
HyperSphere, HyperSphere,
Label,
Nifti Nifti
}; };
@@ -333,6 +346,21 @@ Point: symbols::Shape = {
} }
}; };
// Filter by Label, a.k.a use an ID to define a volume, and use that volume to
// select data points.
Label: symbols::Shape = {
"label" "{"
<id:String>
<rs:( "," <String> )?>
"}" => {
let space_id = match rs {
Some(id) => id,
None => Space::universe().name().clone(),
};
symbols::Shape::Label(space_id, id)
}
};
// Define a shape as the non-zero values in a NIfTI object, defined by // Define a shape as the non-zero values in a NIfTI object, defined by
// nifti{ // nifti{
// spaceId: string, // spaceId: string,

View File

@@ -61,7 +61,7 @@ pub enum Bag {
ViewPort(Box<Bag>), ViewPort(Box<Bag>),
// Bags // Bags
Distinct(Box<Bag>), Distinct(Box<Bag>),
Filter(Option<Predicate>, Box<Bag>), Filter(Option<Predicate>, Option<Box<Bag>>),
Complement(Box<Bag>), Complement(Box<Bag>),
Intersection(Box<Bag>, Box<Bag>), Intersection(Box<Bag>, Box<Bag>),
Union(Box<Bag>, Box<Bag>), Union(Box<Bag>, Box<Bag>),
@@ -77,7 +77,10 @@ impl Bag {
match self { match self {
Bag::ViewPort(bag) => bag.space(), Bag::ViewPort(bag) => bag.space(),
Bag::Distinct(bag) => bag.space(), Bag::Distinct(bag) => bag.space(),
Bag::Filter(_, bag) => bag.space(), Bag::Filter(_, bag) => match bag {
None => space::Space::universe().name(),
Some(b) => b.space(),
},
Bag::Complement(bag) => bag.space(), Bag::Complement(bag) => bag.space(),
Bag::Intersection(lh, _) => { Bag::Intersection(lh, _) => {
// We are assuming lh and rh are in the same space. // We are assuming lh and rh are in the same space.
@@ -124,6 +127,7 @@ pub enum Shape {
Point(String, LiteralPosition), Point(String, LiteralPosition),
HyperRectangle(String, Vec<LiteralPosition>), HyperRectangle(String, Vec<LiteralPosition>),
HyperSphere(String, LiteralPosition, LiteralNumber), HyperSphere(String, LiteralPosition, LiteralNumber),
Label(String, String),
Nifti(String), Nifti(String),
} }
@@ -133,6 +137,7 @@ impl Shape {
Shape::Point(space, _) => space, Shape::Point(space, _) => space,
Shape::HyperRectangle(space, _) => space, Shape::HyperRectangle(space, _) => space,
Shape::HyperSphere(space, _, _) => space, Shape::HyperSphere(space, _, _) => space,
Shape::Label(space, _) => space,
Shape::Nifti(space) => space, Shape::Nifti(space) => space,
} }
} }
@@ -201,9 +206,17 @@ impl Shape {
a * radius.powi(i as i32) a * radius.powi(i as i32)
} }
Shape::Nifti(_) => unimplemented!(), Shape::Label(_, _) => {
// FIXME: Needs to find a way to figure out the approximate volume of this specific ID, or return MAX or MIN..
std::f64::EPSILON
}
Shape::Nifti(_) => unimplemented!("Nifti"),
} }
} }
pub fn rasterize<'e>(&self) -> mercator_db::ResultSet<'e> {
unimplemented!("rasterize")
}
} }
/**********************************************************************/ /**********************************************************************/
@@ -426,37 +439,7 @@ impl LiteralSelector {
} }
println!("LiteralSelector.str(): {:?}", self); println!("LiteralSelector.str(): {:?}", self);
unimplemented!(); unimplemented!("Unknown Field");
}
}
// 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)))
}
} }
} }

View File

@@ -56,7 +56,10 @@ impl Validator for Bag {
match self { match self {
Bag::ViewPort(bag) => bag.validate(), Bag::ViewPort(bag) => bag.validate(),
Bag::Distinct(bag) => bag.validate(), Bag::Distinct(bag) => bag.validate(),
Bag::Filter(_, bag) => bag.validate(), Bag::Filter(_, bag) => match bag {
None => Ok(LiteralPosition(vec![]).get_type()),
Some(b) => b.validate(),
},
Bag::Complement(bag) => bag.validate(), Bag::Complement(bag) => bag.validate(),
Bag::Intersection(lh, rh) => compare_bag_types(lh, rh), Bag::Intersection(lh, rh) => compare_bag_types(lh, rh),
Bag::Union(lh, rh) => compare_bag_types(lh, rh), Bag::Union(lh, rh) => compare_bag_types(lh, rh),
@@ -149,6 +152,10 @@ impl Validator for Shape {
} }
} }
Shape::HyperSphere(_, pos, _) => pos.validate(), Shape::HyperSphere(_, pos, _) => pos.validate(),
Shape::Label(_, _) => {
// FIXME: Quick Hack, we need to fix this and return the effective type of the object Id.
Ok(LiteralPosition(vec![]).get_type())
}
Shape::Nifti(_) => Err("not yet implemented".to_string()), Shape::Nifti(_) => Err("not yet implemented".to_string()),
} }
} }