Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passing a unique column to queryBuilder for UPSERT st… #63

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/queryBuilder.ts
Expand Up @@ -32,6 +32,7 @@ export type GenerateQueryOptions<T extends object> = {
orderBy?: OrderBy<T> | OrderBy<T>[];
data?: Partial<T>;
upsertOnlyUpdateData?: Partial<T>;
uniqueColumn?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you run this code against D1 to confirm it works?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the type can be a little more strict but otherwise I have tested this on a D1 preview and it works fine.

};

/**
Expand Down Expand Up @@ -171,9 +172,10 @@ export function GenerateQuery<T extends object>(
.split("")
.join(", ")})`;

const primaryKeyStr = Array.isArray(primaryKeys)
const primaryKeyStr = options.uniqueColumn || (Array.isArray(primaryKeys)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we can't just use the primary key value for this? It could use a rename - but functionally it appears to do exactly the same

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this covers a completely different use case, for example I have a table where the primary key is id but when upserting I want to use a different unique column like email, api_key or something similar.

I probably would not want to update the primary key to be email or api_key instead of id. does that make sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we instead renamed the variable to something like conflictConstraint, that would demonstrate it's purpose better, and still allow you to do this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting to change options.uniqueColumn to options.conflictConstraint or you are thinking more like renaming the primaryKeys in the options passed to the model constructor?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's the later then do you not think that would cause issues with the migrations? I was under the impression that you can also create the table using the model configurations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do mean the latter. This would ideally:

  • Rename the variable in the Query builder
  • Edit the Model#Upsert method to have a new optional parameter for constraint columns, which will default to the primary key(s) if not already present

? primaryKeys.join(", ")
: primaryKeys;
: primaryKeys);

query += ` ON CONFLICT (${primaryKeyStr}) DO UPDATE SET`;
query += ` ${updateDataKeys.map((key) => `${key} = ?`).join(", ")}`;
query += ` WHERE ${whereKeys.map((key) => `${key} = ?`).join(" AND ")}`;
Expand Down
9 changes: 9 additions & 0 deletions test/querybuilder.test.js
Expand Up @@ -291,6 +291,15 @@ describe("Query Builder", () => {
upsertOnlyUpdateData: { id: 1 },
})
).to.not.throw();

expect(() =>
GenerateQuery(QueryType.UPSERT, "test", {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fault of your test in particular, but we really should be testing the output of these queries too

data: { id: 1 },
where: { id: 1 },
upsertOnlyUpdateData: { id: 1 },
uniqueColumn: 'email'
})
).to.not.throw();
});
it("should generate a basic query", () => {
const statement = GenerateQuery(QueryType.UPSERT, "test", {
Expand Down