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

update to ndc-spec v0.1.6 to implement support for querying nested collections #101

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 18 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
107 changes: 107 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,113 @@ This changelog documents the changes between release versions.
### Added

- Extended JSON fields now support all comparison and aggregation functions ([#99](https://github.com/hasura/ndc-mongodb/pull/99))
- Update to ndc-spec v0.1.6 which allows filtering by object values in array fields ([#101](https://github.com/hasura/ndc-mongodb/pull/101))

#### Filtering by values in arrays

In this update you can filter by making comparisons to object values inside
arrays. For example consider a MongoDB database with these three documents:

```json
{ "institution": "Black Mesa", "staff": [{ "name": "Freeman" }, { "name": "Calhoun" }] }
{ "institution": "Aperture Science", "staff": [{ "name": "GLaDOS" }, { "name": "Chell" }] }
{ "institution": "City 17", "staff": [{ "name": "Alyx" }, { "name": "Freeman" }, { "name": "Breen" }] }
```

You can now write a GraphQL query with a `where` clause that checks individual
entries in the `staff` arrays:

```graphql
query {
institutions(where: { staff: { name: { _eq: "Freeman" } } }) {
institution
}
}
```

Which produces the result:

```json
{ "data": { "institutions": [
{ "institution": "Black Mesa" },
{ "institution": "City 17" }
] } }
```

The filter selects documents where **any** element in the array passes the
condition. If you want to select only documents where _every_ array element
passes then negate the comparison on array element values, and also negate the
entire predicate like this:

```graphql
query EveryElementMustMatch {
institutions(
where: { _not: { staff: { name: { _neq: "Freeman" } } } }
) {
institution
}
}
```

**Note:** It is currently only possible to filter on arrays that contain
objects. Filtering on arrays that contain scalar values or nested arrays will
come later.

To configure DDN metadata to filter on array fields configure the
`BooleanExpressionType` for the containing document object type to use an
**object** boolean expression type for comparisons on the array field. The
GraphQL Engine will transparently distribute object comparisons over array
elements. For example the above example is configured with this boolean
expression type for documents:

```yaml
---
kind: BooleanExpressionType
version: v1
definition:
name: InstitutionComparisonExp
operand:
object:
type: Institution
comparableFields:
- fieldName: id
booleanExpressionType: ObjectIdComparisonExp
- fieldName: institution
booleanExpressionType: StringComparisonExp
- fieldName: staff
booleanExpressionType: InstitutionStaffComparisonExp
comparableRelationships: []
logicalOperators:
enable: true
isNull:
enable: true
graphql:
typeName: InstitutionComparisonExp
```

`InstitutionStaffComparisonExp` is the boolean expression type for objects
inside the `staff` array. It looks like this:

```yaml
---
kind: BooleanExpressionType
version: v1
definition:
name: InstitutionStaffComparisonExp
operand:
object:
type: InstitutionStaff
comparableFields:
- fieldName: name
booleanExpressionType: StringComparisonExp
comparableRelationships: []
logicalOperators:
enable: true
isNull:
enable: true
graphql:
typeName: InstitutionStaffComparisonExp
```

### Fixed

Expand Down
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ resolver = "2"
# The tag or rev of ndc-models must match the locked tag or rev of the
# ndc-models dependency of ndc-sdk
[workspace.dependencies]
ndc-sdk = { git = "https://github.com/hasura/ndc-sdk-rs.git", tag = "v0.2.1" }
ndc-models = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.5" }
ndc-sdk = { git = "https://github.com/hasura/ndc-sdk-rs.git", tag = "v0.4.0" }
ndc-models = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.6" }

indexmap = { version = "2", features = [
"serde",
Expand Down
2 changes: 1 addition & 1 deletion arion-compose/e2e-testing.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ in

connector = import ./services/connector.nix {
inherit pkgs;
configuration-dir = ../fixtures/hasura/chinook/connector/chinook;
configuration-dir = ../fixtures/hasura/chinook/connector;
database-uri = "mongodb://mongodb/chinook";
port = connector-port;
service.depends_on.mongodb.condition = "service_healthy";
Expand Down
18 changes: 16 additions & 2 deletions arion-compose/integration-test-services.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
, otlp-endpoint ? null
, connector-port ? "7130"
, connector-chinook-port ? "7131"
, connector-test-cases-port ? "7132"
, engine-port ? "7100"
, mongodb-port ? "27017"
}:
Expand All @@ -21,7 +22,7 @@ in
{
connector = import ./services/connector.nix {
inherit pkgs otlp-endpoint;
configuration-dir = ../fixtures/hasura/sample_mflix/connector/sample_mflix;
configuration-dir = ../fixtures/hasura/sample_mflix/connector;
database-uri = "mongodb://mongodb/sample_mflix";
port = connector-port;
hostPort = hostPort connector-port;
Expand All @@ -32,7 +33,7 @@ in

connector-chinook = import ./services/connector.nix {
inherit pkgs otlp-endpoint;
configuration-dir = ../fixtures/hasura/chinook/connector/chinook;
configuration-dir = ../fixtures/hasura/chinook/connector;
database-uri = "mongodb://mongodb/chinook";
port = connector-chinook-port;
hostPort = hostPort connector-chinook-port;
Expand All @@ -41,6 +42,17 @@ in
};
};

connector-test-cases = import ./services/connector.nix {
inherit pkgs otlp-endpoint;
configuration-dir = ../fixtures/hasura/test_cases/connector;
database-uri = "mongodb://mongodb/test_cases";
port = connector-test-cases-port;
hostPort = hostPort connector-test-cases-port;
service.depends_on = {
mongodb.condition = "service_healthy";
};
};

mongodb = import ./services/mongodb.nix {
inherit pkgs;
port = mongodb-port;
Expand All @@ -60,10 +72,12 @@ in
connectors = {
chinook = "http://connector-chinook:${connector-chinook-port}";
sample_mflix = "http://connector:${connector-port}";
test_cases = "http://connector-test-cases:${connector-test-cases-port}";
};
ddn-dirs = [
../fixtures/hasura/chinook/metadata
../fixtures/hasura/sample_mflix/metadata
../fixtures/hasura/test_cases/metadata
../fixtures/hasura/common/metadata
];
service.depends_on = {
Expand Down
3 changes: 3 additions & 0 deletions arion-compose/integration-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
let
connector-port = "7130";
connector-chinook-port = "7131";
connector-test-cases-port = "7132";
engine-port = "7100";

services = import ./integration-test-services.nix {
Expand All @@ -26,10 +27,12 @@ in
inherit pkgs;
connector-url = "http://connector:${connector-port}/";
connector-chinook-url = "http://connector-chinook:${connector-chinook-port}/";
connector-test-cases-url = "http://connector-test-cases:${connector-test-cases-port}/";
engine-graphql-url = "http://engine:${engine-port}/graphql";
service.depends_on = {
connector.condition = "service_healthy";
connector-chinook.condition = "service_healthy";
connector-test-cases.condition = "service_healthy";
engine.condition = "service_healthy";
};
# Run the container as the current user so when it writes to the snapshots
Expand Down
2 changes: 1 addition & 1 deletion arion-compose/ndc-test.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ in
# command = ["test" "--snapshots-dir" "/snapshots" "--seed" "1337_1337_1337_1337_1337_1337_13"];
# Replay and test the recorded snapshots
# command = ["replay" "--snapshots-dir" "/snapshots"];
configuration-dir = ../fixtures/hasura/chinook/connector/chinook;
configuration-dir = ../fixtures/hasura/chinook/connector;
database-uri = "mongodb://mongodb:${mongodb-port}/chinook";
service.depends_on.mongodb.condition = "service_healthy";
# Run the container as the current user so when it writes to the snapshots directory it doesn't write as root
Expand Down
8 changes: 3 additions & 5 deletions arion-compose/services/connector.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
, profile ? "dev" # Rust crate profile, usually either "dev" or "release"
, hostPort ? null
, command ? ["serve"]
, configuration-dir ? ../../fixtures/hasura/sample_mflix/connector/sample_mflix
, configuration-dir ? ../../fixtures/hasura/sample_mflix/connector
, database-uri ? "mongodb://mongodb/sample_mflix"
, service ? { } # additional options to customize this service configuration
, otlp-endpoint ? null
Expand All @@ -32,16 +32,14 @@ let
"${hostPort}:${port}" # host:container
];
environment = pkgs.lib.filterAttrs (_: v: v != null) {
HASURA_CONFIGURATION_DIRECTORY = "/configuration";
HASURA_CONFIGURATION_DIRECTORY = (pkgs.lib.sources.cleanSource configuration-dir).outPath;
HASURA_CONNECTOR_PORT = port;
MONGODB_DATABASE_URI = database-uri;
OTEL_SERVICE_NAME = "mongodb-connector";
OTEL_EXPORTER_OTLP_ENDPOINT = otlp-endpoint;
RUST_LOG = "configuration=debug,mongodb_agent_common=debug,mongodb_connector=debug,mongodb_support=debug,ndc_query_plan=debug";
};
volumes = [
"${configuration-dir}:/configuration:ro"
] ++ extra-volumes;
volumes = extra-volumes;
healthcheck = {
test = [ "CMD" "${pkgs.pkgsCross.linux.curl}/bin/curl" "-f" "http://localhost:${port}/health" ];
start_period = "5s";
Expand Down
2 changes: 2 additions & 0 deletions arion-compose/services/integration-tests.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{ pkgs
, connector-url
, connector-chinook-url
, connector-test-cases-url
, engine-graphql-url
, service ? { } # additional options to customize this service configuration
}:
Expand All @@ -16,6 +17,7 @@ let
environment = {
CONNECTOR_URL = connector-url;
CONNECTOR_CHINOOK_URL = connector-chinook-url;
CONNECTOR_TEST_CASES_URL = connector-test-cases-url;
ENGINE_GRAPHQL_URL = engine-graphql-url;
INSTA_WORKSPACE_ROOT = repo-source-mount-point;
MONGODB_IMAGE = builtins.getEnv "MONGODB_IMAGE";
Expand Down
21 changes: 21 additions & 0 deletions crates/integration-tests/src/tests/filtering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,24 @@ async fn filters_on_extended_json_using_string_comparison() -> anyhow::Result<()
);
Ok(())
}

#[tokio::test]
async fn filters_by_comparisons_on_elements_of_array_field() -> anyhow::Result<()> {
assert_yaml_snapshot!(
graphql_query(
r#"
query {
testCases_nestedCollection(
where: { staff: { name: { _eq: "Freeman" } } }
order_by: { institution: Asc }
) {
institution
}
}
"#
)
.run()
.await?
);
Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/integration-tests/src/tests/filtering.rs
expression: "graphql_query(r#\"\n query {\n testCases_nestedCollection(\n where: { staff: { name: { _eq: \"Freeman\" } } }\n order_by: { institution: Asc }\n ) {\n institution\n }\n }\n \"#).run().await?"
---
data:
testCases_nestedCollection:
- institution: Black Mesa
- institution: City 17
errors: ~
15 changes: 0 additions & 15 deletions crates/mongodb-agent-common/src/health.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::fmt::{self, Display};
use std::{
borrow::Cow,
fmt::{self, Display},
};

use http::StatusCode;
use mongodb::bson;
Expand All @@ -19,7 +22,7 @@ pub enum MongoAgentError {
MongoDBDeserialization(#[from] mongodb::bson::de::Error),
MongoDBSerialization(#[from] mongodb::bson::ser::Error),
MongoDBSupport(#[from] mongodb_support::error::Error),
NotImplemented(&'static str),
NotImplemented(Cow<'static, str>),
Procedure(#[from] ProcedureError),
QueryPlan(#[from] QueryPlanError),
ResponseSerialization(#[from] QueryResponseError),
Expand Down
1 change: 0 additions & 1 deletion crates/mongodb-agent-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pub mod aggregation_function;
pub mod comparison_function;
pub mod explain;
pub mod health;
pub mod interface_types;
pub mod mongo_query_plan;
pub mod mongodb;
Expand Down
Loading
Loading