Skip to content

Commit

Permalink
docs: MERGE examples (#1188)
Browse files Browse the repository at this point in the history
  • Loading branch information
igalklebanov authored Oct 24, 2024
1 parent 5b79e88 commit cb4914a
Show file tree
Hide file tree
Showing 16 changed files with 241 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"script:copy-interface-doc": "node scripts/copy-interface-documentation.js",
"script:add-deno-type-references": "node scripts/add-deno-type-references.js",
"script:align-site-version": "node scripts/align-site-version.js",
"script:generate-site-examples": "node scripts/generate-site-examples.js",
"prepublishOnly": "npm run build"
},
"author": "Sami Koskimäki <[email protected]>",
Expand Down
45 changes: 40 additions & 5 deletions scripts/generate-site-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ const moreExamplesByCategory = {
'returning method':
'https://kysely-org.github.io/kysely-apidoc/classes/DeleteQueryBuilder.html#returning',
},
merge: {
'mergeInto method':
'https://kysely-org.github.io/kysely-apidoc/classes/Kysely.html#mergeInto',
'using method':
'https://kysely-org.github.io/kysely-apidoc/classes/MergeQueryBuilder.html#using',
'whenMatched method':
'https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenMatched',
'thenUpdateSet method':
'https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenUpdateSet',
'thenDelete method':
'https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDelete',
'thenDoNothing method':
'https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDoNothing',
'whenNotMatched method':
'https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenNotMatched',
'thenInsertValues method':
'https://kysely-org.github.io/kysely-apidoc/classes/NotMatchedThenableMergeQueryBuilder.html#thenInsertValues',
},
transactions: {
'transaction method':
'https://kysely-org.github.io/kysely-apidoc/classes/Kysely.html#transaction',
Expand All @@ -88,6 +106,7 @@ function main() {
const lines = readLines(filePath)
const state = {
filePath,
/** @type {string | null} */
line: null,
lineIndex: 0,
annotation: null,
Expand Down Expand Up @@ -153,7 +172,8 @@ function writeSiteExample(state) {
const codeVariable = _.camelCase(name)

const fileName = `${priority.padStart(4, '0')}-${_.kebabCase(name)}`
const filePath = path.join(SITE_EXAMPLE_PATH, category, fileName)
const folderPath = path.join(SITE_EXAMPLE_PATH, category)
const filePath = path.join(folderPath, fileName)

const codeFile = `export const ${codeVariable} = \`${deindent(code)
.replaceAll('`', '\\`')
Expand Down Expand Up @@ -197,7 +217,22 @@ function writeSiteExample(state) {

const exampleFile = parts.join('\n')

fs.writeFileSync(filePath + '.js', codeFile)
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath, { recursive: true })
fs.writeFileSync(
path.join(folderPath, '_category_.json'),
`{
"label": "${_.startCase(category)}",
"position": 0, // TODO: set the position of the category.
"link": {
"type": "generated-index",
"description": "Short and simple examples of using the ${category} functionality." // TODO: review this.
}
}`,
)
}

fs.writeFileSync(filePath + '.js', codeFile, {})
fs.writeFileSync(filePath + '.mdx', exampleFile)
}

Expand Down Expand Up @@ -298,10 +333,10 @@ function deindent(str) {
let ws = Number.MAX_SAFE_INTEGER
for (const line of lines) {
if (line.trim().length > 0) {
const wsExec = /^(\s*)/.exec(line)
const [, wsExec] = /^(\s*)/.exec(line) || []

if (wsExec[1].length < ws) {
ws = wsExec[1].length
if (wsExec != null && wsExec.length < ws) {
ws = wsExec.length
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion site/docs/examples/cte/_category_.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"label": "CTE",
"position": 7,
"position": 9,
"link": {
"type": "generated-index",
"description": "Short and simple examples of how to use Common Table Expressions (CTE) in queries."
Expand Down
10 changes: 10 additions & 0 deletions site/docs/examples/merge/0010-source-row-existence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const sourceRowExistence = `const result = await db
.mergeInto('person as target')
.using('pet as source', 'source.owner_id', 'target.id')
.whenMatchedAnd('target.has_pets', '!=', 'Y')
.thenUpdateSet({ has_pets: 'Y' })
.whenNotMatchedBySourceAnd('target.has_pets', '=', 'Y')
.thenUpdateSet({ has_pets: 'N' })
.executeTakeFirstOrThrow()
console.log(result.numChangedRows)`
36 changes: 36 additions & 0 deletions site/docs/examples/merge/0010-source-row-existence.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: 'Source row existence'
---

# Source row existence

Update a target column based on the existence of a source row:

import {
Playground,
exampleSetup,
} from '../../../src/components/Playground'

import {
sourceRowExistence
} from './0010-source-row-existence'

<div style={{ marginBottom: '1em' }}>
<Playground code={sourceRowExistence} setupCode={exampleSetup} />
</div>

:::info[More examples]
The API documentation is packed with examples. The API docs are hosted [here](https://kysely-org.github.io/kysely-apidoc/),
but you can access the same documentation by hovering over functions/methods/classes in your IDE. The examples are always
just one hover away!

For example, check out these sections:
- [mergeInto method](https://kysely-org.github.io/kysely-apidoc/classes/Kysely.html#mergeInto)
- [using method](https://kysely-org.github.io/kysely-apidoc/classes/MergeQueryBuilder.html#using)
- [whenMatched method](https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenMatched)
- [thenUpdateSet method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenUpdateSet)
- [thenDelete method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDelete)
- [thenDoNothing method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDoNothing)
- [whenNotMatched method](https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenNotMatched)
- [thenInsertValues method](https://kysely-org.github.io/kysely-apidoc/classes/NotMatchedThenableMergeQueryBuilder.html#thenInsertValues)
:::
23 changes: 23 additions & 0 deletions site/docs/examples/merge/0020-temporary-changes-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const temporaryChangesTable = `const result = await db
.mergeInto("wine as target")
.using(
"wine_stock_change as source",
"source.wine_name",
"target.name",
)
.whenNotMatchedAnd("source.stock_delta", ">", 0)
.thenInsertValues(({ ref }) => ({
name: ref("source.wine_name"),
stock: ref("source.stock_delta"),
}))
.whenMatchedAnd(
(eb) => eb("target.stock", "+", eb.ref("source.stock_delta")),
">",
0,
)
.thenUpdateSet("stock", (eb) =>
eb("target.stock", "+", eb.ref("source.stock_delta")),
)
.whenMatched()
.thenDelete()
.executeTakeFirstOrThrow()`
36 changes: 36 additions & 0 deletions site/docs/examples/merge/0020-temporary-changes-table.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: 'Temporary changes table'
---

# Temporary changes table

Merge new entries from a temporary changes table:

import {
Playground,
exampleSetup,
} from '../../../src/components/Playground'

import {
temporaryChangesTable
} from './0020-temporary-changes-table'

<div style={{ marginBottom: '1em' }}>
<Playground code={temporaryChangesTable} setupCode={exampleSetup} />
</div>

:::info[More examples]
The API documentation is packed with examples. The API docs are hosted [here](https://kysely-org.github.io/kysely-apidoc/),
but you can access the same documentation by hovering over functions/methods/classes in your IDE. The examples are always
just one hover away!

For example, check out these sections:
- [mergeInto method](https://kysely-org.github.io/kysely-apidoc/classes/Kysely.html#mergeInto)
- [using method](https://kysely-org.github.io/kysely-apidoc/classes/MergeQueryBuilder.html#using)
- [whenMatched method](https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenMatched)
- [thenUpdateSet method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenUpdateSet)
- [thenDelete method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDelete)
- [thenDoNothing method](https://kysely-org.github.io/kysely-apidoc/classes/MatchedThenableMergeQueryBuilder.html#thenDoNothing)
- [whenNotMatched method](https://kysely-org.github.io/kysely-apidoc/classes/WheneableMergeQueryBuilder.html#whenNotMatched)
- [thenInsertValues method](https://kysely-org.github.io/kysely-apidoc/classes/NotMatchedThenableMergeQueryBuilder.html#thenInsertValues)
:::
8 changes: 8 additions & 0 deletions site/docs/examples/merge/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "MERGE",
"position": 7,
"link": {
"type": "generated-index",
"description": "Short and simple examples of how to write MERGE queries."
}
}
2 changes: 0 additions & 2 deletions site/docs/examples/select/0070-distinct.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ title: 'Distinct'

# Distinct

### Examples

import {
Playground,
exampleSetup,
Expand Down
2 changes: 1 addition & 1 deletion site/docs/examples/transactions/_category_.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"label": "Transactions",
"position": 6,
"position": 8,
"link": {
"type": "generated-index",
"description": "Short and simple examples of how to use transactions."
Expand Down
2 changes: 1 addition & 1 deletion site/package-lock.json

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

2 changes: 1 addition & 1 deletion site/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kysely",
"version": "0.27.3",
"version": "0.27.4",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
Expand Down
19 changes: 17 additions & 2 deletions site/src/components/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ interface PlaygroundState {
export const exampleSetup = `import { Generated } from 'kysely'
export interface Database {
person: PersonTable
pet: PetTable
person: PersonTable
pet: PetTable
wine: WineTable
wine_stock_change: WineStockChangeTable
}
interface PersonTable {
Expand All @@ -87,6 +89,7 @@ interface PersonTable {
last_name: string | null
created_at: Generated<Date>
age: number
has_pets: Generated<'Y' | 'N'>
}
interface PetTable {
Expand All @@ -96,4 +99,16 @@ interface PetTable {
species: 'cat' | 'dog'
is_favorite: boolean
}
interface WineTable {
id: Generated<string>
name: string
stock: number
}
interface WineStockChangeTable {
id: Generated<string>
wine_name: string
stock_delta: number
}
`
5 changes: 5 additions & 0 deletions site/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
-14.8px 50.6px 59.3px -2.5px hsl(var(--shadow-color) / 0.1);
}

/* remove default theme max-width in examples content */
:root [class^='col docItemCol_'] {
max-width: unset !important;
}

[data-theme='dark'] {
--ifm-color-primary: var(--sky9);
--ifm-color-primary-dark: var(--sky10);
Expand Down
4 changes: 2 additions & 2 deletions src/query-builder/select-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,10 @@ export interface SelectQueryBuilder<DB, TB extends keyof DB, O>
/**
* Makes the selection distinct.
*
* <!-- siteExample("select", "Distinct", 70) -->
*
* ### Examples
*
* <!-- siteExample("select", "Distinct", 70) -->
*
* ```ts
* const persons = await db.selectFrom('person')
* .select('first_name')
Expand Down
69 changes: 59 additions & 10 deletions src/query-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,14 +512,18 @@ export class QueryCreator<DB> {
*
* ### Examples
*
* <!-- siteExample("merge", "Source row existence", 10) -->
*
* Update a target column based on the existence of a source row:
*
* ```ts
* const result = await db
* .mergeInto('person')
* .using('pet', 'pet.owner_id', 'person.id')
* .whenMatched((and) => and('has_pets', '!=', 'Y'))
* .mergeInto('person as target')
* .using('pet as source', 'source.owner_id', 'target.id')
* .whenMatchedAnd('target.has_pets', '!=', 'Y')
* .thenUpdateSet({ has_pets: 'Y' })
* .whenNotMatched()
* .thenDoNothing()
* .whenNotMatchedBySourceAnd('target.has_pets', '=', 'Y')
* .thenUpdateSet({ has_pets: 'N' })
* .executeTakeFirstOrThrow()
*
* console.log(result.numChangedRows)
Expand All @@ -529,11 +533,56 @@ export class QueryCreator<DB> {
*
* ```sql
* merge into "person"
* using "pet" on "pet"."owner_id" = "person"."id"
* when matched and "has_pets" != $1 then
* update set "has_pets" = $2
* when not matched then
* do nothing
* using "pet"
* on "pet"."owner_id" = "person"."id"
* when matched and "has_pets" != $1
* then update set "has_pets" = $2
* when not matched by source and "has_pets" = $3
* then update set "has_pets" = $4
* ```
*
* <!-- siteExample("merge", "Temporary changes table", 20) -->
*
* Merge new entries from a temporary changes table:
*
* ```ts
* const result = await db
* .mergeInto("wine as target")
* .using(
* "wine_stock_change as source",
* "source.wine_name",
* "target.name",
* )
* .whenNotMatchedAnd("source.stock_delta", ">", 0)
* .thenInsertValues(({ ref }) => ({
* name: ref("source.wine_name"),
* stock: ref("source.stock_delta"),
* }))
* .whenMatchedAnd(
* (eb) => eb("target.stock", "+", eb.ref("source.stock_delta")),
* ">",
* 0,
* )
* .thenUpdateSet("stock", (eb) =>
* eb("target.stock", "+", eb.ref("source.stock_delta")),
* )
* .whenMatched()
* .thenDelete()
* .executeTakeFirstOrThrow()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* merge into "wine" as "target"
* using "wine_stock_change" as "source"
* on "source"."wine_name" = "target"."name"
* when not matched and "source"."stock_delta" > $1
* then insert ("name", "stock") values ("source"."wine_name", "source"."stock_delta")
* when matched and "target"."stock" + "source"."stock_delta" > $2
* then update set "stock" = "target"."stock" + "source"."stock_delta"
* when matched
* then delete
* ```
*/
mergeInto<TR extends keyof DB & string>(
Expand Down

0 comments on commit cb4914a

Please sign in to comment.