Skip to content

Commit

Permalink
feat(pg): support ordering with NULLS FIRST / NULLS LAST (#332)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattbretl authored and benjie committed Nov 21, 2018
1 parent 4a05670 commit 545d082
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 11 deletions.
4 changes: 2 additions & 2 deletions packages/graphile-build-pg/src/QueryBuilder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class QueryBuilder {
public where(exprGen: SQLGen): void;
public whereBound(exprGen: SQLGen, isLower: boolean): void;
public setOrderIsUnique(): void;
public orderBy(exprGen: SQLGen, ascending: boolean): void;
public orderBy(exprGen: SQLGen, ascending: boolean, nullsFirst: boolean): void;
public limit(limitGen: NumberGen): void;
public offset(offsetGen: NumberGen): void;
public first(first: number): void;
Expand All @@ -45,7 +45,7 @@ export default class QueryBuilder {
};
public getFinalOffset(): number;
public getFinalLimit(): number;
public getOrderByExpressionsAndDirections(): Array<[SQL, boolean]>;
public getOrderByExpressionsAndDirections(): Array<[SQL, boolean, boolean]>;
public getSelectFieldsCount(): number;
public buildSelectFields(): SQL;
public buildSelectJson({ addNullCase }: { addNullCase?: boolean }): SQL;
Expand Down
19 changes: 13 additions & 6 deletions packages/graphile-build-pg/src/QueryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class QueryBuilder {
lower: Array<SQLGen>,
upper: Array<SQLGen>,
},
orderBy: Array<[SQLGen, boolean]>,
orderBy: Array<[SQLGen, boolean, boolean]>,
orderIsUnique: boolean,
limit: ?NumberGen,
offset: ?NumberGen,
Expand All @@ -88,7 +88,7 @@ class QueryBuilder {
lower: Array<SQL>,
upper: Array<SQL>,
},
orderBy: Array<[SQL, boolean]>,
orderBy: Array<[SQL, boolean, boolean]>,
orderIsUnique: boolean,
limit: ?number,
offset: ?number,
Expand Down Expand Up @@ -236,9 +236,9 @@ class QueryBuilder {
setOrderIsUnique() {
this.data.orderIsUnique = true;
}
orderBy(exprGen: SQLGen, ascending: boolean = true) {
orderBy(exprGen: SQLGen, ascending: boolean = true, nullsFirst: boolean) {
this.checkLock("orderBy");
this.data.orderBy.push([exprGen, ascending]);
this.data.orderBy.push([exprGen, ascending, nullsFirst]);
}
limit(limitGen: NumberGen) {
this.checkLock("limit");
Expand Down Expand Up @@ -473,11 +473,17 @@ class QueryBuilder {
this.compiledData.orderBy.length
? sql.fragment`order by ${sql.join(
this.compiledData.orderBy.map(
([expr, ascending]) =>
([expr, ascending, nullsFirst]) =>
sql.fragment`${expr} ${
Number(ascending) ^ Number(flip)
? sql.fragment`ASC`
: sql.fragment`DESC`
}${
nullsFirst === true
? sql.fragment` NULLS FIRST`
: nullsFirst === false
? sql.fragment` NULLS LAST`
: null
}`
),
","
Expand Down Expand Up @@ -554,9 +560,10 @@ class QueryBuilder {
}, []);
} else if (type === "orderBy") {
const context = getContext();
this.compiledData[type] = this.data[type].map(([a, b]) => [
this.compiledData[type] = this.data[type].map(([a, b, c]) => [
callIfNecessary(a, context),
b,
c,
]);
} else if (type === "from") {
if (this.data.from) {
Expand Down
14 changes: 11 additions & 3 deletions packages/graphile-build-pg/src/plugins/PgConnectionArgOrderBy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import isString from "lodash/isString";
import type { Plugin } from "graphile-build";

export default (function PgConnectionArgOrderBy(builder) {
export default (function PgConnectionArgOrderBy(builder, { orderByNullsLast }) {
builder.hook("init", (_, build) => {
const {
newWithHooks,
Expand Down Expand Up @@ -121,13 +121,21 @@ export default (function PgConnectionArgOrderBy(builder) {
Array.isArray(specs[0]) || specs.length === 0
? specs
: [specs];
orders.forEach(([col, ascending]) => {
orders.forEach(([col, ascending, specNullsFirst]) => {
const expr = isString(col)
? sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier(
col
)}`
: col;
queryBuilder.orderBy(expr, ascending);
// If the enum specifies null ordering, use that
// Otherwise, use the orderByNullsLast option if present
const nullsFirst =
specNullsFirst != null
? specNullsFirst
: orderByNullsLast != null
? !orderByNullsLast
: undefined;
queryBuilder.orderBy(expr, ascending, nullsFirst);
});
if (unique) {
queryBuilder.setOrderIsUnique();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
asc: allSimilarTable1S(orderBy: COL2_ASC) {
nodes {
nodeId
col2
}
}
desc: allSimilarTable1S(orderBy: COL2_DESC) {
nodes {
nodeId
col2
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2007,6 +2007,61 @@ Object {
}
`;
exports[`orderByNullsLast.graphql 1`] = `
Object {
"data": Object {
"asc": Object {
"nodes": Array [
Object {
"col2": 1,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiw1XQ==",
},
Object {
"col2": 4,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwzXQ==",
},
Object {
"col2": 6,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwxXQ==",
},
Object {
"col2": null,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwyXQ==",
},
Object {
"col2": null,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiw0XQ==",
},
],
},
"desc": Object {
"nodes": Array [
Object {
"col2": 6,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwxXQ==",
},
Object {
"col2": 4,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwzXQ==",
},
Object {
"col2": 1,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiw1XQ==",
},
Object {
"col2": null,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiwyXQ==",
},
Object {
"col2": null,
"nodeId": "WyJzaW1pbGFyX3RhYmxlXzFTIiw0XQ==",
},
],
},
},
}
`;
exports[`posts.graphql 1`] = `
Object {
"data": Object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ beforeAll(() => {
viewUniqueKey,
dSchema,
simpleCollections,
orderByNullsLast,
] = await Promise.all([
createPostGraphileSchema(pgClient, ["a", "b", "c"]),
createPostGraphileSchema(pgClient, ["a", "b", "c"], { classicIds: true }),
Expand All @@ -61,6 +62,11 @@ beforeAll(() => {
createPostGraphileSchema(pgClient, ["a", "b", "c"], {
simpleCollections: "both",
}),
createPostGraphileSchema(pgClient, ["a"], {
graphileBuildOptions: {
orderByNullsLast: true,
},
}),
]);
// Now for RBAC-enabled tests
await pgClient.query("set role postgraphile_test_authenticator");
Expand All @@ -78,6 +84,7 @@ beforeAll(() => {
viewUniqueKey,
dSchema,
simpleCollections,
orderByNullsLast,
rbac,
};
});
Expand Down Expand Up @@ -119,6 +126,7 @@ beforeAll(() => {
"simple-procedure-computed-fields.graphql":
gqlSchemas.simpleCollections,
"simple-procedure-query.graphql": gqlSchemas.simpleCollections,
"orderByNullsLast.graphql": gqlSchemas.orderByNullsLast,
};
let gqlSchema = schemas[fileName];
if (!gqlSchema) {
Expand Down

0 comments on commit 545d082

Please sign in to comment.