Skip to content

Commit

Permalink
code-gen: support multiple 'viaXxx' in the query builder (#661)
Browse files Browse the repository at this point in the history
This works by intersecting the result of multiple traverses. This guarantees the same 'AND' behaviour as usual. It also works when the 'xxIn' property is already set, by converting it inline to a 'select from values' expression.

Closes #660
  • Loading branch information
dirkdev98 authored Feb 5, 2021
1 parent 96ab3e7 commit 6a798ee
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 36 deletions.
27 changes: 25 additions & 2 deletions packages/code-gen/src/generator/sql/query-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { addToData } from "../../generate.js";
import { upperCaseFirst } from "../../utils.js";
import { js } from "../tag/tag.js";
import { getTypeNameForType } from "../types.js";
import { typeTable } from "./structure.js";
import { getPrimaryKeyWithType, getQueryEnabledObjects } from "./utils.js";

/**
Expand Down Expand Up @@ -397,13 +398,35 @@ if (!isNil(builder.${key}.limit)) {
}
`;

// Note that we need to the xxxIn params first before we can add xxxIn and set it
// to a query. The user may have set it to an array or another traverser may have
// set a query object already. To get the same guarantees ('AND') we convert
// arrays with values to a query part & if a query part exists, add 'INTERSECT'.
let sqlCastType = typeTable[type.keys[ownKey].type];
if (typeof sqlCastType === "function") {
sqlCastType = sqlCastType(type.keys[ownKey].type, false);
}
const traverseJoinPart = js`
if (builder.via${upperCaseFirst(relationKey)}) {
builder.where = builder.where ?? {};
// Prepare ${ownKey}In
if (isQueryPart(builder.where.${ownKey}In)) {
builder.where.${ownKey}In.append(query\` INTERSECT \`);
} else if (Array.isArray(builder.where.${ownKey}In) && builder.where.${ownKey}In.length > 0) {
builder.where.${ownKey}In =
query([
"(SELECT value::${sqlCastType} FROM(values (",
...builder.where.${ownKey}In.map(() => "").join("), ("),
")) as ids(value)) INTERSECT "
], ...builder.where.${ownKey}In)
} else {
builder.where.${ownKey}In = query\`\`;
}
${getLimitOffset(true)}
builder.where.${ownKey}In = query\`
builder.where.${ownKey}In.append(query\`
SELECT DISTINCT ${otherShortName}."${referencedKey}"
$\{internalQuery${
upperCaseFirst(otherSide.name) +
Expand All @@ -413,7 +436,7 @@ if (!isNil(builder.${key}.limit)) {
}(
builder.via${upperCaseFirst(relationKey)})}
$\{offsetLimitQb}
\`;
\`);
}
`;

Expand Down
15 changes: 9 additions & 6 deletions packages/code-gen/src/generator/sql/structure.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { upperCaseFirst } from "../../utils.js";
import { getQueryEnabledObjects, getSortedKeysForType } from "./utils.js";
import { getSearchableFields } from "./where-type.js";

const typeTable = {
export const typeTable = {
any: "jsonb",
anyOf: "jsonb",
array: "jsonb",
Expand All @@ -13,16 +13,19 @@ const typeTable = {
/**
*
* @param {CodeGenNumberType} type
* @param {boolean} skipPrimary
* @returns {string}
*/ number: (type) =>
!type.sql?.primary
*/ number: (type, skipPrimary) =>
!type.sql?.primary || skipPrimary
? type.validator.floatingPoint
? "float"
: "int"
: "BIGSERIAL PRIMARY KEY",
object: "jsonb",
string: (type) => (type.sql?.primary ? "varchar PRIMARY KEY" : "varchar"),
uuid: (type) => (type.sql?.primary ? "uuid PRIMARY KEY" : "uuid"),
string: (type, skipPrimary) =>
type.sql?.primary && !skipPrimary ? "varchar PRIMARY KEY" : "varchar",
uuid: (type, skipPrimary) =>
type.sql?.primary && !skipPrimary ? "uuid PRIMARY KEY" : "uuid",
};

/**
Expand Down Expand Up @@ -77,7 +80,7 @@ function getFields(object) {

let sqlType = typeTable[type.type];
if (typeof sqlType === "function") {
sqlType = sqlType(type);
sqlType = sqlType(type, false);
}

let defaultValue = "";
Expand Down
17 changes: 17 additions & 0 deletions packages/code-gen/test/sql.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,23 @@ test("code-gen/e2e/sql", async (t) => {
t.equal(dbUser.id, user.id);
});

t.test("traverse with 'via' and idIn", async (t) => {
const [dbUser] = await client
.queryUser({
where: {
idIn: [post.writer],
},
viaPosts: {
where: {
id: post.id,
},
},
})
.exec(sql);

t.equal(dbUser.id, user.id);
});

t.test("traverse via queryCategory", async (t) => {
const builder = {
viaPosts: {
Expand Down
44 changes: 40 additions & 4 deletions packages/store/src/generated/database/file.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6a798ee

Please sign in to comment.