From 2b12c41095bd31de83b8fe03dddedac507fd6b35 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Mon, 7 May 2018 22:33:35 -0700 Subject: [PATCH] WIP: nested pull. --- query-parser/src/parse.rs | 6 +++++- query-pull/src/errors.rs | 5 +++++ query-pull/src/lib.rs | 30 ++++++++++++++++++++++++++++-- query/src/lib.rs | 26 +++++++++++++++++++++++++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/query-parser/src/parse.rs b/query-parser/src/parse.rs index 244a0cca..886917e4 100644 --- a/query-parser/src/parse.rs +++ b/query-parser/src/parse.rs @@ -366,7 +366,11 @@ fn validate_attributes<'a, I>(attrs: I) -> std::result::Result<(), &'static str> return Err("wildcard with specified attributes"); } }, - // TODO: map form. + &PullAttributeSpec::Nested(ref _attr, ref patterns) => { + if patterns.is_empty() { + return Err("empty nested pull map"); + } + }, } } Ok(()) diff --git a/query-pull/src/errors.rs b/query-pull/src/errors.rs index 94c38426..871bb47f 100644 --- a/query-pull/src/errors.rs +++ b/query-pull/src/errors.rs @@ -27,6 +27,11 @@ error_chain! { description(":db/id repeated") display(":db/id repeated") } + + NonRefNestedPullAttribute { + description("nested pull attribute is non-ref") + display("nested pull attribute is non-ref") + } } links { diff --git a/query-pull/src/lib.rs b/query-pull/src/lib.rs index 26ee1895..2af6fe0e 100644 --- a/query-pull/src/lib.rs +++ b/query-pull/src/lib.rs @@ -88,6 +88,7 @@ use mentat_core::{ StructuredMap, TypedValue, ValueRc, + ValueType, }; use mentat_db::cache; @@ -144,8 +145,18 @@ pub struct Puller { // The domain of this map is the set of attributes to fetch. // The range is the set of aliases to use in the output. attributes: BTreeMap>, + + // The original spec for this puller. attribute_spec: cache::AttributeSpec, + + // If :db/id is mentioned in the attribute list, its alias is this. db_id_alias: Option>, + + // A pull expression can be arbitrarily nested. We represent this both + // within the `attribute_spec` itself and also as a nested set of `Puller`s. + // When an attribute in the list above returns an entity -- and it should! -- + // it is accumulated and we recurse down into these nested layers. + nested: BTreeMap, } impl Puller { @@ -156,8 +167,8 @@ impl Puller { let lookup_name = |i: &Entid| { // In the unlikely event that we have an attribute with no name, we bail. schema.get_ident(*i) - .map(|ident| ValueRc::new(ident.clone())) - .ok_or_else(|| ErrorKind::UnnamedAttribute(*i)) + .map(|ident| ValueRc::new(ident.clone())) + .ok_or_else(|| ErrorKind::UnnamedAttribute(*i)) }; let mut names: BTreeMap> = Default::default(); @@ -204,6 +215,20 @@ impl Puller { }, } }, + + // An attribute that nests must be ref-typed. + &PullAttributeSpec::Nested(ref attribute, ref patterns) => { + let value_type = attribute.get_attribute(schema) + .map(|(a, _e)| a.value_type); + let is_ref_typed = value_type.map(|v| v == ValueType::Ref) + .unwrap_or(false); + if !is_ref_typed { + bail!(ErrorKind::NonRefNestedPullAttribute); + } + + // TODO + unimplemented!(); + }, } } @@ -211,6 +236,7 @@ impl Puller { attributes: names, attribute_spec: cache::AttributeSpec::specified(&attrs, schema), db_id_alias, + nested: Default::default(), }) } diff --git a/query/src/lib.rs b/query/src/lib.rs index cae69051..2eb2ee1a 100644 --- a/query/src/lib.rs +++ b/query/src/lib.rs @@ -55,7 +55,11 @@ pub use edn::{ }; use mentat_core::{ + Attribute, FromRc, + HasSchema, + KnownEntid, + Schema, TypedValue, ValueRc, ValueType, @@ -499,6 +503,19 @@ pub enum PullConcreteAttribute { Entid(i64), } +impl PullConcreteAttribute { + pub fn get_attribute<'s>(&self, schema: &'s Schema) -> Option<(&'s Attribute, KnownEntid)> { + match self { + &PullConcreteAttribute::Ident(ref rc) => { + schema.attribute_for_ident(rc.as_ref()) + }, + &PullConcreteAttribute::Entid(e) => { + schema.attribute_for_entid(e).map(|a| (a, KnownEntid(e))) + }, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct NamedPullAttribute { pub attribute: PullConcreteAttribute, @@ -518,7 +535,7 @@ impl From for NamedPullAttribute { pub enum PullAttributeSpec { Wildcard, Attribute(NamedPullAttribute), - // PullMapSpec(Vec<…>), + Nested(PullConcreteAttribute, Vec), // LimitedAttribute(PullConcreteAttribute, u64), // Limit nil => Attribute instead. // DefaultedAttribute(PullConcreteAttribute, PullDefaultValue), } @@ -556,6 +573,13 @@ impl std::fmt::Display for PullAttributeSpec { &PullAttributeSpec::Attribute(ref attr) => { write!(f, "{}", attr) }, + &PullAttributeSpec::Nested(ref attr, ref patterns) => { + write!(f, "{{{} [", attr)?; + for p in patterns { + write!(f, " {}", p)?; + } + write!(f, "]}}") + }, } } }