Skip to content

Commit 4911c90

Browse files
committed
store: Generate different queries depending on child multiplicity
1 parent 580d26b commit 4911c90

File tree

2 files changed

+145
-20
lines changed

2 files changed

+145
-20
lines changed

store/postgres/src/relational_queries.rs

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use std::str::FromStr;
1818

1919
use graph::data::{schema::FulltextAlgorithm, store::scalar};
2020
use graph::prelude::{
21-
format_err, serde_json, Attribute, BlockNumber, Entity, EntityCollection, EntityFilter,
22-
EntityKey, EntityLink, EntityOrder, EntityRange, EntityWindow, ParentLink, QueryExecutionError,
23-
StoreError, Value,
21+
format_err, serde_json, Attribute, BlockNumber, ChildMultiplicity, Entity, EntityCollection,
22+
EntityFilter, EntityKey, EntityLink, EntityOrder, EntityRange, EntityWindow, ParentLink,
23+
QueryExecutionError, StoreError, Value,
2424
};
2525

2626
use crate::block_range::{
@@ -1361,16 +1361,16 @@ impl ParentIds {
13611361
/// corresponding table and column
13621362
#[derive(Debug, Clone)]
13631363
enum TableLink<'a> {
1364-
Direct(&'a Column),
1364+
Direct(&'a Column, ChildMultiplicity),
13651365
Parent(ParentIds),
13661366
}
13671367

13681368
impl<'a> TableLink<'a> {
13691369
fn new(child_table: &'a Table, link: EntityLink) -> Result<Self, QueryExecutionError> {
13701370
match link {
1371-
EntityLink::Direct(attribute, _) => {
1371+
EntityLink::Direct(attribute, multiplicity) => {
13721372
let column = child_table.column_for_field(attribute.name())?;
1373-
Ok(TableLink::Direct(column))
1373+
Ok(TableLink::Direct(column, multiplicity))
13741374
}
13751375
EntityLink::Parent(parent_link) => Ok(TableLink::Parent(ParentIds::new(parent_link))),
13761376
}
@@ -1487,7 +1487,7 @@ impl<'a> FilterWindow<'a> {
14871487
// limit {first} offset {skip}) c
14881488
// order by c.{sort_key}
14891489

1490-
out.push_sql("\n from unnest(");
1490+
out.push_sql("\n/* children_type_a */ from unnest(");
14911491
column.bind_ids(&self.ids, out)?;
14921492
out.push_sql(") as p(id) cross join lateral (select * from ");
14931493
out.push_sql(self.table.qualified_name.as_str());
@@ -1503,6 +1503,37 @@ impl<'a> FilterWindow<'a> {
15031503
Ok(())
15041504
}
15051505

1506+
fn child_type_a(
1507+
&self,
1508+
column: &Column,
1509+
limit: ParentLimit<'_>,
1510+
block: BlockNumber,
1511+
out: &mut AstPass<Pg>,
1512+
) -> QueryResult<()> {
1513+
assert!(column.is_list());
1514+
1515+
// Generate
1516+
// from unnest({parent_ids}) as p(id),
1517+
// children c
1518+
// where c.{parent_field} @> array[p.id]
1519+
// and .. other conditions on c ..
1520+
// limit {parent_ids.len} + 1
1521+
1522+
out.push_sql("\n/* child_type_a */ from unnest(");
1523+
column.bind_ids(&self.ids, out)?;
1524+
out.push_sql(") as p(id), ");
1525+
out.push_sql(self.table.qualified_name.as_str());
1526+
out.push_sql(" c where ");
1527+
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1528+
limit.filter(out);
1529+
out.push_sql(" and c.");
1530+
out.push_identifier(column.name.as_str())?;
1531+
out.push_sql(" @> array[p.id]");
1532+
self.and_filter(out.reborrow())?;
1533+
limit.single_limit(self.ids.len(), out);
1534+
Ok(())
1535+
}
1536+
15061537
fn children_type_b(
15071538
&self,
15081539
column: &Column,
@@ -1523,7 +1554,7 @@ impl<'a> FilterWindow<'a> {
15231554
// limit {first} offset {skip}) c
15241555
// order by c.{sort_key}
15251556

1526-
out.push_sql("\n from unnest(");
1557+
out.push_sql("\n/* children_type_b */ from unnest(");
15271558
column.bind_ids(&self.ids, out)?;
15281559
out.push_sql(") as p(id) cross join lateral (select * from ");
15291560
out.push_sql(self.table.qualified_name.as_str());
@@ -1538,6 +1569,35 @@ impl<'a> FilterWindow<'a> {
15381569
Ok(())
15391570
}
15401571

1572+
fn child_type_b(
1573+
&self,
1574+
column: &Column,
1575+
limit: ParentLimit<'_>,
1576+
block: BlockNumber,
1577+
out: &mut AstPass<Pg>,
1578+
) -> QueryResult<()> {
1579+
assert!(!column.is_list());
1580+
1581+
// Generate
1582+
// from unnest({parent_ids}) as p(id), children c
1583+
// where c.{parent_field} = p.id
1584+
// and .. other conditions on c ..
1585+
// limit {parent_ids.len} + 1
1586+
1587+
out.push_sql("\n/* child_type_b */ from unnest(");
1588+
column.bind_ids(&self.ids, out)?;
1589+
out.push_sql(") as p(id), ");
1590+
out.push_sql(self.table.qualified_name.as_str());
1591+
out.push_sql(" c where ");
1592+
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1593+
limit.filter(out);
1594+
out.push_sql(" and p.id = c.");
1595+
out.push_identifier(column.name.as_str())?;
1596+
self.and_filter(out.reborrow())?;
1597+
limit.single_limit(self.ids.len(), out);
1598+
Ok(())
1599+
}
1600+
15411601
fn children_type_c(
15421602
&self,
15431603
child_ids: &Vec<Vec<Option<SafeString>>>,
@@ -1557,7 +1617,7 @@ impl<'a> FilterWindow<'a> {
15571617
// limit {first} offset {skip}) c
15581618
// order by c.{sort_key}
15591619

1560-
out.push_sql("\n from ");
1620+
out.push_sql("\n/* children_type_c */ from ");
15611621
out.push_sql("rows from (unnest(");
15621622
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
15631623
out.push_sql("), reduce_dim(");
@@ -1588,7 +1648,7 @@ impl<'a> FilterWindow<'a> {
15881648
// where c.id = p.child_id
15891649
// and .. other conditions on c ..
15901650

1591-
out.push_sql("\n from rows from (unnest(");
1651+
out.push_sql("\n/* child_type_d */ from rows from (unnest(");
15921652
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
15931653
out.push_sql("), unnest(");
15941654
self.table.primary_key().bind_ids(&child_ids, out)?;
@@ -1611,11 +1671,18 @@ impl<'a> FilterWindow<'a> {
16111671
mut out: AstPass<Pg>,
16121672
) -> QueryResult<()> {
16131673
match &self.link {
1614-
TableLink::Direct(column) => {
1674+
TableLink::Direct(column, multiplicity) => {
1675+
use ChildMultiplicity::*;
16151676
if column.is_list() {
1616-
self.children_type_a(column, limit, block, &mut out)
1677+
match multiplicity {
1678+
Many => self.children_type_a(column, limit, block, &mut out),
1679+
Single => self.child_type_a(column, limit, block, &mut out),
1680+
}
16171681
} else {
1618-
self.children_type_b(column, limit, block, &mut out)
1682+
match multiplicity {
1683+
Many => self.children_type_b(column, limit, block, &mut out),
1684+
Single => self.child_type_b(column, limit, block, &mut out),
1685+
}
16191686
}
16201687
}
16211688
TableLink::Parent(ParentIds::List(child_ids)) => {

store/postgres/tests/relational_bytes.rs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,18 @@ fn delete() {
332332
const ROOT: &str = "dead00";
333333
const CHILD1: &str = "babe01";
334334
const CHILD2: &str = "babe02";
335-
335+
const GRANDCHILD1: &str = "fafa01";
336+
const GRANDCHILD2: &str = "fafa02";
337+
338+
/// Create a set of test data that forms a tree through the `parent` and `children` attributes.
339+
/// The tree has this form:
340+
///
341+
/// root
342+
/// +- child1
343+
/// +- grandchild1
344+
/// +- child2
345+
/// +- grandchild2
346+
///
336347
fn make_thing_tree(conn: &PgConnection, layout: &Layout) -> (Entity, Entity, Entity) {
337348
let root = entity! {
338349
id: ROOT,
@@ -342,16 +353,31 @@ fn make_thing_tree(conn: &PgConnection, layout: &Layout) -> (Entity, Entity, Ent
342353
let child1 = entity! {
343354
id: CHILD1,
344355
name: "child1",
345-
parent: "dead00"
356+
parent: "dead00",
357+
children: vec![GRANDCHILD1]
346358
};
347359
let child2 = entity! {
348360
id: CHILD2,
349361
name: "child2",
350-
parent: "dead00"
362+
parent: "dead00",
363+
children: vec![GRANDCHILD1]
364+
};
365+
let grand_child1 = entity! {
366+
id: GRANDCHILD1,
367+
name: "grandchild1",
368+
parent: CHILD1
351369
};
370+
let grand_child2 = entity! {
371+
id: GRANDCHILD2,
372+
name: "grandchild2",
373+
parent: CHILD2
374+
};
375+
352376
insert_entity(conn, layout, "Thing", root.clone());
353377
insert_entity(conn, layout, "Thing", child1.clone());
354378
insert_entity(conn, layout, "Thing", child2.clone());
379+
insert_entity(conn, layout, "Thing", grand_child1.clone());
380+
insert_entity(conn, layout, "Thing", grand_child2.clone());
355381
(root, child1, child2)
356382
}
357383

@@ -375,6 +401,12 @@ fn query() {
375401
}
376402

377403
run_test(|conn, layout| -> Result<(), ()> {
404+
// This test exercises the different types of queries we generate;
405+
// the type of query is based on knowledge of what the test data
406+
// looks like, not on just an inference from the GraphQL model.
407+
// Especially the multiplicity for type A and B queries is determined
408+
// by knowing whether there are one or many entities per parent
409+
// in the test data
378410
make_thing_tree(conn, layout);
379411

380412
// See https://graphprotocol.github.io/rfcs/engineering-plans/0001-graphql-query-prefetching.html#handling-parentchild-relationships
@@ -383,9 +415,9 @@ fn query() {
383415
// EntityCollection::All
384416
let coll = EntityCollection::All(vec!["Thing".to_owned()]);
385417
let things = fetch(conn, layout, coll);
386-
assert_eq!(vec![CHILD1, CHILD2, ROOT], things);
418+
assert_eq!(vec![CHILD1, CHILD2, ROOT, GRANDCHILD1, GRANDCHILD2], things);
387419

388-
// EntityCollection::Window, type A
420+
// EntityCollection::Window, type A, many
389421
// things(where: { children_contains: [CHILD1] }) { id }
390422
let coll = EntityCollection::Window(vec![EntityWindow {
391423
child_type: "Thing".to_owned(),
@@ -398,19 +430,45 @@ fn query() {
398430
let things = fetch(conn, layout, coll);
399431
assert_eq!(vec![ROOT], things);
400432

401-
// EntityCollection::Window, type B
433+
// EntityCollection::Window, type A, single
434+
// things(where: { children_contains: [GRANDCHILD1, GRANDCHILD2] }) { id }
435+
let coll = EntityCollection::Window(vec![EntityWindow {
436+
child_type: "Thing".to_owned(),
437+
ids: vec![GRANDCHILD1.to_owned(), GRANDCHILD2.to_owned()],
438+
link: EntityLink::Direct(
439+
WindowAttribute::List("children".to_string()),
440+
ChildMultiplicity::Single,
441+
),
442+
}]);
443+
let things = fetch(conn, layout, coll);
444+
assert_eq!(vec![CHILD1, CHILD2], things);
445+
446+
// EntityCollection::Window, type B, many
402447
// things(where: { parent: [ROOT] }) { id }
403448
let coll = EntityCollection::Window(vec![EntityWindow {
404449
child_type: "Thing".to_owned(),
405450
ids: vec![ROOT.to_owned()],
406451
link: EntityLink::Direct(
407452
WindowAttribute::Scalar("parent".to_string()),
408-
ChildMultiplicity::Single,
453+
ChildMultiplicity::Many,
409454
),
410455
}]);
411456
let things = fetch(conn, layout, coll);
412457
assert_eq!(vec![CHILD1, CHILD2], things);
413458

459+
// EntityCollection::Window, type B, single
460+
// things(where: { parent: [CHILD1, CHILD2] }) { id }
461+
let coll = EntityCollection::Window(vec![EntityWindow {
462+
child_type: "Thing".to_owned(),
463+
ids: vec![CHILD1.to_owned(), CHILD2.to_owned()],
464+
link: EntityLink::Direct(
465+
WindowAttribute::Scalar("parent".to_string()),
466+
ChildMultiplicity::Single,
467+
),
468+
}]);
469+
let things = fetch(conn, layout, coll);
470+
assert_eq!(vec![GRANDCHILD1, GRANDCHILD2], things);
471+
414472
// EntityCollection::Window, type C
415473
// things { children { id } }
416474
// This is the inner 'children' query

0 commit comments

Comments
 (0)