From a0d30feab9a577d9ea46fec0aacb4a009d7e346e Mon Sep 17 00:00:00 2001 From: paritosh-08 <85472423+paritosh-08@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:41:49 +0530 Subject: [PATCH] add ndc_explain to select node (#285) Co-authored-by: Anon Ray V3_GIT_ORIGIN_REV_ID: e45510454fe299fb41690a8fb56bcdce7628cd67 --- v3/ci.docker-compose.yaml | 2 +- v3/custom-connector/src/bin/agent/main.rs | 2 +- v3/docker-compose.yaml | 2 +- v3/engine/src/execute.rs | 33 +- v3/engine/src/execute/explain.rs | 121 +- v3/engine/src/execute/explain/types.rs | 58 +- v3/engine/tests/common.rs | 19 +- .../execute/relationships/array/metadata.json | 5 +- .../remote_relationships/array/metadata.json | 5 +- v3/engine/tests/explain.rs | 27 +- .../expected.json | 37 +- .../field_with_local_relationship/request.gql | 10 +- .../expected.json | 71 +- .../metadata.json | 451 +++ .../request.gql | 16 +- .../expected.json | 87 +- .../metadata.json | 612 ++++ .../request.gql | 18 +- .../expected.json | 96 +- .../metadata.json | 613 ++++ .../request.gql | 18 +- .../expected.json | 46 +- .../request.gql | 10 +- .../explain/introspection_query/metadata.json | 15 + v3/engine/tests/explain/metadata.json | 2666 ----------------- .../multi_root_field_queries/expected.json | 75 +- .../multi_root_field_queries/request.gql | 17 +- v3/lang-graphql/src/http.rs | 6 +- 28 files changed, 2245 insertions(+), 2893 deletions(-) create mode 100644 v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/metadata.json create mode 100644 v3/engine/tests/explain/field_with_nested_remote_relationship_1/metadata.json create mode 100644 v3/engine/tests/explain/field_with_nested_remote_relationship_2/metadata.json create mode 100644 v3/engine/tests/explain/introspection_query/metadata.json delete mode 100644 v3/engine/tests/explain/metadata.json diff --git a/v3/ci.docker-compose.yaml b/v3/ci.docker-compose.yaml index 6150409dc7a28..28ab9970261da 100644 --- a/v3/ci.docker-compose.yaml +++ b/v3/ci.docker-compose.yaml @@ -25,7 +25,7 @@ services: timeout: 10s retries: 20 postgres_connector: - image: ghcr.io/hasura/ndc-postgres:dev-main-5aec135c1 + image: ghcr.io/hasura/ndc-postgres:dev-main-dd4172889 volumes: - ./engine/tests/pg_ndc_config.json:/config.json depends_on: diff --git a/v3/custom-connector/src/bin/agent/main.rs b/v3/custom-connector/src/bin/agent/main.rs index 017c72062657a..cdab0e3005ba7 100644 --- a/v3/custom-connector/src/bin/agent/main.rs +++ b/v3/custom-connector/src/bin/agent/main.rs @@ -164,7 +164,7 @@ async fn get_capabilities() -> Json { Json(models::CapabilitiesResponse { versions: "^0.1.0".into(), capabilities: models::Capabilities { - explain: Some(models::LeafCapability {}), + explain: None, query: models::QueryCapabilities { aggregates: Some(models::LeafCapability {}), variables: Some(models::LeafCapability {}), diff --git a/v3/docker-compose.yaml b/v3/docker-compose.yaml index 6a8b837c3eb8f..b824d89a65057 100644 --- a/v3/docker-compose.yaml +++ b/v3/docker-compose.yaml @@ -54,7 +54,7 @@ services: COLLECTOR_OTLP_ENABLED: 'true' COLLECTOR_ZIPKIN_HOST_PORT: '9411' postgres_connector: - image: ghcr.io/hasura/ndc-postgres:dev-main-5aec135c1 + image: ghcr.io/hasura/ndc-postgres:dev-main-dd4172889 ports: - 8100:8100 volumes: diff --git a/v3/engine/src/execute.rs b/v3/engine/src/execute.rs index 20c0ed23c2b3e..462a99cc92930 100644 --- a/v3/engine/src/execute.rs +++ b/v3/engine/src/execute.rs @@ -238,18 +238,27 @@ pub async fn execute_request_internal( .await } RequestMode::Explain => { - tracer.in_span("explain", SpanVisibility::Internal, || { - // convert the query plan to explain step - let explain_response = - match crate::execute::explain::explain_query_plan(query_plan) { - Ok(step) => step.to_explain_response(), - Err(e) => explain::types::ExplainResponse::error( - e.to_graphql_error(None), - ), - }; - - ExecuteOrExplainResponse::Explain(explain_response) - }) + tracer + .in_span_async("explain", SpanVisibility::Internal, || { + Box::pin(async { + // convert the query plan to explain step + let explain_response = + match crate::execute::explain::explain_query_plan( + http_client, + query_plan, + ) + .await + { + Ok(step) => step.make_explain_response(), + Err(e) => explain::types::ExplainResponse::error( + e.to_graphql_error(None), + ), + }; + + ExecuteOrExplainResponse::Explain(explain_response) + }) + }) + .await } }; Ok(response) diff --git a/v3/engine/src/execute/explain.rs b/v3/engine/src/execute/explain.rs index 9f9079880943e..65c8472979aa3 100644 --- a/v3/engine/src/execute/explain.rs +++ b/v3/engine/src/execute/explain.rs @@ -1,16 +1,19 @@ +use super::remote_joins::types::RemoteJoinType; +use super::ExecuteOrExplainResponse; use crate::execute::query_plan::{NodeQueryPlan, ProcessResponseAs}; use crate::execute::remote_joins::types::{JoinId, JoinLocations, RemoteJoin}; +use crate::execute::{error, query_plan}; +use crate::metadata::resolved; use crate::schema::GDS; +use async_recursion::async_recursion; use hasura_authn_core::Session; use lang_graphql as gql; use lang_graphql::{http::RawRequest, schema::Schema}; use nonempty::NonEmpty; - -use crate::execute::{error, query_plan}; - -use super::remote_joins::types::RemoteJoinType; -use super::ExecuteOrExplainResponse; +use tracing_util::SpanVisibility; pub mod types; +use lang_graphql::ast::common as ast; +use ndc_client as ndc; pub async fn execute_explain( http_client: &reqwest::Client, @@ -49,7 +52,8 @@ pub async fn execute_explain_internal( } } -pub fn explain_query_plan( +pub(crate) async fn explain_query_plan( + http_client: &reqwest::Client, query_plan: query_plan::QueryPlan<'_, '_, '_>, ) -> Result { let mut parallel_root_steps = vec![]; @@ -58,20 +62,26 @@ pub fn explain_query_plan( match node { NodeQueryPlan::NDCQueryExecution(ndc_query_execution) => { let sequence_steps = get_execution_steps( + http_client, alias, &ndc_query_execution.process_response_as, ndc_query_execution.execution_tree.remote_executions, ndc_query_execution.execution_tree.root_node.query, - ); + ndc_query_execution.execution_tree.root_node.data_connector, + ) + .await; parallel_root_steps.push(Box::new(types::Step::Sequence(sequence_steps))); } NodeQueryPlan::RelayNodeSelect(Some(ndc_query_execution)) => { let sequence_steps = get_execution_steps( + http_client, alias, &ndc_query_execution.process_response_as, ndc_query_execution.execution_tree.remote_executions, ndc_query_execution.execution_tree.root_node.query, - ); + ndc_query_execution.execution_tree.root_node.data_connector, + ) + .await; parallel_root_steps.push(Box::new(types::Step::Sequence(sequence_steps))); } NodeQueryPlan::TypeName { .. } => { @@ -114,40 +124,58 @@ pub fn explain_query_plan( } } -fn get_execution_steps<'s>( +async fn get_execution_steps<'s>( + http_client: &reqwest::Client, alias: gql::ast::common::Alias, process_response_as: &ProcessResponseAs<'s>, join_locations: JoinLocations<(RemoteJoin<'s, '_>, JoinId)>, ndc_query_request: ndc_client::models::QueryRequest, + data_connector: &resolved::data_connector::DataConnector, ) -> NonEmpty> { let mut sequence_steps = match process_response_as { ProcessResponseAs::CommandResponse { .. } => { // A command execution node + let data_connector_explain = fetch_explain_from_data_connector( + http_client, + ndc_query_request.clone(), + data_connector, + ) + .await; NonEmpty::new(Box::new(types::Step::CommandSelect( types::CommandSelectIR { command_name: alias.to_string(), query_request: ndc_query_request, + ndc_explain: data_connector_explain, }, ))) } ProcessResponseAs::Array { .. } | ProcessResponseAs::Object { .. } => { // A model execution node + let data_connector_explain = fetch_explain_from_data_connector( + http_client, + ndc_query_request.clone(), + data_connector, + ) + .await; NonEmpty::new(Box::new(types::Step::ModelSelect(types::ModelSelectIR { model_name: alias.to_string(), query_request: ndc_query_request, + ndc_explain: data_connector_explain, }))) } }; - if let Some(join_steps) = get_join_steps(alias.to_string(), join_locations) { + if let Some(join_steps) = get_join_steps(alias.to_string(), join_locations, http_client).await { sequence_steps.push(Box::new(types::Step::Parallel(join_steps))); sequence_steps.push(Box::new(types::Step::HashJoin)); }; sequence_steps } -fn get_join_steps( +#[async_recursion] +async fn get_join_steps( _root_field_name: String, - join_locations: JoinLocations<(RemoteJoin<'_, '_>, JoinId)>, + join_locations: JoinLocations<(RemoteJoin<'async_recursion, 'async_recursion>, JoinId)>, + http_client: &reqwest::Client, ) -> Option>> { let mut parallel_join_steps = vec![]; for (alias, location) in join_locations.locations { @@ -155,24 +183,33 @@ fn get_join_steps( if let Some((remote_join, _join_id)) = location.join_node { let mut query_request = remote_join.target_ndc_ir; query_request.variables = Some(vec![]); + let data_connector_explain = fetch_explain_from_data_connector( + http_client, + query_request.clone(), + remote_join.target_data_connector, + ) + .await; sequence_steps.push(Box::new(types::Step::ForEach( + // We don't support ndc_explain for for-each steps yet match remote_join.remote_join_type { RemoteJoinType::ToModel => { types::ForEachStep::ModelSelect(types::ModelSelectIR { model_name: alias.clone(), query_request, + ndc_explain: data_connector_explain, }) } RemoteJoinType::ToCommand => { types::ForEachStep::CommandSelect(types::CommandSelectIR { command_name: alias.clone(), query_request, + ndc_explain: data_connector_explain, }) } }, ))) }; - if let Some(rest_join_steps) = get_join_steps(alias, location.rest) { + if let Some(rest_join_steps) = get_join_steps(alias, location.rest, http_client).await { sequence_steps.push(Box::new(types::Step::Parallel(rest_join_steps))); sequence_steps.push(Box::new(types::Step::HashJoin)); }; @@ -212,32 +249,74 @@ fn simplify_step(step: Box) -> Box { } } +async fn fetch_explain_from_data_connector( + http_client: &reqwest::Client, + query_request: ndc_client::models::QueryRequest, + data_connector: &resolved::data_connector::DataConnector, +) -> types::NDCExplainResponse { + let tracer = tracing_util::global_tracer(); + let response = tracer + .in_span_async( + "fetch_explain_from_data_connector", + SpanVisibility::Internal, + || { + Box::pin(async { + let ndc_config = ndc::apis::configuration::Configuration { + base_path: data_connector.url.get_url(ast::OperationType::Query), + user_agent: None, + // This is isn't expensive, reqwest::Client is behind an Arc + client: http_client.clone(), + headers: data_connector.headers.0.clone(), + }; + { + // TODO: use capabilities from the data connector context + let capabilities = + ndc::apis::default_api::capabilities_get(&ndc_config).await?; + match capabilities.capabilities.explain { + None => Ok(None), + Some(_) => { + ndc::apis::default_api::explain_post(&ndc_config, query_request) + .await + .map(Some) + .map_err(error::Error::from) // ndc_client::apis::Error -> InternalError -> Error + } + } + } + }) + }, + ) + .await; + match response { + Ok(Some(response)) => types::NDCExplainResponse::success(response), + Ok(None) => types::NDCExplainResponse::not_supported(), + Err(e) => types::NDCExplainResponse::error(e), + } +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_simplify_steps() { - let step = types::Step::HashJoin; - let simplified_steps = simplify_step(Box::new(types::Step::Parallel(nonempty::nonempty![ - Box::new(step.clone()) + Box::new(types::Step::HashJoin) ]))); - assert_eq!(*simplified_steps, step.clone()); + assert_eq!(*simplified_steps, types::Step::HashJoin); let simplified_steps = simplify_step(Box::new(types::Step::Sequence(nonempty::nonempty![ - Box::new(step.clone()) + Box::new(types::Step::HashJoin) ]))); - assert_eq!(*simplified_steps, step.clone()); + assert_eq!(*simplified_steps, types::Step::HashJoin); let nested_step = types::Step::Parallel(nonempty::nonempty![Box::new( types::Step::Sequence(nonempty::nonempty![Box::new(types::Step::Parallel( nonempty::nonempty![Box::new(types::Step::Sequence(nonempty::nonempty![ - Box::new(step.clone()) + Box::new(types::Step::HashJoin) ]))] ))]) )]); let simplified_steps = simplify_step(Box::new(nested_step)); - assert_eq!(*simplified_steps, step.clone()); + assert_eq!(*simplified_steps, types::Step::HashJoin); } } diff --git a/v3/engine/src/execute/explain/types.rs b/v3/engine/src/execute/explain/types.rs index 4900b4004746c..140b68f2b6964 100644 --- a/v3/engine/src/execute/explain/types.rs +++ b/v3/engine/src/execute/explain/types.rs @@ -1,3 +1,4 @@ +use crate::execute::error; use crate::execute::GraphQLErrors; use lang_graphql::http::GraphQLError; use nonempty::NonEmpty; @@ -15,9 +16,9 @@ pub enum RequestMode { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ExplainResponse { - pub explain: Option, + explain: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub errors: Option>, + errors: Option>, } impl Traceable for ExplainResponse { @@ -35,6 +36,10 @@ impl ExplainResponse { errors: Some(nonempty::nonempty![error]), } } + + pub fn does_contain_error(&self) -> bool { + self.errors.is_some() + } } impl axum::response::IntoResponse for ExplainResponse { @@ -51,10 +56,10 @@ impl axum::response::IntoResponse for ExplainResponse { } } -#[derive(Serialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] #[serde(tag = "type", content = "value")] -pub enum Step { +pub(crate) enum Step { ModelSelect(ModelSelectIR), CommandSelect(CommandSelectIR), ForEach(ForEachStep), @@ -64,7 +69,7 @@ pub enum Step { } impl Step { - pub fn to_explain_response(self) -> ExplainResponse { + pub(crate) fn make_explain_response(self) -> ExplainResponse { ExplainResponse { explain: Some(self), errors: None, @@ -72,24 +77,47 @@ impl Step { } } -#[derive(Serialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] -pub struct ModelSelectIR { - pub model_name: String, - pub query_request: ndc_client::models::QueryRequest, +pub(crate) struct ModelSelectIR { + pub(crate) model_name: String, + pub(crate) query_request: ndc_client::models::QueryRequest, + pub(crate) ndc_explain: NDCExplainResponse, } -#[derive(Serialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] -pub struct CommandSelectIR { - pub command_name: String, - pub query_request: ndc_client::models::QueryRequest, +pub(crate) struct CommandSelectIR { + pub(crate) command_name: String, + pub(crate) query_request: ndc_client::models::QueryRequest, + pub(crate) ndc_explain: NDCExplainResponse, } -#[derive(Serialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] #[serde(tag = "type", content = "value")] -pub enum ForEachStep { +pub(crate) enum ForEachStep { ModelSelect(ModelSelectIR), CommandSelect(CommandSelectIR), } + +#[derive(Serialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +#[serde(tag = "type", content = "value")] +pub(crate) enum NDCExplainResponse { + NotSupported, + Response(ndc_client::models::ExplainResponse), + Error(GraphQLError), +} + +impl NDCExplainResponse { + pub(crate) fn error(error: error::Error) -> Self { + Self::Error(error.to_graphql_error(None)) + } + pub(crate) fn success(response: ndc_client::models::ExplainResponse) -> Self { + Self::Response(response) + } + pub(crate) fn not_supported() -> Self { + Self::NotSupported + } +} diff --git a/v3/engine/tests/common.rs b/v3/engine/tests/common.rs index e7a820a61537d..e4ac59dbebd82 100644 --- a/v3/engine/tests/common.rs +++ b/v3/engine/tests/common.rs @@ -219,12 +219,25 @@ pub fn merge_with_common_metadata>( } #[allow(dead_code)] -pub fn test_execute_explain(metadata_file_path: &str, test_dir_path: &str) { +pub fn test_execute_explain( + test_path_string: &str, + test_metadata_path: &str, + common_metadata_paths: &[&str], +) { tokio_test::block_on(async { let root_test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests"); let mut test_ctx = setup(&root_test_dir); + let test_path = root_test_dir.join(test_path_string); + let gql_request_file_path = test_path.join("request.gql"); + let expected_response_file = test_path_string.to_string() + "/expected.json"; - let metadata = read_json(&root_test_dir.join(metadata_file_path)); + let test_metadata_path = root_test_dir.join(test_metadata_path); + let metadata = merge_with_common_metadata( + &test_metadata_path, + common_metadata_paths + .iter() + .map(|path| root_test_dir.join(path)), + ); let gds = GDS::new(serde_json::from_value(metadata).unwrap()).unwrap(); let schema = GDS::build_schema(&gds).unwrap(); let session = { @@ -235,8 +248,6 @@ pub fn test_execute_explain(metadata_file_path: &str, test_dir_path: &str) { serde_json::from_str(session_variables_raw).unwrap(); resolve_session(session_variables) }; - let gql_request_file_path = test_dir_path.to_string() + "/request.gql"; - let expected_response_file = test_dir_path.to_string() + "/expected.json"; let query = std::fs::read_to_string(root_test_dir.join(gql_request_file_path)).unwrap(); let raw_request = lang_graphql::http::RawRequest { operation_name: None, diff --git a/v3/engine/tests/execute/relationships/array/metadata.json b/v3/engine/tests/execute/relationships/array/metadata.json index 8951671e87dfc..a8380d0757601 100644 --- a/v3/engine/tests/execute/relationships/array/metadata.json +++ b/v3/engine/tests/execute/relationships/array/metadata.json @@ -82,7 +82,10 @@ "author_id" ] } - ] + ], + "selectMany": { + "queryRootField": "AuthorMany" + } }, "filterableFields": [ { diff --git a/v3/engine/tests/execute/remote_relationships/array/metadata.json b/v3/engine/tests/execute/remote_relationships/array/metadata.json index 5bf76daa08ccd..8e13a89273749 100644 --- a/v3/engine/tests/execute/remote_relationships/array/metadata.json +++ b/v3/engine/tests/execute/remote_relationships/array/metadata.json @@ -298,7 +298,10 @@ "author_id" ] } - ] + ], + "selectMany": { + "queryRootField": "AuthorMany" + } }, "filterableFields": [ { diff --git a/v3/engine/tests/explain.rs b/v3/engine/tests/explain.rs index 266288519f6c5..1abcf8a1fd93e 100644 --- a/v3/engine/tests/explain.rs +++ b/v3/engine/tests/explain.rs @@ -3,50 +3,63 @@ mod common; #[test] fn test_explain_introspection() { - common::test_execute_explain("explain/metadata.json", "explain/introspection_query/"); + common::test_execute_explain( + "explain/introspection_query/", + "explain/introspection_query/metadata.json", + &[], + ); } #[test] fn test_multi_root_field_queries() { - common::test_execute_explain("explain/metadata.json", "explain/multi_root_field_queries/"); + common::test_execute_explain( + "explain/multi_root_field_queries/", + "execute/multiple_root_fields/successful_execution/metadata.json", + &[], + ); } #[test] fn test_field_with_remote_relationship() { common::test_execute_explain( - "explain/metadata.json", "explain/field_with_remote_relationship/", + "execute/remote_relationships/array/metadata.json", + &["execute/common_metadata/two_postgres_connector_schema.json"], ); } #[test] fn test_field_with_local_relationship() { common::test_execute_explain( - "explain/metadata.json", "explain/field_with_local_relationship/", + "execute/relationships/array/metadata.json", + &["execute/common_metadata/postgres_connector_schema.json"], ); } #[test] fn test_field_with_multi_remote_relationship_subfields() { common::test_execute_explain( - "explain/metadata.json", "explain/field_with_multi_remote_relationship_subfields/", + "explain/field_with_multi_remote_relationship_subfields/metadata.json", + &["execute/common_metadata/two_postgres_connector_schema.json"], ); } #[test] fn test_field_with_nested_remote_relationship_1() { common::test_execute_explain( - "explain/metadata.json", "explain/field_with_nested_remote_relationship_1/", + "explain/field_with_nested_remote_relationship_1/metadata.json", + &["execute/common_metadata/two_postgres_connector_schema.json"], ); } #[test] fn test_field_with_nested_remote_relationship_2() { common::test_execute_explain( - "explain/metadata.json", "explain/field_with_nested_remote_relationship_2/", + "explain/field_with_nested_remote_relationship_2/metadata.json", + &["execute/common_metadata/two_postgres_connector_schema.json"], ); } diff --git a/v3/engine/tests/explain/field_with_local_relationship/expected.json b/v3/engine/tests/explain/field_with_local_relationship/expected.json index 5c7150e2577db..2d096a29b5f8e 100644 --- a/v3/engine/tests/explain/field_with_local_relationship/expected.json +++ b/v3/engine/tests/explain/field_with_local_relationship/expected.json @@ -2,45 +2,54 @@ "explain": { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "AuthorMany", "queryRequest": { - "collection": "city", + "collection": "authors", "query": { "fields": { - "code": { + "author_id": { "type": "column", - "column": "code" + "column": "id" }, - "name": { + "first_name": { "type": "column", - "column": "name" + "column": "first_name" }, - "state": { + "Articles": { "type": "relationship", "query": { "fields": { - "name": { + "title": { "type": "column", - "column": "name" + "column": "title" } } }, - "relationship": "[{\"subgraph\":\"default\",\"name\":\"city\"},\"state\"]", + "relationship": "[{\"subgraph\":\"default\",\"name\":\"author\"},\"Articles\"]", "arguments": {} } } }, "arguments": {}, "collection_relationships": { - "[{\"subgraph\":\"default\",\"name\":\"city\"},\"state\"]": { + "[{\"subgraph\":\"default\",\"name\":\"author\"},\"Articles\"]": { "column_mapping": { - "state_code": "code" + "id": "author_id" }, - "relationship_type": "object", - "target_collection": "states", + "relationship_type": "array", + "target_collection": "articles", "arguments": {} } } + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=21328.02..21328.03 rows=1 width=32)\n -> Aggregate (cost=21328.00..21328.01 rows=1 width=32)\n -> Nested Loop Left Join (cost=25.03..21323.75 rows=850 width=68)\n -> Seq Scan on author \"%0_authors\" (cost=0.00..18.50 rows=850 width=36)\n -> Subquery Scan on \"%3_rows\" (cost=25.03..25.06 rows=1 width=32)\n -> Aggregate (cost=25.03..25.04 rows=1 width=32)\n -> Seq Scan on article \"%2_articles\" (cost=0.00..25.00 rows=6 width=32)\n Filter: (\"%0_authors\".id = author_id)", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%5_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%6_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_authors\".\"id\" AS \"author_id\",\n \"%0_authors\".\"first_name\" AS \"first_name\",\n \"%1_RELATIONSHIP_Articles\".\"Articles\" AS \"Articles\"\n FROM\n \"public\".\"author\" AS \"%0_authors\"\n LEFT OUTER JOIN LATERAL (\n SELECT\n row_to_json(\"%1_RELATIONSHIP_Articles\") AS \"Articles\"\n FROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%2_articles\".\"title\" AS \"title\"\n FROM\n \"public\".\"article\" AS \"%2_articles\"\n WHERE\n (\"%0_authors\".\"id\" = \"%2_articles\".\"author_id\")\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%1_RELATIONSHIP_Articles\"\n ) AS \"%1_RELATIONSHIP_Articles\" ON ('true')\n ) AS \"%6_rows\"\n ) AS \"%6_rows\"\n ) AS \"%5_universe\"" + } + } } } } diff --git a/v3/engine/tests/explain/field_with_local_relationship/request.gql b/v3/engine/tests/explain/field_with_local_relationship/request.gql index 2f4de1bc2123b..a855f2158e1bd 100644 --- a/v3/engine/tests/explain/field_with_local_relationship/request.gql +++ b/v3/engine/tests/explain/field_with_local_relationship/request.gql @@ -1,9 +1,9 @@ { - cities { - code - name - state { - name + AuthorMany { + author_id + first_name + Articles { + title } } } diff --git a/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/expected.json b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/expected.json index 20842d565215f..06eee2405bb38 100644 --- a/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/expected.json +++ b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/expected.json @@ -5,27 +5,36 @@ { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "Album", "queryRequest": { - "collection": "city", + "collection": "Album", "query": { "fields": { - "code": { + "ArtistId": { "type": "column", - "column": "code" + "column": "ArtistId" }, - "name": { + "__hasura_phantom_field__ArtistId": { "type": "column", - "column": "name" + "column": "ArtistId" }, - "__hasura_phantom_field__code": { + "__hasura_phantom_field__AlbumId": { "type": "column", - "column": "code" + "column": "AlbumId" } } }, "arguments": {}, "collection_relationships": {} + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=13.17..13.18 rows=1 width=32)\n -> Aggregate (cost=13.15..13.16 rows=1 width=32)\n -> Seq Scan on \"Album\" \"%0_Album\" (cost=0.00..12.10 rows=210 width=8)", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%1_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%2_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_Album\".\"ArtistId\" AS \"ArtistId\",\n \"%0_Album\".\"ArtistId\" AS \"__hasura_phantom_field__ArtistId\",\n \"%0_Album\".\"AlbumId\" AS \"__hasura_phantom_field__AlbumId\"\n FROM\n \"public\".\"Album\" AS \"%0_Album\"\n ) AS \"%2_rows\"\n ) AS \"%2_rows\"\n ) AS \"%1_universe\"" + } + } } } }, @@ -37,21 +46,25 @@ "value": { "type": "modelSelect", "value": { - "modelName": "weather", + "modelName": "Artist", "queryRequest": { - "collection": "weather", + "collection": "Artist", "query": { "fields": { - "forecast": { + "ArtistId": { + "type": "column", + "column": "ArtistId" + }, + "Name": { "type": "column", - "column": "forecast" + "column": "Name" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "code", + "name": "ArtistId", "path": [] }, "operator": { @@ -59,13 +72,22 @@ }, "value": { "type": "variable", - "name": "$code" + "name": "$ArtistId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Artist\".\"ArtistId\" AS \"ArtistId\",\n \"%1_Artist\".\"Name\" AS \"Name\"\n FROM\n \"public\".\"Artist\" AS \"%1_Artist\"\n WHERE\n (\n \"%1_Artist\".\"ArtistId\" = cast(\"%0_%variables_table\".\"$ArtistId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } @@ -75,21 +97,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "census", + "modelName": "Tracks", "queryRequest": { - "collection": "census", + "collection": "Track", "query": { "fields": { - "data": { + "TrackId": { "type": "column", - "column": "data" + "column": "TrackId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "city_code", + "name": "AlbumId", "path": [] }, "operator": { @@ -97,13 +119,22 @@ }, "value": { "type": "variable", - "name": "$city_code" + "name": "$AlbumId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Track\".\"TrackId\" AS \"TrackId\"\n FROM\n \"public\".\"Track\" AS \"%1_Track\"\n WHERE\n (\n \"%1_Track\".\"AlbumId\" = cast(\"%0_%variables_table\".\"$AlbumId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } diff --git a/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/metadata.json b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/metadata.json new file mode 100644 index 0000000000000..cbd23349ab6fa --- /dev/null +++ b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/metadata.json @@ -0,0 +1,451 @@ +{ + "version": "v2", + "subgraphs": [ + { + "name": "connector_1", + "objects": [ + { + "definition": { + "name": "Artist", + "fields": [ + { + "name": "ArtistId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + } + ], + "graphql": { + "typeName": "Artist" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "name": "Track", + "fields": [ + { + "name": "TrackId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + }, + { + "name": "AlbumId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Track" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Artist", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "ArtistId", + "Name" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "typeName": "Track", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "TrackId", + "Name", + "AlbumId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Tracks", + "objectType": "Track", + "source": { + "dataConnectorName": "db", + "collection": "Track", + "typeMapping": { + "Track": { + "fieldMapping": { + "TrackId": { + "column": "TrackId" + }, + "Name": { + "column": "Name" + }, + "AlbumId": { + "column": "AlbumId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "TrackId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "TrackId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Tracks", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "name": "Artists", + "objectType": "Artist", + "source": { + "dataConnectorName": "db", + "collection": "Artist", + "typeMapping": { + "Artist": { + "fieldMapping": { + "ArtistId": { + "column": "ArtistId" + }, + "Name": { + "column": "Name" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Artists", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + } + ] + }, + { + "name": "connector_2", + "objects": [ + { + "definition": { + "name": "Album", + "fields": [ + { + "name": "AlbumId", + "type": "Int" + }, + { + "name": "Title", + "type": "String" + }, + { + "name": "ArtistId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Album" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Album", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "AlbumId", + "Title", + "ArtistId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Albums", + "objectType": "Album", + "source": { + "dataConnectorName": "db", + "collection": "Album", + "typeMapping": { + "Album": { + "fieldMapping": { + "AlbumId": { + "column": "AlbumId" + }, + "Title": { + "column": "Title" + }, + "ArtistId": { + "column": "ArtistId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ], + "selectMany": { + "queryRootField": "Album" + } + }, + "filterableFields": [ + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Albums", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "source": "Album", + "name": "Tracks", + "target": { + "model": { + "name": "Tracks", + "subgraph": "connector_1", + "relationshipType": "Array" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "AlbumId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "AlbumId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + }, + { + "definition": { + "source": "Album", + "name": "Artist", + "target": { + "model": { + "name": "Artists", + "subgraph": "connector_1", + "relationshipType": "Object" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "ArtistId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "ArtistId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + } + ] + } + ] +} \ No newline at end of file diff --git a/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/request.gql b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/request.gql index c903045813d84..5aac408425599 100644 --- a/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/request.gql +++ b/v3/engine/tests/explain/field_with_multi_remote_relationship_subfields/request.gql @@ -1,12 +1,12 @@ { - cities { - code - name - weather { - forecast + Album { + ArtistId + Artist { + ArtistId + Name } - census { - data + Tracks { + TrackId } } -} +} \ No newline at end of file diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_1/expected.json b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/expected.json index a4471cc6e1bb2..53bd0d73a219b 100644 --- a/v3/engine/tests/explain/field_with_nested_remote_relationship_1/expected.json +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/expected.json @@ -5,53 +5,62 @@ { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "Album", "queryRequest": { - "collection": "city", + "collection": "Album", "query": { "fields": { - "code": { + "AlbumId": { "type": "column", - "column": "code" + "column": "AlbumId" }, - "name": { + "ArtistId": { "type": "column", - "column": "name" + "column": "ArtistId" }, - "__hasura_phantom_field__code": { + "__hasura_phantom_field__ArtistId": { "type": "column", - "column": "code" + "column": "ArtistId" }, - "state": { + "Tracks": { "type": "relationship", "query": { "fields": { - "name": { + "TrackId": { "type": "column", - "column": "name" + "column": "TrackId" }, - "__hasura_phantom_field__code": { + "__hasura_phantom_field__AlbumId": { "type": "column", - "column": "code" + "column": "AlbumId" } } }, - "relationship": "[{\"subgraph\":\"default\",\"name\":\"city\"},\"state\"]", + "relationship": "[{\"subgraph\":\"connector_2\",\"name\":\"Album\"},\"Tracks\"]", "arguments": {} } } }, "arguments": {}, "collection_relationships": { - "[{\"subgraph\":\"default\",\"name\":\"city\"},\"state\"]": { + "[{\"subgraph\":\"connector_2\",\"name\":\"Album\"},\"Tracks\"]": { "column_mapping": { - "state_code": "code" + "AlbumId": "AlbumId" }, - "relationship_type": "object", - "target_collection": "states", + "relationship_type": "array", + "target_collection": "Track", "arguments": {} } } + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=2342.72..2342.73 rows=1 width=32)\n -> Aggregate (cost=2342.70..2342.71 rows=1 width=32)\n -> Nested Loop Left Join (cost=11.06..2341.65 rows=210 width=40)\n -> Seq Scan on \"Album\" \"%0_Album\" (cost=0.00..12.10 rows=210 width=8)\n -> Subquery Scan on \"%3_rows\" (cost=11.06..11.08 rows=1 width=32)\n -> Aggregate (cost=11.06..11.07 rows=1 width=32)\n -> Bitmap Heap Scan on \"Track\" \"%2_Track\" (cost=4.29..11.05 rows=2 width=8)\n Recheck Cond: (\"%0_Album\".\"AlbumId\" = \"AlbumId\")\n -> Bitmap Index Scan on \"IFK_TrackAlbumId\" (cost=0.00..4.29 rows=2 width=0)\n Index Cond: (\"AlbumId\" = \"%0_Album\".\"AlbumId\")", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%5_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%6_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_Album\".\"AlbumId\" AS \"AlbumId\",\n \"%0_Album\".\"ArtistId\" AS \"ArtistId\",\n \"%0_Album\".\"ArtistId\" AS \"__hasura_phantom_field__ArtistId\",\n \"%1_RELATIONSHIP_Tracks\".\"Tracks\" AS \"Tracks\"\n FROM\n \"public\".\"Album\" AS \"%0_Album\"\n LEFT OUTER JOIN LATERAL (\n SELECT\n row_to_json(\"%1_RELATIONSHIP_Tracks\") AS \"Tracks\"\n FROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%2_Track\".\"TrackId\" AS \"TrackId\",\n \"%2_Track\".\"AlbumId\" AS \"__hasura_phantom_field__AlbumId\"\n FROM\n \"public\".\"Track\" AS \"%2_Track\"\n WHERE\n (\"%0_Album\".\"AlbumId\" = \"%2_Track\".\"AlbumId\")\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%1_RELATIONSHIP_Tracks\"\n ) AS \"%1_RELATIONSHIP_Tracks\" ON ('true')\n ) AS \"%6_rows\"\n ) AS \"%6_rows\"\n ) AS \"%5_universe\"" + } + } } } }, @@ -63,21 +72,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "weather", + "modelName": "Artist", "queryRequest": { - "collection": "weather", + "collection": "Artist", "query": { "fields": { - "forecast": { + "ArtistId": { "type": "column", - "column": "forecast" + "column": "ArtistId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "code", + "name": "ArtistId", "path": [] }, "operator": { @@ -85,13 +94,22 @@ }, "value": { "type": "variable", - "name": "$code" + "name": "$ArtistId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Artist\".\"ArtistId\" AS \"ArtistId\"\n FROM\n \"public\".\"Artist\" AS \"%1_Artist\"\n WHERE\n (\n \"%1_Artist\".\"ArtistId\" = cast(\"%0_%variables_table\".\"$ArtistId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } @@ -104,21 +122,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "weather", + "modelName": "Album", "queryRequest": { - "collection": "weather", + "collection": "Album", "query": { "fields": { - "forecast": { + "AlbumId": { "type": "column", - "column": "forecast" + "column": "AlbumId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "code", + "name": "AlbumId", "path": [] }, "operator": { @@ -126,13 +144,22 @@ }, "value": { "type": "variable", - "name": "$code" + "name": "$AlbumId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Album\".\"AlbumId\" AS \"AlbumId\"\n FROM\n \"public\".\"Album\" AS \"%1_Album\"\n WHERE\n (\n \"%1_Album\".\"AlbumId\" = cast(\"%0_%variables_table\".\"$AlbumId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_1/metadata.json b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/metadata.json new file mode 100644 index 0000000000000..2a2f12e2946d3 --- /dev/null +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/metadata.json @@ -0,0 +1,612 @@ +{ + "version": "v2", + "subgraphs": [ + { + "name": "connector_1", + "objects": [ + { + "definition": { + "name": "Artist", + "fields": [ + { + "name": "ArtistId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + } + ], + "graphql": { + "typeName": "Artist" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Artist", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "ArtistId", + "Name" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Artists", + "objectType": "Artist", + "source": { + "dataConnectorName": "db", + "collection": "Artist", + "typeMapping": { + "Artist": { + "fieldMapping": { + "ArtistId": { + "column": "ArtistId" + }, + "Name": { + "column": "Name" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Artists", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "name": "Album", + "fields": [ + { + "name": "AlbumId", + "type": "Int" + }, + { + "name": "Title", + "type": "String" + }, + { + "name": "ArtistId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Album_2" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Album", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "AlbumId", + "Title", + "ArtistId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Albums", + "objectType": "Album", + "source": { + "dataConnectorName": "db", + "collection": "Album", + "typeMapping": { + "Album": { + "fieldMapping": { + "AlbumId": { + "column": "AlbumId" + }, + "Title": { + "column": "Title" + }, + "ArtistId": { + "column": "ArtistId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Albums", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + } + ] + }, + { + "name": "connector_2", + "objects": [ + { + "definition": { + "name": "Album", + "fields": [ + { + "name": "AlbumId", + "type": "Int" + }, + { + "name": "Title", + "type": "String" + }, + { + "name": "ArtistId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Album" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Album", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "AlbumId", + "Title", + "ArtistId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Albums", + "objectType": "Album", + "source": { + "dataConnectorName": "db", + "collection": "Album", + "typeMapping": { + "Album": { + "fieldMapping": { + "AlbumId": { + "column": "AlbumId" + }, + "Title": { + "column": "Title" + }, + "ArtistId": { + "column": "ArtistId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ], + "selectMany": { + "queryRootField": "Album" + } + }, + "filterableFields": [ + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Albums", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "name": "Track", + "fields": [ + { + "name": "TrackId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + }, + { + "name": "AlbumId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Track" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Track", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "TrackId", + "Name", + "AlbumId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Tracks", + "objectType": "Track", + "source": { + "dataConnectorName": "db", + "collection": "Track", + "typeMapping": { + "Track": { + "fieldMapping": { + "TrackId": { + "column": "TrackId" + }, + "Name": { + "column": "Name" + }, + "AlbumId": { + "column": "AlbumId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "TrackId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "TrackId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Tracks", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "source": "Album", + "name": "Tracks", + "target": { + "model": { + "name": "Tracks", + "relationshipType": "Array" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "AlbumId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "AlbumId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + }, + { + "definition": { + "source": "Track", + "name": "Album", + "target": { + "model": { + "name": "Albums", + "subgraph": "connector_1", + "relationshipType": "Object" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "AlbumId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "AlbumId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + }, + { + "definition": { + "source": "Album", + "name": "Artist", + "target": { + "model": { + "name": "Artists", + "subgraph": "connector_1", + "relationshipType": "Object" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "ArtistId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "ArtistId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + } + ] + } + ] +} \ No newline at end of file diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_1/request.gql b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/request.gql index 8dd410b587867..a495cb9cbbdeb 100644 --- a/v3/engine/tests/explain/field_with_nested_remote_relationship_1/request.gql +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_1/request.gql @@ -1,14 +1,14 @@ { - cities { - code - name - weather { - forecast + Album { + AlbumId + ArtistId + Artist { + ArtistId } - state { - name - weather { - forecast + Tracks { + TrackId + Album { + AlbumId } } } diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_2/expected.json b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/expected.json index 9e44552ed1cd1..9270506838adc 100644 --- a/v3/engine/tests/explain/field_with_nested_remote_relationship_2/expected.json +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/expected.json @@ -5,27 +5,40 @@ { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "Album", "queryRequest": { - "collection": "city", + "collection": "Album", "query": { "fields": { - "code": { + "AlbumId": { "type": "column", - "column": "code" + "column": "AlbumId" }, - "name": { + "ArtistId": { "type": "column", - "column": "name" + "column": "ArtistId" }, - "__hasura_phantom_field__code": { + "__hasura_phantom_field__AlbumId": { "type": "column", - "column": "code" + "column": "AlbumId" + }, + "__hasura_phantom_field__ArtistId": { + "type": "column", + "column": "ArtistId" } } }, "arguments": {}, "collection_relationships": {} + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=13.17..13.18 rows=1 width=32)\n -> Aggregate (cost=13.15..13.16 rows=1 width=32)\n -> Seq Scan on \"Album\" \"%0_Album\" (cost=0.00..12.10 rows=210 width=8)", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%1_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%2_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_Album\".\"AlbumId\" AS \"AlbumId\",\n \"%0_Album\".\"ArtistId\" AS \"ArtistId\",\n \"%0_Album\".\"AlbumId\" AS \"__hasura_phantom_field__AlbumId\",\n \"%0_Album\".\"ArtistId\" AS \"__hasura_phantom_field__ArtistId\"\n FROM\n \"public\".\"Album\" AS \"%0_Album\"\n ) AS \"%2_rows\"\n ) AS \"%2_rows\"\n ) AS \"%1_universe\"" + } + } } } }, @@ -40,25 +53,25 @@ "value": { "type": "modelSelect", "value": { - "modelName": "departments", + "modelName": "Tracks", "queryRequest": { - "collection": "departments", + "collection": "Track", "query": { "fields": { - "name": { + "TrackId": { "type": "column", - "column": "name" + "column": "TrackId" }, - "__hasura_phantom_field__id": { + "__hasura_phantom_field__AlbumId": { "type": "column", - "column": "id" + "column": "AlbumId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "city_code", + "name": "AlbumId", "path": [] }, "operator": { @@ -66,13 +79,22 @@ }, "value": { "type": "variable", - "name": "$city_code" + "name": "$AlbumId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Track\".\"TrackId\" AS \"TrackId\",\n \"%1_Track\".\"AlbumId\" AS \"__hasura_phantom_field__AlbumId\"\n FROM\n \"public\".\"Track\" AS \"%1_Track\"\n WHERE\n (\n \"%1_Track\".\"AlbumId\" = cast(\"%0_%variables_table\".\"$AlbumId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } @@ -82,21 +104,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "ministers", + "modelName": "Album", "queryRequest": { - "collection": "ministers", + "collection": "Album", "query": { "fields": { - "name": { + "AlbumId": { "type": "column", - "column": "name" + "column": "AlbumId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "department_id", + "name": "AlbumId", "path": [] }, "operator": { @@ -104,13 +126,22 @@ }, "value": { "type": "variable", - "name": "$department_id" + "name": "$AlbumId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Album\".\"AlbumId\" AS \"AlbumId\"\n FROM\n \"public\".\"Album\" AS \"%1_Album\"\n WHERE\n (\n \"%1_Album\".\"AlbumId\" = cast(\"%0_%variables_table\".\"$AlbumId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } @@ -125,21 +156,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "census", + "modelName": "Artist", "queryRequest": { - "collection": "census", + "collection": "Artist", "query": { "fields": { - "data": { + "ArtistId": { "type": "column", - "column": "data" + "column": "ArtistId" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "city_code", + "name": "ArtistId", "path": [] }, "operator": { @@ -147,13 +178,22 @@ }, "value": { "type": "variable", - "name": "$city_code" + "name": "$ArtistId" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_Artist\".\"ArtistId\" AS \"ArtistId\"\n FROM\n \"public\".\"Artist\" AS \"%1_Artist\"\n WHERE\n (\n \"%1_Artist\".\"ArtistId\" = cast(\"%0_%variables_table\".\"$ArtistId\" as int4)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_2/metadata.json b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/metadata.json new file mode 100644 index 0000000000000..a56df7104de5c --- /dev/null +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/metadata.json @@ -0,0 +1,613 @@ +{ + "version": "v2", + "subgraphs": [ + { + "name": "connector_1", + "objects": [ + { + "definition": { + "name": "Artist", + "fields": [ + { + "name": "ArtistId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + } + ], + "graphql": { + "typeName": "Artist" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Artist", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "ArtistId", + "Name" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Artists", + "objectType": "Artist", + "source": { + "dataConnectorName": "db", + "collection": "Artist", + "typeMapping": { + "Artist": { + "fieldMapping": { + "ArtistId": { + "column": "ArtistId" + }, + "Name": { + "column": "Name" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Artists", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "name": "Album", + "fields": [ + { + "name": "AlbumId", + "type": "Int" + }, + { + "name": "Title", + "type": "String" + }, + { + "name": "ArtistId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Album_2" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Album", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "AlbumId", + "Title", + "ArtistId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Albums", + "objectType": "Album", + "source": { + "dataConnectorName": "db", + "collection": "Album", + "typeMapping": { + "Album": { + "fieldMapping": { + "AlbumId": { + "column": "AlbumId" + }, + "Title": { + "column": "Title" + }, + "ArtistId": { + "column": "ArtistId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Albums", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "name": "Track", + "fields": [ + { + "name": "TrackId", + "type": "Int" + }, + { + "name": "Name", + "type": "String" + }, + { + "name": "AlbumId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Track" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Track", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "TrackId", + "Name", + "AlbumId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Tracks", + "objectType": "Track", + "source": { + "dataConnectorName": "db", + "collection": "Track", + "typeMapping": { + "Track": { + "fieldMapping": { + "TrackId": { + "column": "TrackId" + }, + "Name": { + "column": "Name" + }, + "AlbumId": { + "column": "AlbumId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ] + }, + "filterableFields": [ + { + "fieldName": "TrackId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "TrackId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Name", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Tracks", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "source": "Track", + "name": "Album", + "target": { + "model": { + "name": "Albums", + "subgraph": "connector_2", + "relationshipType": "Object" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "AlbumId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "AlbumId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + } + ] + }, + { + "name": "connector_2", + "objects": [ + { + "definition": { + "name": "Album", + "fields": [ + { + "name": "AlbumId", + "type": "Int" + }, + { + "name": "Title", + "type": "String" + }, + { + "name": "ArtistId", + "type": "Int" + } + ], + "graphql": { + "typeName": "Album" + } + }, + "version": "v1", + "kind": "ObjectType" + }, + { + "definition": { + "typeName": "Album", + "permissions": [ + { + "role": "admin", + "output": { + "allowedFields": [ + "AlbumId", + "Title", + "ArtistId" + ] + } + } + ] + }, + "version": "v1", + "kind": "TypePermissions" + }, + { + "definition": { + "name": "Albums", + "objectType": "Album", + "source": { + "dataConnectorName": "db", + "collection": "Album", + "typeMapping": { + "Album": { + "fieldMapping": { + "AlbumId": { + "column": "AlbumId" + }, + "Title": { + "column": "Title" + }, + "ArtistId": { + "column": "ArtistId" + } + } + } + } + }, + "graphql": { + "selectUniques": [ + ], + "selectMany": { + "queryRootField": "Album" + } + }, + "filterableFields": [ + { + "fieldName": "AlbumId", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "operators": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "operators": { + "enableAll": true + } + } + ], + "orderableFields": [ + { + "fieldName": "AlbumId", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "Title", + "orderByDirections": { + "enableAll": true + } + }, + { + "fieldName": "ArtistId", + "orderByDirections": { + "enableAll": true + } + } + ] + }, + "version": "v1", + "kind": "Model" + }, + { + "definition": { + "modelName": "Albums", + "permissions": [ + { + "role": "admin", + "select": { + "filter": null + } + } + ] + }, + "version": "v1", + "kind": "ModelPermissions" + }, + { + "definition": { + "source": "Album", + "name": "Tracks", + "target": { + "model": { + "name": "Tracks", + "subgraph": "connector_1", + "relationshipType": "Array" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "AlbumId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "AlbumId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + }, + { + "definition": { + "source": "Album", + "name": "Artist", + "target": { + "model": { + "name": "Artists", + "subgraph": "connector_1", + "relationshipType": "Object" + } + }, + "mapping": [ + { + "source": { + "fieldPath": [ + { + "fieldName": "ArtistId" + } + ] + }, + "target": { + "modelField": [ + { + "fieldName": "ArtistId" + } + ] + } + } + ] + }, + "version": "v1", + "kind": "Relationship" + } + ] + } + ] +} \ No newline at end of file diff --git a/v3/engine/tests/explain/field_with_nested_remote_relationship_2/request.gql b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/request.gql index b395b5a20bfae..2d41225f49b09 100644 --- a/v3/engine/tests/explain/field_with_nested_remote_relationship_2/request.gql +++ b/v3/engine/tests/explain/field_with_nested_remote_relationship_2/request.gql @@ -1,15 +1,15 @@ { - cities { - code - name - departments { - name - ministers { - name + Album { + AlbumId + ArtistId + Tracks { + TrackId + Album { + AlbumId } } - census { - data + Artist { + ArtistId } } } diff --git a/v3/engine/tests/explain/field_with_remote_relationship/expected.json b/v3/engine/tests/explain/field_with_remote_relationship/expected.json index 15c7077bfa140..abf172733f9fc 100644 --- a/v3/engine/tests/explain/field_with_remote_relationship/expected.json +++ b/v3/engine/tests/explain/field_with_remote_relationship/expected.json @@ -5,27 +5,36 @@ { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "AuthorMany", "queryRequest": { - "collection": "city", + "collection": "authors", "query": { "fields": { - "code": { + "author_id": { "type": "column", - "column": "code" + "column": "id" }, - "name": { + "first_name": { "type": "column", - "column": "name" + "column": "first_name" }, - "__hasura_phantom_field__code": { + "__hasura_phantom_field__id": { "type": "column", - "column": "code" + "column": "id" } } }, "arguments": {}, "collection_relationships": {} + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=22.77..22.78 rows=1 width=32)\n -> Aggregate (cost=22.75..22.76 rows=1 width=32)\n -> Seq Scan on author \"%0_authors\" (cost=0.00..18.50 rows=850 width=36)", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%1_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%2_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_authors\".\"id\" AS \"author_id\",\n \"%0_authors\".\"first_name\" AS \"first_name\",\n \"%0_authors\".\"id\" AS \"__hasura_phantom_field__id\"\n FROM\n \"public\".\"author\" AS \"%0_authors\"\n ) AS \"%2_rows\"\n ) AS \"%2_rows\"\n ) AS \"%1_universe\"" + } + } } } }, @@ -34,21 +43,21 @@ "value": { "type": "modelSelect", "value": { - "modelName": "weather", + "modelName": "Articles", "queryRequest": { - "collection": "weather", + "collection": "articles", "query": { "fields": { - "forecast": { + "title": { "type": "column", - "column": "forecast" + "column": "title" } }, "where": { "type": "binary_comparison_operator", "column": { "type": "column", - "name": "code", + "name": "author_id", "path": [] }, "operator": { @@ -56,13 +65,22 @@ }, "value": { "type": "variable", - "name": "$code" + "name": "$author_id" } } }, "arguments": {}, "collection_relationships": {}, "variables": [] + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(\"%5_universe_agg\".\"universe\"), '[]') AS \"universe\"\nFROM\n (\n SELECT\n row_to_json(\"%2_universe\") AS \"universe\"\n FROM\n json_to_recordset(cast($1 as json)) AS \"%0_%variables_table\"(\"%variable_order\" int)\n CROSS JOIN LATERAL (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%3_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%1_articles\".\"title\" AS \"title\"\n FROM\n \"public\".\"article\" AS \"%1_articles\"\n WHERE\n (\n \"%1_articles\".\"author_id\" = cast(\"%0_%variables_table\".\"$author_id\" as integer)\n )\n ) AS \"%3_rows\"\n ) AS \"%3_rows\"\n ) AS \"%2_universe\"\n ORDER BY\n \"%0_%variables_table\".\"%variable_order\" ASC\n ) AS \"%5_universe_agg\"" + } + } } } } diff --git a/v3/engine/tests/explain/field_with_remote_relationship/request.gql b/v3/engine/tests/explain/field_with_remote_relationship/request.gql index cad8d3786087b..a855f2158e1bd 100644 --- a/v3/engine/tests/explain/field_with_remote_relationship/request.gql +++ b/v3/engine/tests/explain/field_with_remote_relationship/request.gql @@ -1,9 +1,9 @@ { - cities { - code - name - weather { - forecast + AuthorMany { + author_id + first_name + Articles { + title } } } diff --git a/v3/engine/tests/explain/introspection_query/metadata.json b/v3/engine/tests/explain/introspection_query/metadata.json new file mode 100644 index 0000000000000..3b4d3c170ce00 --- /dev/null +++ b/v3/engine/tests/explain/introspection_query/metadata.json @@ -0,0 +1,15 @@ +{ + "version": "v2", + "supergraph": { + "objects": [] + }, + "subgraphs": [ + { + "name": "default", + "objects": [] + } + ], + "flags": { + "require_ndc_capabilities": false + } +} diff --git a/v3/engine/tests/explain/metadata.json b/v3/engine/tests/explain/metadata.json deleted file mode 100644 index 2feb66a06a7ee..0000000000000 --- a/v3/engine/tests/explain/metadata.json +++ /dev/null @@ -1,2666 +0,0 @@ -{ - "version": "v2", - "supergraph": { - "objects": [] - }, - "subgraphs": [ - { - "name": "default", - "objects": [ - { - "kind": "DataConnector", - "version": "v2", - "definition": { - "name": "default", - "url": { - "singleUrl": { - "value": "http://fake.url" - } - }, - "schema": { - "scalar_types": { - "int4": { - "aggregate_functions": { - "bit_and": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_or": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_xor": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "max": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "int4" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "int4" - } - } - } - }, - "text": { - "aggregate_functions": { - "max": { - "result_type": { - "type": "named", - "name": "text" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "text" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_ilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_iregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_like": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_niregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nlike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_regex": { - "argument_type": { - "type": "named", - "name": "text" - } - } - } - }, - "uuid": { - "aggregate_functions": {}, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - } - } - } - }, - "object_types": { - "city": { - "fields": { - "code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "name": { - "type": { - "type": "named", - "name": "text" - } - }, - "state_code": { - "type": { - "type": "nullable", - "underlying_type": { - "type": "named", - "name": "int4" - } - } - } - } - }, - "country": { - "fields": { - "code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "name": { - "type": { - "type": "nullable", - "underlying_type": { - "type": "named", - "name": "text" - } - } - } - } - }, - "states": { - "fields": { - "code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "name": { - "type": { - "type": "named", - "name": "text" - } - } - } - } - }, - "collections": [ - { - "name": "city", - "arguments": {}, - "type": "city", - "uniqueness_constraints": { - "city_pkey": { - "unique_columns": [ - "code" - ] - } - }, - "foreign_keys": { - "city_state_code_fkey": { - "column_mapping": { - "state_code": "code" - }, - "foreign_collection": "states" - } - } - }, - { - "name": "country", - "arguments": {}, - "type": "country", - "uniqueness_constraints": { - "country_pkey": { - "unique_columns": [ - "code" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "states", - "arguments": {}, - "type": "states", - "uniqueness_constraints": { - "states_pkey": { - "unique_columns": [ - "code" - ] - } - }, - "foreign_keys": {} - } - ], - "functions": [], - "procedures": [] - }, - "capabilities": { - "versions": "^0.1.0", - "capabilities": { - "query": { - "aggregates": {}, - "variables": {} - }, - "explain": {}, - "relationships": { - "relation_comparisons": {}, - "order_by_aggregate": {} - } - } - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "default_int4", - "graphql": { - "typeName": "default_int4" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "default", - "dataConnectorScalarType": "int4", - "representation": "default_int4", - "graphql": { - "comparisonExpressionTypeName": "default_int4_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "default_text", - "graphql": { - "typeName": "default_text" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "default", - "dataConnectorScalarType": "text", - "representation": "default_text", - "graphql": { - "comparisonExpressionTypeName": "default_text_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "default_uuid", - "graphql": { - "typeName": "default_uuid" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "default", - "dataConnectorScalarType": "uuid", - "representation": "default_uuid", - "graphql": { - "comparisonExpressionTypeName": "default_uuid_comparison" - } - } - }, - { - "kind": "DataConnector", - "version": "v2", - "definition": { - "name": "remote", - "url": { - "singleUrl": { - "value": "http://fake.url" - } - }, - "schema": { - "scalar_types": { - "int4": { - "aggregate_functions": { - "bit_and": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_or": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_xor": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "max": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "int4" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "int4" - } - } - } - }, - "text": { - "aggregate_functions": { - "max": { - "result_type": { - "type": "named", - "name": "text" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "text" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_ilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_iregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_like": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_niregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nlike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_regex": { - "argument_type": { - "type": "named", - "name": "text" - } - } - } - }, - "uuid": { - "aggregate_functions": {}, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - } - } - } - }, - "object_types": { - "census": { - "fields": { - "city_code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "data": { - "type": { - "type": "named", - "name": "uuid" - } - } - } - }, - "weather": { - "fields": { - "code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "forecast": { - "type": { - "type": "named", - "name": "text" - } - } - } - } - }, - "collections": [ - { - "name": "census", - "arguments": {}, - "type": "census", - "uniqueness_constraints": { - "census_pkey": { - "unique_columns": [ - "city_code" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "weather", - "arguments": {}, - "type": "weather", - "uniqueness_constraints": { - "weather_pkey": { - "unique_columns": [ - "code" - ] - } - }, - "foreign_keys": {} - } - ], - "functions": [], - "procedures": [] - }, - "capabilities": { - "versions": "^0.1.0", - "capabilities": { - "query": { - "aggregates": {}, - "variables": {} - }, - "explain": {}, - "relationships": { - "relation_comparisons": {}, - "order_by_aggregate": {} - } - } - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_int4", - "graphql": { - "typeName": "remote_int4" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote", - "dataConnectorScalarType": "int4", - "representation": "remote_int4", - "graphql": { - "comparisonExpressionTypeName": "remote_int4_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_text", - "graphql": { - "typeName": "remote_text" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote", - "dataConnectorScalarType": "text", - "representation": "remote_text", - "graphql": { - "comparisonExpressionTypeName": "remote_text_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_uuid", - "graphql": { - "typeName": "remote_uuid" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote", - "dataConnectorScalarType": "uuid", - "representation": "remote_uuid", - "graphql": { - "comparisonExpressionTypeName": "remote_uuid_comparison" - } - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "city", - "fields": [ - { - "name": "code", - "type": "default_int4!" - }, - { - "name": "name", - "type": "default_text!" - }, - { - "name": "state_code", - "type": "default_int4" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "city", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "city", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "code", - "name", - "state_code" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "city", - "objectType": "city", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "default", - "collection": "city", - "typeMapping": { - "city": { - "fieldMapping": { - "state_code": { - "column": "state_code" - }, - "code": { - "column": "code" - }, - "name": { - "column": "name" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "name", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "state_code", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "name", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "state_code", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "city_by_code", - "uniqueIdentifier": [ - "code" - ] - } - ], - "selectMany": { - "queryRootField": "cities" - }, - "argumentsInputType": null, - "filterExpressionType": "city_bool_exp", - "orderByExpressionType": "city_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "city", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "country", - "fields": [ - { - "name": "code", - "type": "default_int4!" - }, - { - "name": "name", - "type": "default_text" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "country", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "country", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "name", - "code" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "country", - "objectType": "country", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "default", - "collection": "country", - "typeMapping": { - "country": { - "fieldMapping": { - "name": { - "column": "name" - }, - "code": { - "column": "code" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "name", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "name", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "country_by_code", - "uniqueIdentifier": [ - "code" - ] - } - ], - "selectMany": { - "queryRootField": "countries" - }, - "argumentsInputType": null, - "filterExpressionType": "country_bool_exp", - "orderByExpressionType": "country_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "country", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "states", - "fields": [ - { - "name": "code", - "type": "default_int4!" - }, - { - "name": "name", - "type": "default_text!" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "states", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "states", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "code", - "name" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "states", - "objectType": "states", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "default", - "collection": "states", - "typeMapping": { - "states": { - "fieldMapping": { - "code": { - "column": "code" - }, - "name": { - "column": "name" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "name", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "name", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "states_by_code", - "uniqueIdentifier": [ - "code" - ] - } - ], - "selectMany": { - "queryRootField": "states" - }, - "argumentsInputType": null, - "filterExpressionType": "states_bool_exp", - "orderByExpressionType": "states_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "states", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "census", - "fields": [ - { - "name": "city_code", - "type": "remote_int4!" - }, - { - "name": "data", - "type": "remote_uuid!" - } - ], - "globalIdFields": [ - "city_code" - ], - "graphql": { - "typeName": "census", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "census", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "city_code", - "data" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "census", - "objectType": "census", - "globalIdSource": true, - "arguments": [], - "source": { - "dataConnectorName": "remote", - "collection": "census", - "typeMapping": { - "census": { - "fieldMapping": { - "city_code": { - "column": "city_code" - }, - "data": { - "column": "data" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "city_code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "data", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "city_code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "data", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "census_by_city_code", - "uniqueIdentifier": [ - "city_code" - ] - } - ], - "selectMany": { - "queryRootField": "census" - }, - "argumentsInputType": null, - "filterExpressionType": "census_bool_exp", - "orderByExpressionType": "census_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "census", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "weather", - "fields": [ - { - "name": "code", - "type": "remote_int4!" - }, - { - "name": "forecast", - "type": "remote_text!" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "weather", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "weather", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "forecast", - "code" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "weather", - "objectType": "weather", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "remote", - "collection": "weather", - "typeMapping": { - "weather": { - "fieldMapping": { - "code": { - "column": "code" - }, - "forecast": { - "column": "forecast" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "forecast", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "forecast", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "weather_by_code", - "uniqueIdentifier": [ - "code" - ] - } - ], - "selectMany": { - "queryRootField": "weathers" - }, - "argumentsInputType": null, - "filterExpressionType": "weather_bool_exp", - "orderByExpressionType": "weather_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "weather", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "state", - "source": "city", - "target": { - "model": { - "name": "states", - "namespace": null, - "subgraph": null, - "relationshipType": "Object" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "state_code" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "code" - } - ] - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "weather", - "source": "city", - "target": { - "model": { - "name": "weather", - "namespace": null, - "subgraph": null, - "relationshipType": "Object" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "code" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "code" - } - ] - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "census", - "source": "city", - "target": { - "model": { - "name": "census", - "namespace": null, - "subgraph": null, - "relationshipType": "Object" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "code" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "city_code" - } - ] - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "weather", - "source": "states", - "target": { - "model": { - "name": "weather", - "namespace": null, - "subgraph": null, - "relationshipType": "Object" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "code" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "code" - } - ] - } - } - ] - } - }, - { - "kind": "DataConnector", - "version": "v2", - "definition": { - "name": "remote_2", - "url": { - "singleUrl": { - "value": "http://fake.url" - } - }, - "schema": { - "scalar_types": { - "int4": { - "aggregate_functions": { - "bit_and": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_or": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_xor": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "max": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "int4" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "int4" - } - } - } - }, - "text": { - "aggregate_functions": { - "max": { - "result_type": { - "type": "named", - "name": "text" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "text" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_ilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_iregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_like": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_niregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nlike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_regex": { - "argument_type": { - "type": "named", - "name": "text" - } - } - } - }, - "uuid": { - "aggregate_functions": {}, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - } - } - } - }, - "object_types": { - "departments": { - "fields": { - "city_code": { - "type": { - "type": "named", - "name": "int4" - } - }, - "id": { - "type": { - "type": "named", - "name": "int4" - } - }, - "name": { - "type": { - "type": "named", - "name": "text" - } - } - } - } - }, - "collections": [ - { - "name": "departments", - "arguments": {}, - "type": "departments", - "uniqueness_constraints": { - "departments_pkey": { - "unique_columns": [ - "id" - ] - } - }, - "foreign_keys": {} - } - ], - "functions": [], - "procedures": [] - }, - "capabilities": { - "versions": "^0.1.0", - "capabilities": { - "query": { - "aggregates": {}, - "variables": {} - }, - "explain": {}, - "relationships": { - "relation_comparisons": {}, - "order_by_aggregate": {} - } - } - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_2_int4", - "graphql": { - "typeName": "remote_2_int4" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_2", - "dataConnectorScalarType": "int4", - "representation": "remote_2_int4", - "graphql": { - "comparisonExpressionTypeName": "remote_2_int4_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_2_text", - "graphql": { - "typeName": "remote_2_text" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_2", - "dataConnectorScalarType": "text", - "representation": "remote_2_text", - "graphql": { - "comparisonExpressionTypeName": "remote_2_text_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_2_uuid", - "graphql": { - "typeName": "remote_2_uuid" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_2", - "dataConnectorScalarType": "uuid", - "representation": "remote_2_uuid", - "graphql": { - "comparisonExpressionTypeName": "remote_2_uuid_comparison" - } - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "departments", - "fields": [ - { - "name": "city_code", - "type": "remote_2_int4!" - }, - { - "name": "id", - "type": "remote_2_int4!" - }, - { - "name": "name", - "type": "remote_2_text!" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "departments", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "departments", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "id", - "name", - "city_code" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "departments", - "objectType": "departments", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "remote_2", - "collection": "departments", - "typeMapping": { - "departments": { - "fieldMapping": { - "name": { - "column": "name" - }, - "id": { - "column": "id" - }, - "city_code": { - "column": "city_code" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "city_code", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "id", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "name", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "city_code", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "id", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "name", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "departments_by_id", - "uniqueIdentifier": [ - "id" - ] - } - ], - "selectMany": { - "queryRootField": "departments" - }, - "argumentsInputType": null, - "filterExpressionType": "departments_bool_exp", - "orderByExpressionType": "departments_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "departments", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "DataConnector", - "version": "v2", - "definition": { - "name": "remote_3", - "url": { - "singleUrl": { - "value": "http://fake.url" - } - }, - "schema": { - "scalar_types": { - "int4": { - "aggregate_functions": { - "bit_and": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_or": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "bit_xor": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "max": { - "result_type": { - "type": "named", - "name": "int4" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "int4" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "int4" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "int4" - } - } - } - }, - "text": { - "aggregate_functions": { - "max": { - "result_type": { - "type": "named", - "name": "text" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "text" - } - } - }, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_ilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_iregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_like": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nilike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_niregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nlike": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_nregex": { - "argument_type": { - "type": "named", - "name": "text" - } - }, - "_regex": { - "argument_type": { - "type": "named", - "name": "text" - } - } - } - }, - "uuid": { - "aggregate_functions": {}, - "comparison_operators": { - "_eq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_gte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lt": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_lte": { - "argument_type": { - "type": "named", - "name": "uuid" - } - }, - "_neq": { - "argument_type": { - "type": "named", - "name": "uuid" - } - } - } - } - }, - "object_types": { - "ministers": { - "fields": { - "department_id": { - "type": { - "type": "named", - "name": "int4" - } - }, - "id": { - "type": { - "type": "named", - "name": "int4" - } - }, - "name": { - "type": { - "type": "named", - "name": "text" - } - } - } - } - }, - "collections": [ - { - "name": "ministers", - "arguments": {}, - "type": "ministers", - "uniqueness_constraints": { - "ministers_pkey": { - "unique_columns": [ - "id" - ] - } - }, - "foreign_keys": {} - } - ], - "functions": [], - "procedures": [] - }, - "capabilities": { - "versions": "^0.1.0", - "capabilities": { - "query": { - "aggregates": {}, - "variables": {} - }, - "explain": {}, - "relationships": { - "relation_comparisons": {}, - "order_by_aggregate": {} - } - } - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_3_int4", - "graphql": { - "typeName": "remote_3_int4" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_3", - "dataConnectorScalarType": "int4", - "representation": "remote_3_int4", - "graphql": { - "comparisonExpressionTypeName": "remote_3_int4_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_3_text", - "graphql": { - "typeName": "remote_3_text" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_3", - "dataConnectorScalarType": "text", - "representation": "remote_3_text", - "graphql": { - "comparisonExpressionTypeName": "remote_3_text_comparison" - } - } - }, - { - "kind": "ScalarType", - "version": "v1", - "definition": { - "name": "remote_3_uuid", - "graphql": { - "typeName": "remote_3_uuid" - } - } - }, - { - "kind": "DataConnectorScalarRepresentation", - "version": "v1", - "definition": { - "dataConnectorName": "remote_3", - "dataConnectorScalarType": "uuid", - "representation": "remote_3_uuid", - "graphql": { - "comparisonExpressionTypeName": "remote_3_uuid_comparison" - } - } - }, - { - "kind": "ObjectType", - "version": "v1", - "definition": { - "name": "ministers", - "fields": [ - { - "name": "department_id", - "type": "remote_3_int4!" - }, - { - "name": "id", - "type": "remote_3_int4!" - }, - { - "name": "name", - "type": "remote_3_text!" - } - ], - "globalIdFields": null, - "graphql": { - "typeName": "ministers", - "inputTypeName": null - } - } - }, - { - "kind": "TypePermissions", - "version": "v1", - "definition": { - "typeName": "ministers", - "permissions": [ - { - "role": "admin", - "output": { - "allowedFields": [ - "department_id", - "name", - "id" - ] - } - } - ] - } - }, - { - "kind": "Model", - "version": "v1", - "definition": { - "name": "ministers", - "objectType": "ministers", - "globalIdSource": false, - "arguments": [], - "source": { - "dataConnectorName": "remote_3", - "collection": "ministers", - "typeMapping": { - "ministers": { - "fieldMapping": { - "id": { - "column": "id" - }, - "name": { - "column": "name" - }, - "department_id": { - "column": "department_id" - } - } - } - }, - "argumentMapping": {} - }, - "filterableFields": [ - { - "fieldName": "department_id", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "id", - "operators": { - "enableAll": true - } - }, - { - "fieldName": "name", - "operators": { - "enableAll": true - } - } - ], - "orderableFields": [ - { - "fieldName": "department_id", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "id", - "orderByDirections": { - "enableAll": true - } - }, - { - "fieldName": "name", - "orderByDirections": { - "enableAll": true - } - } - ], - "graphql": { - "selectUniques": [ - { - "queryRootField": "ministers_by_id", - "uniqueIdentifier": [ - "id" - ] - } - ], - "selectMany": { - "queryRootField": "ministers" - }, - "argumentsInputType": null, - "filterExpressionType": "ministers_bool_exp", - "orderByExpressionType": "ministers_order_by" - } - } - }, - { - "kind": "ModelPermissions", - "version": "v1", - "definition": { - "modelName": "ministers", - "permissions": [ - { - "role": "admin", - "select": { - "filter": null - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "departments", - "source": "city", - "target": { - "model": { - "name": "departments", - "namespace": null, - "subgraph": null, - "relationshipType": "Array" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "code" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "city_code" - } - ] - } - } - ] - } - }, - { - "kind": "Relationship", - "version": "v1", - "definition": { - "name": "ministers", - "source": "departments", - "target": { - "model": { - "name": "ministers", - "namespace": null, - "subgraph": null, - "relationshipType": "Array" - } - }, - "mapping": [ - { - "source": { - "fieldPath": [ - { - "fieldName": "id" - } - ] - }, - "target": { - "modelField": [ - { - "fieldName": "department_id" - } - ] - } - } - ] - } - } - ] - } - ], - "flags": { - "require_ndc_capabilities": false - } -} diff --git a/v3/engine/tests/explain/multi_root_field_queries/expected.json b/v3/engine/tests/explain/multi_root_field_queries/expected.json index 176c318ca7e3d..8ac7a2070e370 100644 --- a/v3/engine/tests/explain/multi_root_field_queries/expected.json +++ b/v3/engine/tests/explain/multi_root_field_queries/expected.json @@ -2,49 +2,100 @@ "explain": { "type": "parallel", "value": [ + { + "type": "commandSelect", + "value": { + "commandName": "getActorById", + "queryRequest": { + "collection": "get_actor_by_id", + "query": { + "fields": { + "name": { + "type": "column", + "column": "name" + } + } + }, + "arguments": { + "id": { + "type": "literal", + "value": 1 + } + }, + "collection_relationships": {} + }, + "ndcExplain": { + "type": "notSupported" + } + } + }, { "type": "modelSelect", "value": { - "modelName": "cities", + "modelName": "AuthorMany", "queryRequest": { - "collection": "city", + "collection": "authors", "query": { "fields": { - "code": { + "author_id": { "type": "column", - "column": "code" + "column": "id" }, - "name": { + "first_name": { "type": "column", - "column": "name" + "column": "first_name" } + }, + "order_by": { + "elements": [ + { + "order_direction": "desc", + "target": { + "type": "column", + "name": "id", + "path": [] + } + } + ] } }, "arguments": {}, "collection_relationships": {} + }, + "ndcExplain": { + "type": "response", + "value": { + "details": { + "Execution Plan": "Aggregate (cost=73.67..73.68 rows=1 width=32)\n -> Aggregate (cost=73.65..73.66 rows=1 width=32)\n -> Subquery Scan on \"%2_rows\" (cost=0.15..69.40 rows=850 width=60)\n -> Index Scan Backward using author_pkey on author \"%0_authors\" (cost=0.15..60.90 rows=850 width=36)", + "SQL Query": "EXPLAIN\nSELECT\n coalesce(json_agg(row_to_json(\"%1_universe\")), '[]') AS \"universe\"\nFROM\n (\n SELECT\n *\n FROM\n (\n SELECT\n coalesce(json_agg(row_to_json(\"%2_rows\")), '[]') AS \"rows\"\n FROM\n (\n SELECT\n \"%0_authors\".\"id\" AS \"author_id\",\n \"%0_authors\".\"first_name\" AS \"first_name\"\n FROM\n \"public\".\"author\" AS \"%0_authors\"\n ORDER BY\n \"%0_authors\".\"id\" DESC\n ) AS \"%2_rows\"\n ) AS \"%2_rows\"\n ) AS \"%1_universe\"" + } + } } } }, { "type": "modelSelect", "value": { - "modelName": "countries", + "modelName": "MovieMany", "queryRequest": { - "collection": "country", + "collection": "movies", "query": { "fields": { - "code": { + "title": { "type": "column", - "column": "code" + "column": "title" }, - "name": { + "rating": { "type": "column", - "column": "name" + "column": "rating" } } }, "arguments": {}, "collection_relationships": {} + }, + "ndcExplain": { + "type": "notSupported" } } } diff --git a/v3/engine/tests/explain/multi_root_field_queries/request.gql b/v3/engine/tests/explain/multi_root_field_queries/request.gql index 70545b9013dd8..9196501d7a2a7 100644 --- a/v3/engine/tests/explain/multi_root_field_queries/request.gql +++ b/v3/engine/tests/explain/multi_root_field_queries/request.gql @@ -1,10 +1,15 @@ { - cities { - code + getActorById(actor_id: 1){ name } - countries { - code - name + + AuthorMany(order_by: {author_id: Desc}){ + author_id + first_name + } + + MovieMany{ + title + rating } -} +} \ No newline at end of file diff --git a/v3/lang-graphql/src/http.rs b/v3/lang-graphql/src/http.rs index cc1d9dd70356b..398455e787c51 100644 --- a/v3/lang-graphql/src/http.rs +++ b/v3/lang-graphql/src/http.rs @@ -32,7 +32,7 @@ pub type Path = Vec; /// A path segment is either a field name or an index into a list. /// https://spec.graphql.org/October2021/#sel-HAPHRPJABABEyoB -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, PartialEq)] #[serde(untagged)] pub enum PathSegment { /// Path segment that represent a field. @@ -53,7 +53,7 @@ impl PathSegment { } } -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, PartialEq)] pub struct Extensions { /// Details of any error pub details: serde_json::Value, @@ -61,7 +61,7 @@ pub struct Extensions { /// A GraphQL error as defined by the spec. /// https://spec.graphql.org/October2021/#sec-Errors.Error-result-format -#[derive(Serialize, Debug)] +#[derive(Serialize, Debug, PartialEq)] pub struct GraphQLError { /// A string describing the error pub message: String,