Add Exists in Nested Collection to Expression type (#1027)

<!-- The PR description should answer 2 important questions: -->

### What

Add exists in nested collection to our internal `Expression` type so we
can use it in NDC requests.

### How

<!-- How is it trying to accomplish it (what are the implementation
steps)? -->

V3_GIT_ORIGIN_REV_ID: b8c2454e35667074a8814844d80430fc765ccdab
This commit is contained in:
Daniel Harvey 2024-08-28 16:48:02 +01:00 committed by hasura-bot
parent eabc3966db
commit 6795ea60fc
10 changed files with 104 additions and 7 deletions

2
v3/Cargo.lock generated
View File

@ -3031,7 +3031,7 @@ dependencies = [
[[package]]
name = "ndc-models"
version = "0.2.0"
source = "git+https://github.com/hasura/ndc-spec.git?tag=v0.2.0-rc.0#2893acd5de273667e84f9f87a25901615b368593"
source = "git+https://github.com/hasura/ndc-spec.git?rev=54baf298b5d6a69f12a701e9f0e3ce8f188a194d#54baf298b5d6a69f12a701e9f0e3ce8f188a194d"
dependencies = [
"indexmap 2.4.0",
"ref-cast",

View File

@ -61,7 +61,7 @@ used_underscore_binding = "allow"
private_intra_doc_links = "allow"
[workspace.dependencies]
ndc-models = { git = "https://github.com/hasura/ndc-spec.git", tag = "v0.2.0-rc.0" } # When you update this tag, also update the schema references in crates/open-dds/src/data_connector.rs
ndc-models = { git = "https://github.com/hasura/ndc-spec.git", rev = "54baf298b5d6a69f12a701e9f0e3ce8f188a194d" } # When you update this tag, also update the schema references in crates/open-dds/src/data_connector.rs
ndc-models-v01 = { package = "ndc-models", git = "https://github.com/hasura/ndc-spec.git", tag = "v0.1.6" }
anyhow = "1"

View File

@ -1038,6 +1038,9 @@ fn eval_in_collection(
get_collection_by_name(collection, &arguments, state)
}
ndc_models::ExistsInCollection::NestedCollection { .. } => {
todo!("ExistsInCollection::NestedCollection not currently supported")
}
}
}

View File

@ -45,6 +45,7 @@ pub fn get_capabilities() -> ndc_models::CapabilitiesResponse {
exists: ndc_models::ExistsCapabilities {
named_scopes: None,
unrelated: Some(ndc_models::LeafCapability {}),
nested_collections: None,
},
},
relationships: Some(ndc_models::RelationshipCapabilities {

View File

@ -129,7 +129,10 @@ async fn explain_query_predicate<'s>(
ir::Expression::Not { expression } => {
explain_query_predicate(expose_internal_errors, http_context, expression, steps).await
}
ir::Expression::LocalField { .. } | ir::Expression::LocalRelationship { .. } => Ok(()),
ir::Expression::LocalField { .. }
| ir::Expression::LocalRelationship { .. }
| ir::Expression::LocalNestedArray { .. } => Ok(()),
ir::Expression::RemoteRelationship {
relationship: _,
target_model_name,

View File

@ -10,6 +10,7 @@ use super::query;
use super::relationships::{self, process_model_relationship_definition};
use crate::{error, ndc, HttpContext};
use ir::{NdcFieldAlias, NdcRelationshipName};
use open_dds::data_connector::DataConnectorColumnName;
/// Plan the filter expression IR.
/// This function will take the filter expression IR and convert it into a planned filter expression
@ -80,6 +81,18 @@ pub fn plan_expression<'s, 'a>(
ir::Expression::LocalField(local_field_comparison) => {
Ok(ir::Expression::LocalField(local_field_comparison.clone()))
}
ir::Expression::LocalNestedArray {
predicate,
field_path,
column,
} => {
let resolved_predicate = plan_expression(predicate, relationships)?;
Ok(ir::Expression::LocalNestedArray {
column: column.clone(),
field_path: field_path.clone(),
predicate: Box::new(resolved_predicate),
})
}
ir::Expression::LocalRelationship {
relationship,
predicate,
@ -175,6 +188,11 @@ pub enum ResolvedFilterExpression {
expression: Box<ResolvedFilterExpression>,
},
LocalFieldComparison(ir::LocalFieldComparison),
LocalNestedArray {
column: DataConnectorColumnName,
field_path: Vec<DataConnectorColumnName>,
predicate: Box<ResolvedFilterExpression>,
},
LocalRelationshipComparison {
relationship: NdcRelationshipName,
predicate: Box<ResolvedFilterExpression>,
@ -317,6 +335,18 @@ where
predicate: Box::new(resolved_expression),
})
}
ir::Expression::LocalNestedArray {
column,
field_path,
predicate,
} => {
let resolved_expression = resolve_expression(*predicate, http_context).await?;
Ok(ResolvedFilterExpression::LocalNestedArray {
column,
field_path,
predicate: Box::new(resolved_expression),
})
}
ir::Expression::RemoteRelationship {
relationship,
target_model_name: _,

View File

@ -242,6 +242,26 @@ fn make_expression(
operator: ndc_models_v01::ComparisonOperatorName::new(operator.into_inner()),
value: make_comparison_value(value),
}),
filter::ResolvedFilterExpression::LocalNestedArray {
column,
field_path,
predicate,
} => {
let ndc_expression = make_expression(*predicate)?;
let field_name = ndc_models_v01::FieldName::new(column.into_inner());
Ok(ndc_models_v01::Expression::Exists {
in_collection: ndc_models_v01::ExistsInCollection::NestedCollection {
column_name: field_name,
field_path: field_path
.into_iter()
.map(|f| ndc_models_v01::FieldName::new(f.into_inner()))
.collect(),
arguments: BTreeMap::new(),
},
predicate: Some(Box::new(ndc_expression)),
})
}
filter::ResolvedFilterExpression::LocalFieldComparison(
ir::LocalFieldComparison::UnaryComparison { column, operator },
) => Ok(ndc_models_v01::Expression::UnaryComparisonOperator {

View File

@ -209,26 +209,31 @@ pub fn make_expression(
match predicate {
filter::ResolvedFilterExpression::And { expressions } => {
let mut ndc_expressions = Vec::new();
for expression in expressions {
let ndc_expression = make_expression(expression)?;
ndc_expressions.push(ndc_expression);
}
Ok(ndc_models_v02::Expression::And {
expressions: ndc_expressions,
})
}
filter::ResolvedFilterExpression::Or { expressions } => {
let mut ndc_expressions = Vec::new();
for expression in expressions {
let ndc_expression = make_expression(expression)?;
ndc_expressions.push(ndc_expression);
}
Ok(ndc_models_v02::Expression::Or {
expressions: ndc_expressions,
})
}
filter::ResolvedFilterExpression::Not { expression } => {
let ndc_expression = make_expression(*expression)?;
Ok(ndc_models_v02::Expression::Not {
expression: Box::new(ndc_expression),
})
@ -244,6 +249,27 @@ pub fn make_expression(
operator: ndc_models_v02::ComparisonOperatorName::new(operator.into_inner()),
value: make_comparison_value(value),
}),
filter::ResolvedFilterExpression::LocalNestedArray {
column,
field_path,
predicate,
} => {
let ndc_expression = make_expression(*predicate)?;
let field_name = ndc_models_v02::FieldName::new(column.into_inner());
Ok(ndc_models_v02::Expression::Exists {
in_collection: ndc_models_v02::ExistsInCollection::NestedCollection {
column_name: field_name,
field_path: field_path
.into_iter()
.map(|f| ndc_models_v02::FieldName::new(f.into_inner()))
.collect(),
arguments: BTreeMap::new(),
},
predicate: Some(Box::new(ndc_expression)),
})
}
filter::ResolvedFilterExpression::LocalFieldComparison(
ir::LocalFieldComparison::UnaryComparison { column, operator },
) => Ok(ndc_models_v02::Expression::UnaryComparisonOperator {

View File

@ -26,6 +26,11 @@ pub enum Expression<'s> {
predicate: Box<Expression<'s>>,
info: LocalModelRelationshipInfo<'s>,
},
LocalNestedArray {
column: DataConnectorColumnName,
field_path: Vec<DataConnectorColumnName>,
predicate: Box<Expression<'s>>,
},
RemoteRelationship {
relationship: RelationshipName,
target_model_name: &'s Qualified<ModelName>,

View File

@ -342,10 +342,7 @@ fn migrate_query_capabilities_from_v01(
nested_fields: migrate_nested_field_capabilities_from_v01(
old_query_capabilities.nested_fields,
),
exists: ndc_models_v02::ExistsCapabilities {
named_scopes: None, // v0.1.x does not have named scopes
unrelated: Some(ndc_models_v02::LeafCapability {}), // v0.1.x assumed this capability
},
exists: migrate_exists_capabilities_from_v01(old_query_capabilities.exists),
}
}
@ -366,6 +363,18 @@ fn migrate_leaf_capability_from_v01(
ndc_models_v02::LeafCapability {}
}
fn migrate_exists_capabilities_from_v01(
old_exists_capabilities: ndc_models_v01::ExistsCapabilities,
) -> ndc_models_v02::ExistsCapabilities {
ndc_models_v02::ExistsCapabilities {
named_scopes: None, // v0.1.x does not have named scopes
unrelated: Some(ndc_models_v02::LeafCapability {}), // v0.1.x assumed this capability
nested_collections: old_exists_capabilities
.nested_collections
.map(migrate_leaf_capability_from_v01),
}
}
fn migrate_nested_field_capabilities_from_v01(
old_nested_field_capabilities: ndc_models_v01::NestedFieldCapabilities,
) -> ndc_models_v02::NestedFieldCapabilities {