Skip to content

Commit 2c84c57

Browse files
committed
Sorting
1 parent ad81c2b commit 2c84c57

File tree

8 files changed

+254
-67
lines changed

8 files changed

+254
-67
lines changed

graph/src/components/store.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ impl EntityFilter {
199199
#[derive(Clone, Debug, PartialEq)]
200200
pub enum EntityOrder {
201201
/// Order ascending by the given attribute. Use `id` as a tie-breaker
202-
Ascending(String, ValueType),
202+
Ascending(String, ValueType, Option<EntityType>),
203203
/// Order descending by the given attribute. Use `id` as a tie-breaker
204-
Descending(String, ValueType),
204+
Descending(String, ValueType, Option<EntityType>),
205205
/// Order by the `id` of the entities
206206
Default,
207207
/// Do not order at all. This speeds up queries where we know that

graphql/src/schema/api.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,7 @@ fn add_order_by_type(
298298
description: None,
299299
name: type_name,
300300
directives: vec![],
301-
values: fields
302-
.iter()
303-
.map(|field| &field.name)
304-
.map(|name| EnumValue {
305-
position: Pos::default(),
306-
description: None,
307-
name: name.to_owned(),
308-
directives: vec![],
309-
})
310-
.collect(),
301+
values: field_enum_values(schema, fields)?,
311302
});
312303
let def = Definition::TypeDefinition(typedef);
313304
schema.definitions.push(def);
@@ -317,6 +308,69 @@ fn add_order_by_type(
317308
Ok(())
318309
}
319310

311+
/// Generates enum values for the given set of fields.
312+
fn field_enum_values(
313+
schema: &Document,
314+
fields: &[Field],
315+
) -> Result<Vec<EnumValue>, APISchemaError> {
316+
// if field has no @derivedFrom and is ObjectType or InterfaceType, extend
317+
let mut enum_values = vec![];
318+
for field in fields {
319+
enum_values.push(EnumValue {
320+
position: Pos::default(),
321+
description: None,
322+
name: field.name.to_owned(),
323+
directives: vec![],
324+
});
325+
enum_values.extend(field_enum_values_from_child_entity(
326+
schema,
327+
field,
328+
&field.field_type,
329+
)?);
330+
}
331+
Ok(enum_values)
332+
}
333+
334+
fn field_enum_values_from_child_entity(
335+
schema: &Document,
336+
field: &Field,
337+
field_type: &Type,
338+
) -> Result<Vec<EnumValue>, APISchemaError> {
339+
match field_type {
340+
Type::NamedType(ref name) => {
341+
let named_type = schema
342+
.get_named_type(name)
343+
.ok_or_else(|| APISchemaError::TypeNotFound(name.clone()))?;
344+
Ok(match named_type {
345+
TypeDefinition::Object(ot) => {
346+
// Only add enum values for object and interface fields
347+
// if they are not @derivedFrom
348+
if ast::get_derived_from_directive(field).is_some() {
349+
vec![]
350+
} else {
351+
ot.fields
352+
.iter()
353+
.map(|f| EnumValue {
354+
position: Pos::default(),
355+
description: None,
356+
name: format!("{}__{}", field.name, f.name),
357+
directives: vec![],
358+
})
359+
.collect()
360+
}
361+
}
362+
// sorting for interfaces is not supported yet
363+
// hard to pick correct table and pass the EntityType to EntityOrder::Ascending(EntityType)
364+
_ => vec![],
365+
})
366+
}
367+
Type::ListType(ref t) => {
368+
Ok(field_enum_values_from_child_entity(schema, field, t).unwrap_or(vec![]))
369+
}
370+
Type::NonNullType(ref t) => field_enum_values_from_child_entity(schema, field, t),
371+
}
372+
}
373+
320374
/// Adds a `<type_name>_filter` enum type for the given fields to the schema.
321375
fn add_filter_type(
322376
schema: &mut Document,

graphql/src/store/query.rs

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ pub(crate) fn build_query<'a>(
5252
query = query.filter(filter);
5353
}
5454
let order = match (
55-
build_order_by(entity, field)?,
55+
build_order_by(entity, field, schema)?,
5656
build_order_direction(field)?,
5757
) {
58-
(Some((attr, value_type)), OrderDirection::Ascending) => {
59-
EntityOrder::Ascending(attr, value_type)
58+
(Some((attr, value_type, entity_type)), OrderDirection::Ascending) => {
59+
EntityOrder::Ascending(attr, value_type, entity_type)
6060
}
61-
(Some((attr, value_type)), OrderDirection::Descending) => {
62-
EntityOrder::Descending(attr, value_type)
61+
(Some((attr, value_type, entity_type)), OrderDirection::Descending) => {
62+
EntityOrder::Descending(attr, value_type, entity_type)
6363
}
6464
(None, _) => EntityOrder::Default,
6565
};
@@ -274,21 +274,61 @@ fn list_values(value: Value, filter_type: &str) -> Result<Vec<Value>, QueryExecu
274274
fn build_order_by(
275275
entity: ObjectOrInterface,
276276
field: &a::Field,
277-
) -> Result<Option<(String, ValueType)>, QueryExecutionError> {
277+
schema: &ApiSchema,
278+
) -> Result<Option<(String, ValueType, Option<EntityType>)>, QueryExecutionError> {
278279
match field.argument_value("orderBy") {
279-
Some(r::Value::Enum(name)) => {
280-
let field = sast::get_field(entity, name).ok_or_else(|| {
281-
QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone())
282-
})?;
283-
sast::get_field_value_type(&field.field_type)
284-
.map(|value_type| Some((name.to_owned(), value_type)))
285-
.map_err(|_| {
286-
QueryExecutionError::OrderByNotSupportedError(
287-
entity.name().to_owned(),
288-
name.clone(),
289-
)
290-
})
291-
}
280+
Some(r::Value::Enum(name)) => match parse_field_as_order(name) {
281+
(_, None) => {
282+
let field = sast::get_field(entity, name).ok_or_else(|| {
283+
QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone())
284+
})?;
285+
sast::get_field_value_type(&field.field_type)
286+
.map(|value_type| Some((name.to_owned(), value_type, None)))
287+
.map_err(|_| {
288+
QueryExecutionError::OrderByNotSupportedError(
289+
entity.name().to_owned(),
290+
name.clone(),
291+
)
292+
})
293+
}
294+
(field_name, Some(child_field_name)) => {
295+
let parent_field =
296+
sast::get_field(entity, &field_name.to_string()).ok_or_else(|| {
297+
QueryExecutionError::EntityFieldError(
298+
entity.name().to_owned(),
299+
field_name.to_string(),
300+
)
301+
})?;
302+
let child_type_name = resolve_type_name(&parent_field.field_type);
303+
let child_entity = schema
304+
.object_or_interface(child_type_name.as_str())
305+
.ok_or_else(|| QueryExecutionError::NamedTypeError(child_type_name.clone()))?;
306+
307+
let child_field = child_entity
308+
.field(&child_field_name.to_string())
309+
.ok_or_else(|| {
310+
QueryExecutionError::EntityFieldError(
311+
child_type_name.clone(),
312+
child_field_name.to_string(),
313+
)
314+
})?;
315+
316+
sast::get_field_value_type(&child_field.field_type)
317+
.map(|value_type| {
318+
Some((
319+
child_field_name.to_string(),
320+
value_type,
321+
Some(EntityType::new(child_type_name.clone())),
322+
))
323+
})
324+
.map_err(|_| {
325+
QueryExecutionError::OrderByNotSupportedError(
326+
child_type_name.clone(),
327+
child_field_name.to_string(),
328+
)
329+
})
330+
}
331+
},
292332
_ => match field.argument_value("text") {
293333
Some(r::Value::Object(filter)) => build_fulltext_order_by_from_object(filter),
294334
None => Ok(None),
@@ -297,14 +337,23 @@ fn build_order_by(
297337
}
298338
}
299339

340+
/// Kamil: I don't like it... What if we have `_` in the field name? For now I will use double underscore
341+
fn parse_field_as_order(field_name: &String) -> (&str, Option<&str>) {
342+
let mut field_name_parts = field_name.split("__");
343+
(
344+
field_name_parts.next().expect("it should never happen"),
345+
field_name_parts.next(),
346+
)
347+
}
348+
300349
fn build_fulltext_order_by_from_object(
301350
object: &Object,
302-
) -> Result<Option<(String, ValueType)>, QueryExecutionError> {
351+
) -> Result<Option<(String, ValueType, Option<EntityType>)>, QueryExecutionError> {
303352
object.iter().next().map_or(
304353
Err(QueryExecutionError::FulltextQueryRequiresFilter),
305354
|(key, value)| {
306355
if let r::Value::String(_) = value {
307-
Ok(Some((key.clone(), ValueType::String)))
356+
Ok(Some((key.clone(), ValueType::String, None)))
308357
} else {
309358
Err(QueryExecutionError::FulltextQueryRequiresFilter)
310359
}
@@ -601,7 +650,7 @@ mod tests {
601650
)
602651
.unwrap()
603652
.order,
604-
EntityOrder::Ascending("name".to_string(), ValueType::String)
653+
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
605654
);
606655

607656
let field = default_field_with("orderBy", r::Value::Enum("email".to_string()));
@@ -618,7 +667,7 @@ mod tests {
618667
)
619668
.unwrap()
620669
.order,
621-
EntityOrder::Ascending("email".to_string(), ValueType::String)
670+
EntityOrder::Ascending("email".to_string(), ValueType::String, None)
622671
);
623672
}
624673

@@ -680,7 +729,7 @@ mod tests {
680729
)
681730
.unwrap()
682731
.order,
683-
EntityOrder::Ascending("name".to_string(), ValueType::String)
732+
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
684733
);
685734

686735
let field = default_field_with_vec(vec![
@@ -700,7 +749,7 @@ mod tests {
700749
)
701750
.unwrap()
702751
.order,
703-
EntityOrder::Descending("name".to_string(), ValueType::String)
752+
EntityOrder::Descending("name".to_string(), ValueType::String, None)
704753
);
705754

706755
let field = default_field_with_vec(vec![
@@ -723,7 +772,7 @@ mod tests {
723772
)
724773
.unwrap()
725774
.order,
726-
EntityOrder::Ascending("name".to_string(), ValueType::String)
775+
EntityOrder::Ascending("name".to_string(), ValueType::String, None)
727776
);
728777

729778
// No orderBy -> EntityOrder::Default

store/postgres/src/relational.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ impl Layout {
641641

642642
let filter_collection = FilterCollection::new(&self, collection, filter.as_ref())?;
643643
let query = FilterQuery::new(
644+
self,
644645
&filter_collection,
645646
filter.as_ref(),
646647
order,

0 commit comments

Comments
 (0)