这是indexloc提供的服务,不要输入任何密码
Skip to content

filter and sort by field of related collection #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7b4301d
combine selected fields from multiple references to the same relation…
hallettj May 29, 2024
a1c4b20
use $elemMatch to make comparisons against fields of related collections
hallettj May 29, 2024
5a301f5
update test to avoid not-null check
hallettj May 29, 2024
921cf8a
do not restrict field selection in relationship queries
hallettj May 29, 2024
3f4b523
set
hallettj May 29, 2024
365c220
Revert "set"
hallettj May 29, 2024
71f530b
propagate needed fields to relationship queries
hallettj May 30, 2024
a99eb70
Revert "do not restrict field selection in relationship queries"
hallettj May 30, 2024
9c29110
explicitly select relation fields in $replaceWith stage
hallettj May 30, 2024
03230bf
prune selection for relationship fields and aggregates
hallettj May 30, 2024
60977bc
update tests
hallettj May 30, 2024
da84e56
do not set predicate on query for exists relation
hallettj May 30, 2024
e82f497
integration test snapshot
hallettj May 30, 2024
91eb37b
test sorting by field of relation
hallettj May 30, 2024
2463a60
update changelog
hallettj May 30, 2024
ba5ca40
update engine to version from 2024-05-30
hallettj May 30, 2024
9410f21
add field selection to exists query
hallettj May 31, 2024
f343efc
add test for non-null comparison
hallettj May 31, 2024
6a01d86
add missing import in test helpers
hallettj May 31, 2024
73617e0
a test hit a relationship unification failure - handle that case
hallettj May 31, 2024
e6c3cec
track integration test snapshot
hallettj May 31, 2024
70014f5
define all chinook relationships
hallettj Jun 3, 2024
f0cf8a1
forward relationship arrays from non-top-level queries
hallettj Jun 3, 2024
3303f11
add relationship unification tests
hallettj Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
This changelog documents the changes between release versions.

## [Unreleased]
- Support filtering and sorting by fields of related collections ([#72](https://github.com/hasura/ndc-mongodb/pull/72))

## [0.0.6] - 2024-05-01
- Enables logging events from the MongoDB driver by setting the `RUST_LOG` variable ([#67](https://github.com/hasura/ndc-mongodb/pull/67))
Expand Down
2 changes: 1 addition & 1 deletion arion-compose/services/dev-auth-webhook.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ in
service = {
useHostStore = true;
command = [
"${dev-auth-webhook}/bin/hasura-dev-auth-webhook"
"${dev-auth-webhook}/bin/dev-auth-webhook"
];
};
}
2 changes: 1 addition & 1 deletion crates/configuration/src/native_mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl NativeMutation {
&object_field.r#type.into(),
MongoScalarType::lookup_scalar_type,
)?,
))
)) as Result<_, QueryPlanError>
})
.try_collect()?;

Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/native_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl NativeQuery {
&object_field.r#type.into(),
MongoScalarType::lookup_scalar_type,
)?,
))
)) as Result<_, QueryPlanError>
})
.try_collect()?;

Expand Down
81 changes: 75 additions & 6 deletions crates/integration-tests/src/tests/local_relationship.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::graphql_query;
use insta::assert_yaml_snapshot;
use serde_json::json;

#[tokio::test]
async fn joins_local_relationships() -> anyhow::Result<()> {
Expand Down Expand Up @@ -37,30 +36,100 @@ async fn joins_local_relationships() -> anyhow::Result<()> {
}
"#
)
.variables(json!({ "limit": 11, "movies_limit": 2 }))
.run()
.await?
);
Ok(())
}

