Skip to content

Commit

Permalink
feat(GraphQL): allow nested queries where fields don't match
Browse files Browse the repository at this point in the history
The GraphQL nested queries up until this change relied on parent tables with foreign refs sharing
the same field name as the primary key on the child table. For example, trips could be joined to a
route on the shared field route_id. This change permits nesting children stops under a parent stop
using stops#parent_station, which will be joined on the parent join value found in stops#stop_id.

refs ibi-group/datatools-ui#428
  • Loading branch information
landonreed committed Feb 27, 2019
1 parent 5ec5285 commit 98194d0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
12 changes: 12 additions & 0 deletions src/main/java/com/conveyal/gtfs/graphql/GraphQLGtfsSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,18 @@ public class GraphQLGtfsSchema {
.field(MapFetcher.field("parent_station"))
.field(MapFetcher.field("location_type", GraphQLInt))
.field(MapFetcher.field("wheelchair_boarding", GraphQLInt))
// Returns all stops that reference parent stop's stop_id
.field(newFieldDefinition()
.name("child_stops")
.type(new GraphQLList(new GraphQLTypeReference("stop")))
.dataFetcher(new JDBCFetcher(
"stops",
"stop_id",
null,
false,
"parent_station"
))
.build())
.field(RowCountFetcher.field("stop_time_count", "stop_times", "stop_id"))
.field(newFieldDefinition()
.name("patterns")
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/conveyal/gtfs/graphql/fetchers/JDBCFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class JDBCFetcher implements DataFetcher<List<Map<String, Object>>> {
final String parentJoinField;
private final String sortField;
private final boolean autoLimit;
private final String childJoinField;

/**
* Constructor for tables that need neither restriction by a where clause nor sorting based on the enclosing entity.
Expand Down Expand Up @@ -96,10 +97,22 @@ public JDBCFetcher (String tableName, String parentJoinField) {
* tables that it is unnatural to expect a limit (e.g., shape points or pattern stops).
*/
public JDBCFetcher (String tableName, String parentJoinField, String sortField, boolean autoLimit) {
this(tableName, parentJoinField, sortField, autoLimit, null);
}

/**
*
* @param childJoinField The child table field that should be joined to the parent join field. This enables joining
* where references from the child table to the parent table do not share the same field name,
* e.g., stops#stop_id -> transfers#from_stop_id. This value defaults to parentJoinField if
* argument is null.
*/
public JDBCFetcher (String tableName, String parentJoinField, String sortField, boolean autoLimit, String childJoinField) {
this.tableName = tableName;
this.parentJoinField = parentJoinField;
this.sortField = sortField;
this.autoLimit = autoLimit;
this.childJoinField = childJoinField != null ? childJoinField : parentJoinField;
}

// We can't automatically generate JDBCFetcher based field definitions for inclusion in a GraphQL schema (as we
Expand Down Expand Up @@ -198,8 +211,8 @@ List<Map<String, Object>> getResults (
// If we are fetching an item nested within a GTFS entity in the GraphQL query, we want to add an SQL "where"
// clause. This could conceivably be done automatically, but it's clearer to just express the intent.
// Note, this is assuming the type of the field in the parent is a string.
if (parentJoinField != null && parentJoinValues != null && !parentJoinValues.isEmpty()) {
whereConditions.add(makeInClause(parentJoinField, parentJoinValues, preparedStatementParameters));
if (childJoinField != null && parentJoinValues != null && !parentJoinValues.isEmpty()) {
whereConditions.add(makeInClause(childJoinField, parentJoinValues, preparedStatementParameters));
}
if (sortField != null) {
// Sort field is not provided by user input, so it's ok to add here (i.e., it's not prone to SQL injection).
Expand Down Expand Up @@ -341,7 +354,7 @@ List<Map<String, Object>> getResults (
}
// This logging produces a lot of noise during testing due to large numbers of joined sub-queries
// LOG.info("table name={}", tableName);
LOG.debug("SQL: {}", preparedStatement.toString());
LOG.info("SQL: {}", preparedStatement.toString());
if (preparedStatement.execute()) {
ResultSet resultSet = preparedStatement.getResultSet();
ResultSetMetaData meta = resultSet.getMetaData();
Expand Down

0 comments on commit 98194d0

Please sign in to comment.