diff --git a/CHANGELOG.md b/CHANGELOG.md index c02e451f..98841a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog documents the changes between release versions. - Fix for databases with field names that begin with a dollar sign, or that contain dots ([#74](https://github.com/hasura/ndc-mongodb/pull/74)) - Implement column-to-column comparisons within the same collection ([#74](https://github.com/hasura/ndc-mongodb/pull/74)) - Fix error tracking collection with no documents by skipping such collections during CLI introspection ([#76](https://github.com/hasura/ndc-mongodb/pull/76)) +- If a field contains both `int` and `double` values then the field type is inferred as `double` instead of `ExtendedJSON` ([#77](https://github.com/hasura/ndc-mongodb/pull/77)) ## [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)) diff --git a/crates/cli/src/introspection/type_unification.rs b/crates/cli/src/introspection/type_unification.rs index 61a8a377..31e539e1 100644 --- a/crates/cli/src/introspection/type_unification.rs +++ b/crates/cli/src/introspection/type_unification.rs @@ -8,7 +8,10 @@ use configuration::{ }; use indexmap::IndexMap; use itertools::Itertools as _; -use mongodb_support::{align::align, BsonScalarType::*}; +use mongodb_support::{ + align::align, + BsonScalarType::{self, *}, +}; use std::string::String; type ObjectField = WithName; @@ -43,11 +46,13 @@ pub fn unify_type(type_a: Type, type_b: Type) -> Type { (Type::Scalar(Null), type_b) => type_b.make_nullable(), (type_a, Type::Scalar(Null)) => type_a.make_nullable(), - // Scalar types unify if they are the same type. + // Scalar types unify if they are the same type, or if one is a superset of the other. // If they are diffferent then the union is ExtendedJSON. (Type::Scalar(scalar_a), Type::Scalar(scalar_b)) => { - if scalar_a == scalar_b { + if scalar_a == scalar_b || is_supertype(&scalar_a, &scalar_b) { Type::Scalar(scalar_a) + } else if is_supertype(&scalar_b, &scalar_a) { + Type::Scalar(scalar_b) } else { Type::ExtendedJSON } @@ -169,6 +174,10 @@ pub fn unify_object_types( merged_type_map.into_values().collect() } +fn is_supertype(a: &BsonScalarType, b: &BsonScalarType) -> bool { + matches!((a, b), (Double, Int)) +} + #[cfg(test)] mod tests { use std::collections::{HashMap, HashSet}; @@ -307,4 +316,34 @@ mod tests { "Missing field in result type") } } + + #[test] + fn test_double_and_int_unify_as_double() { + let double = || Type::Scalar(BsonScalarType::Double); + let int = || Type::Scalar(BsonScalarType::Int); + + let u = unify_type(double(), int()); + assert_eq!(u, double()); + + let u = unify_type(int(), double()); + assert_eq!(u, double()); + } + + #[test] + fn test_nullable_double_and_int_unify_as_nullable_double() { + let double = || Type::Scalar(BsonScalarType::Double); + let int = || Type::Scalar(BsonScalarType::Int); + + for (a, b) in [ + (double().make_nullable(), int()), + (double(), int().make_nullable()), + (double().make_nullable(), int().make_nullable()), + (int(), double().make_nullable()), + (int().make_nullable(), double()), + (int().make_nullable(), double().make_nullable()), + ] { + let u = unify_type(a, b); + assert_eq!(u, double().make_nullable()); + } + } }