Skip to content

Commit

Permalink
Sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela committed Jan 13, 2022
1 parent ad81c2b commit 2c84c57
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 67 deletions.
4 changes: 2 additions & 2 deletions graph/src/components/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ impl EntityFilter {
#[derive(Clone, Debug, PartialEq)]
pub enum EntityOrder {
/// Order ascending by the given attribute. Use `id` as a tie-breaker
Ascending(String, ValueType),
Ascending(String, ValueType, Option<EntityType>),
/// Order descending by the given attribute. Use `id` as a tie-breaker
Descending(String, ValueType),
Descending(String, ValueType, Option<EntityType>),
/// Order by the `id` of the entities
Default,
/// Do not order at all. This speeds up queries where we know that
Expand Down
74 changes: 64 additions & 10 deletions graphql/src/schema/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,16 +298,7 @@ fn add_order_by_type(
description: None,
name: type_name,
directives: vec![],
values: fields
.iter()
.map(|field| &field.name)
.map(|name| EnumValue {
position: Pos::default(),
description: None,
name: name.to_owned(),
directives: vec![],
})
.collect(),
values: field_enum_values(schema, fields)?,
});
let def = Definition::TypeDefinition(typedef);
schema.definitions.push(def);
Expand All @@ -317,6 +308,69 @@ fn add_order_by_type(
Ok(())
}

/// Generates enum values for the given set of fields.
fn field_enum_values(
schema: &Document,
fields: &[Field],
) -> Result<Vec<EnumValue>, APISchemaError> {
// if field has no @derivedFrom and is ObjectType or InterfaceType, extend
let mut enum_values = vec![];
for field in fields {
enum_values.push(EnumValue {
position: Pos::default(),
description: None,
name: field.name.to_owned(),
directives: vec![],
});
enum_values.extend(field_enum_values_from_child_entity(
schema,
field,
&field.field_type,
)?);
}
Ok(enum_values)
}

fn field_enum_values_from_child_entity(
schema: &Document,
field: &Field,
field_type: &Type,
) -> Result<Vec<EnumValue>, APISchemaError> {
match field_type {
Type::NamedType(ref name) => {
let named_type = schema
.get_named_type(name)
.ok_or_else(|| APISchemaError::TypeNotFound(name.clone()))?;
Ok(match named_type {
TypeDefinition::Object(ot) => {
// Only add enum values for object and interface fields
// if they are not @derivedFrom
if ast::get_derived_from_directive(field).is_some() {
vec![]
} else {
ot.fields
.iter()
.map(|f| EnumValue {
position: Pos::default(),
description: None,
name: format!("{}__{}", field.name, f.name),
directives: vec![],
})
.collect()
}
}
// sorting for interfaces is not supported yet
// hard to pick correct table and pass the EntityType to EntityOrder::Ascending(EntityType)
_ => vec![],
})
}
Type::ListType(ref t) => {
Ok(field_enum_values_from_child_entity(schema, field, t).unwrap_or(vec![]))
}
Type::NonNullType(ref t) => field_enum_values_from_child_entity(schema, field, t),
}
}

/// Adds a `<type_name>_filter` enum type for the given fields to the schema.
fn add_filter_type(
schema: &mut Document,
Expand Down
101 changes: 75 additions & 26 deletions graphql/src/store/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ pub(crate) fn build_query<'a>(
query = query.filter(filter);
}
let order = match (
build_order_by(entity, field)?,
build_order_by(entity, field, schema)?,
build_order_direction(field)?,
) {
(Some((attr, value_type)), OrderDirection::Ascending) => {
EntityOrder::Ascending(attr, value_type)
(Some((attr, value_type, entity_type)), OrderDirection::Ascending) => {
EntityOrder::Ascending(attr, value_type, entity_type)
}
(Some((attr, value_type)), OrderDirection::Descending) => {
EntityOrder::Descending(attr, value_type)
(Some((attr, value_type, entity_type)), OrderDirection::Descending) => {
EntityOrder::Descending(attr, value_type, entity_type)
}
(None, _) => EntityOrder::Default,
};
Expand Down Expand Up @@ -274,21 +274,61 @@ fn list_values(value: Value, filter_type: &str) -> Result<Vec<Value>, QueryExecu
fn build_order_by(
entity: ObjectOrInterface,
field: &a::Field,
) -> Result<Option<(String, ValueType)>, QueryExecutionError> {
schema: &ApiSchema,
) -> Result<Option<(String, ValueType, Option<EntityType>)>, QueryExecutionError> {
match field.argument_value("orderBy") {
Some(r::Value::Enum(name)) => {
let field = sast::get_field(entity, name).ok_or_else(|| {
QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone())
})?;
sast::get_field_value_type(&field.field_type)
.map(|value_type| Some((name.to_owned(), value_type)))
.map_err(|_| {
QueryExecutionError::OrderByNotSupportedError(
entity.name().to_owned(),
name.clone(),
)
})
}
Some(r::Value::Enum(name)) => match parse_field_as_order(name) {
(_, None) => {
let field = sast::get_field(entity, name).ok_or_else(|| {
QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone())
})?;
sast::get_field_value_type(&field.field_type)
.map(|value_type| Some((name.to_owned(), value_type, None)))
.map_err(|_| {
QueryExecutionError::OrderByNotSupportedError(
entity.name().to_owned(),
name.clone(),
)
})
}
(field_name, Some(child_field_name)) => {
let parent_field =
sast::get_field(entity, &field_name.to_string()).ok_or_else(|| {
QueryExecutionError::EntityFieldError(
entity.name().to_owned(),
field_name.to_string(),
)
})?;
let child_type_name = resolve_type_name(&parent_field.field_type);
let child_entity = schema
.object_or_interface(child_type_name.as_str())
.ok_or_else(|| QueryExecutionError::NamedTypeError(child_type_name.clone()))?;

let child_field = child_entity
.field(&child_field_name.to_string())
.ok_or_else(|| {
QueryExecutionError::EntityFieldError(
child_type_name.clone(),
child_field_name.to_string(),
)
})?;

sast::get_field_value_type(&child_field.field_type)
.map(|value_type| {
Some((
child_field_name.to_string(),
value_type,
Some(EntityType::new(child_type_name.clone())),
))
})
.map_err(|_| {
QueryExecutionError::OrderByNotSupportedError(
child_type_name.clone(),
child_field_name.to_string(),
)
})
}
},
_ => match field.argument_value("text") {
Some(r::Value::Object(filter)) => build_fulltext_order_by_from_object(filter),
None => Ok(None),
Expand All @@ -297,14 +337,23 @@ fn build_order_by(
}
}

/// Kamil: I don't like it... What if we have `_` in the field name? For now I will use double underscore
fn parse_field_as_order(field_name: &String) -> (&str, Option<&str>) {
let mut field_name_parts = field_name.split("__");
(
field_name_parts.next().expect("it should never happen"),
field_name_parts.next(),
)
}

fn build_fulltext_order_by_from_object(
object: &Object,
) -> Result<Option<(String, ValueType)>, QueryExecutionError> {
) -> Result<Option<(String, ValueType, Option<EntityType>)>, QueryExecutionError> {
object.iter().next().map_or(
Err(QueryExecutionError::FulltextQueryRequiresFilter),
|(key, value)| {
if let r::Value::String(_) = value {
Ok(Some((key.clone(), ValueType::String)))
Ok(Some((key.clone(), ValueType::String, None)))
} else {
Err(QueryExecutionError::FulltextQueryRequiresFilter)
}
Expand Down Expand Up @@ -601,7 +650,7 @@ mod tests {
)
.unwrap()
.order,
EntityOrder::Ascending("name".to_string(), ValueType::String)
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
);

let field = default_field_with("orderBy", r::Value::Enum("email".to_string()));
Expand All @@ -618,7 +667,7 @@ mod tests {
)
.unwrap()
.order,
EntityOrder::Ascending("email".to_string(), ValueType::String)
EntityOrder::Ascending("email".to_string(), ValueType::String, None)
);
}

Expand Down Expand Up @@ -680,7 +729,7 @@ mod tests {
)
.unwrap()
.order,
EntityOrder::Ascending("name".to_string(), ValueType::String)
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
);

let field = default_field_with_vec(vec![
Expand All @@ -700,7 +749,7 @@ mod tests {
)
.unwrap()
.order,
EntityOrder::Descending("name".to_string(), ValueType::String)
EntityOrder::Descending("name".to_string(), ValueType::String, None)
);

let field = default_field_with_vec(vec![
Expand All @@ -723,7 +772,7 @@ mod tests {
)
.unwrap()
.order,
EntityOrder::Ascending("name".to_string(), ValueType::String)
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
);

// No orderBy -> EntityOrder::Default
Expand Down
1 change: 1 addition & 0 deletions store/postgres/src/relational.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ impl Layout {

let filter_collection = FilterCollection::new(&self, collection, filter.as_ref())?;
let query = FilterQuery::new(
self,
&filter_collection,
filter.as_ref(),
order,
Expand Down
Loading

0 comments on commit 2c84c57

Please sign in to comment.