// TODO: Tests an upcoming change in MBD-14
#[ignore]
#[tokio::test]
async fn filters_by_field_of_related_collection() -> anyhow::Result<()> {
assert_yaml_snapshot!(
graphql_query(
r#"
query {
comments(limit: 10, where: {movie: {title: {_is_null: false}}}) {
comments(where: {movie: {rated: {_eq: "G"}}}, limit: 10, order_by: {id: Asc}) {
movie {
title
year
}
}
}
"#
)
.variables(json!({ "limit": 11, "movies_limit": 2 }))
.run()
.await?
);
Ok(())
}

#[tokio::test]
async fn filters_by_non_null_field_of_related_collection() -> anyhow::Result<()> {
assert_yaml_snapshot!(
graphql_query(
r#"
query {
comments(
limit: 10
where: {movie: {title: {_is_null: false}}}
order_by: {id: Asc}
) {
movie {
title
year
}
}
}
"#
)
.run()
.await?
);
Ok(())
}

#[tokio::test]
async fn filters_by_field_of_relationship_of_relationship() -> anyhow::Result<()> {
assert_yaml_snapshot!(
graphql_query(
r#"
query {
artist(where: {albums: {tracks: {name: {_eq: "Princess of the Dawn"}}}}) {
name
albums(order_by: {title: Asc}) {
title
}
}
}
"#
)
.run()
.await?
);
Ok(())
}

#[tokio::test]
async fn sorts_by_field_of_related_collection() -> anyhow::Result<()> {
// Filter by rating to filter out comments whose movie relation is null.
assert_yaml_snapshot!(
graphql_query(
r#"
query {
comments(
limit: 10
order_by: [{movie: {title: Asc}}, {date: Asc}]
where: {movie: {rated: {_eq: "G"}}}
) {
movie {
title
year
}
text
}
}
"#
)
.run()
.await?
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
source: crates/integration-tests/src/tests/local_relationship.rs
expression: "graphql_query(r#\"\n query {\n comments(where: {movie: {rated: {_eq: \"G\"}}}, limit: 10, order_by: {id: Asc}) {\n movie {\n title\n year\n }\n }\n }\n \"#).variables(json!({\n \"limit\": 11, \"movies_limit\": 2\n })).run().await?"
---
data:
comments:
- movie:
title: A Corner in Wheat
year: 1909
- movie:
title: Naughty Marietta
year: 1935
- movie:
title: Modern Times
year: 1936
- movie:
title: The Man Who Came to Dinner
year: 1942
- movie:
title: National Velvet
year: 1944
- movie:
title: National Velvet
year: 1944
- movie:
title: Alice in Wonderland
year: 1951
- movie:
title: The King and I
year: 1956
- movie:
title: 101 Dalmatians
year: 1961
- movie:
title: 101 Dalmatians
year: 1961
errors: ~
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/integration-tests/src/tests/local_relationship.rs
expression: "graphql_query(r#\"\n query {\n artist(where: {albums: {tracks: {name: {_eq: \"Princess of the Dawn\"}}}}) {\n name\n albums(order_by: {title: Asc}) {\n title\n }\n }\n }\n \"#).run().await?"
---
data:
artist:
- name: Accept
albums:
- title: Balls to the Wall
- title: Restless and Wild
errors: ~
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
source: crates/integration-tests/src/tests/local_relationship.rs
expression: "graphql_query(r#\"\n query {\n comments(\n limit: 10\n where: {movie: {title: {_is_null: false}}}\n order_by: {id: Asc}\n ) {\n movie {\n title\n year\n }\n }\n }\n \"#).run().await?"
---
data:
comments:
- movie:
title: The Land Beyond the Sunset
year: 1912
- movie:
title: A Corner in Wheat
year: 1909
- movie:
title: In the Land of the Head Hunters
year: 1914
- movie:
title: Traffic in Souls
year: 1913
- movie:
title: Regeneration
year: 1915
- movie:
title: "Hell's Hinges"
year: 1916
- movie:
title: Broken Blossoms or The Yellow Man and the Girl
year: 1919
- movie:
title: High and Dizzy
year: 1920
- movie:
title: The Ace of Hearts
year: 1921
- movie:
title: The Four Horsemen of the Apocalypse
year: 1921
errors: ~
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
source: crates/integration-tests/src/tests/local_relationship.rs
expression: "graphql_query(r#\"\n query {\n comments(\n limit: 10\n order_by: [{movie: {title: Asc}}, {date: Asc}]\n where: {movie: {rated: {_eq: \"G\"}}}\n ) {\n movie {\n title\n year\n }\n text\n }\n }\n \"#).run().await?"
---
data:
comments:
- movie:
title: 101 Dalmatians
year: 1961
text: Ipsam cumque facilis officiis ipsam molestiae veniam rerum. Voluptatibus totam eius repellendus sint. Dignissimos distinctio accusantium ad voluptas laboriosam.
- movie:
title: 101 Dalmatians
year: 1961
text: Consequatur aliquam commodi quod ad. Id autem rerum reiciendis. Delectus suscipit optio ratione.
- movie:
title: 101 Dalmatians
year: 1961
text: Sequi minima veritatis nobis impedit saepe. Quia consequatur sunt commodi laboriosam ducimus illum nostrum facilis. Fugit nam in ipsum incidunt.
- movie:
title: 101 Dalmatians
year: 1961
text: Cumque maiores dignissimos nostrum aut autem iusto voluptatum. Voluptatum maiores excepturi ea. Quasi expedita dolorum similique aperiam.
- movie:
title: 101 Dalmatians
year: 1961
text: Quo rem tempore repudiandae assumenda. Totam quas fugiat impedit soluta doloremque repellat error. Nesciunt aspernatur quis veritatis dignissimos commodi a. Ullam neque fugiat culpa distinctio.
- movie:
title: 101 Dalmatians
year: 1961
text: Similique unde est dolore amet cum. Molestias debitis laudantium quae animi. Ipsa veniam quos beatae sed facilis omnis est. Aliquid ipsum temporibus dignissimos nostrum.
- movie:
title: 101 Dalmatians
year: 1961
text: Quisquam iusto numquam perferendis. Labore dolorem corporis aperiam dolor officia natus. Officiis debitis cumque pariatur alias. Mollitia commodi aliquid fugiat excepturi veritatis.
- movie:
title: 101 Dalmatians
year: 1961
text: Atque nemo pariatur ipsam magnam sit impedit. Fuga earum laudantium iste laboriosam debitis. Possimus eaque vero consequuntur voluptates.
- movie:
title: 101 Dalmatians
year: 1961
text: Sapiente facilis fugiat labore quo mollitia. Omnis dolor perferendis at et. Maiores voluptates eaque iste quidem praesentium saepe temporibus. Unde occaecati magnam aspernatur repudiandae occaecati.
- movie:
title: 101 Dalmatians
year: 1961
text: A porro temporibus quisquam dolore atque itaque nobis debitis. Dolorum voluptatem qui odit itaque quas quis quidem. Culpa doloribus ut non aut illum quae in. Vero aspernatur excepturi pariatur.
errors: ~
6 changes: 5 additions & 1 deletion crates/mongodb-agent-common/src/comparison_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ impl ComparisonFunction {
}

/// Produce a MongoDB expression that applies this function to the given operands.
pub fn mongodb_expression(self, column_ref: String, comparison_value: Bson) -> Document {
pub fn mongodb_expression(
self,
column_ref: impl Into<String>,
comparison_value: Bson,
) -> Document {
match self {
C::IRegex => {
doc! { column_ref: { self.mongodb_name(): comparison_value, "$options": "i" } }
Expand Down
73 changes: 68 additions & 5 deletions crates/mongodb-agent-common/src/mongodb/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,65 @@ fn selection_for_field(parent_columns: &[&str], field: &Field) -> Result<Bson, M
Field::Relationship {
relationship,
aggregates,
fields,
..
} => {
if aggregates.is_some() {
Ok(doc! { "$first": get_field(relationship) }.into())
// The pipeline for the relationship has already selected the requested fields with the
// appropriate aliases. At this point all we need to do is to prune the selection down
// to requested fields, omitting fields of the relationship that were selected for
// filtering and sorting.
let field_selection: Option<Document> = fields.as_ref().map(|fields| {
fields
.iter()
.map(|(field_name, _)| {
(field_name.to_owned(), format!("$$this.{field_name}").into())
})
.collect()
});

if let Some(aggregates) = aggregates {
let aggregate_selecion: Document = aggregates
.iter()
.map(|(aggregate_name, _)| {
(
aggregate_name.to_owned(),
format!("$$row_set.aggregates.{aggregate_name}").into(),
)
})
.collect();
let mut new_row_set = doc! { "aggregates": aggregate_selecion };

if let Some(field_selection) = field_selection {
new_row_set.insert(
"rows",
doc! {
"$map": {
"input": "$$row_set.rows",
"in": field_selection,
}
},
);
}

Ok(doc! {
"$let": {
"vars": { "row_set": { "$first": get_field(relationship) } },
"in": new_row_set,
}
}
.into())
} else if let Some(field_selection) = field_selection {
Ok(doc! {
"rows": {
"$map": {
"input": get_field(relationship),
"in": field_selection,
}
}
}
.into())
} else {
Ok(doc! { "rows": get_field(relationship) }.into())
Ok(doc! { "rows": [] }.into())
}
}
}
Expand Down Expand Up @@ -276,12 +329,22 @@ mod tests {
doc! {
"class_students": {
"rows": {
"$getField": { "$literal": "class_students" }
"$map": {
"input": { "$getField": { "$literal": "class_students" } },
"in": {
"name": "$$this.name"
},
},
},
},
"students": {
"rows": {
"$getField": { "$literal": "class_students" }
"$map": {
"input": { "$getField": { "$literal": "class_students" } },
"in": {
"student_name": "$$this.student_name"
},
},
},
},
}
Expand Down
Loading