Skip to content

Commit

Permalink
Merge pull request #598 from drizzle-team/pre-rFzFZlS0sU
Browse files Browse the repository at this point in the history
Relation Queries
  • Loading branch information
AndriiSherman authored May 19, 2023
2 parents 3bc0ca8 + 64c9c3b commit 474f1a3
Show file tree
Hide file tree
Showing 91 changed files with 47,368 additions and 666 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
.vscode
dist
dist.new
*.tsbuildinfo
*.tgz
/*.sql
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ Drizzle ORM is being battle-tested on production projects by multiple teams 🚀
- [Zod schema generation](/drizzle-zod/README.md)
- Zero dependencies

## Documentation

Check the full documenation on [the website](https://orm.drizzle.team)

## Supported databases

| Database | Status | |
|:------------------------------------------------------|:------:|:-----------------------------------------------|
| PostgreSQL || [Docs](/drizzle-orm/src/pg-core/README.md) |
| MySQL || [Docs](/drizzle-orm/src/mysql-core/README.md) |
| SQLite || [Docs](/drizzle-orm/src/sqlite-core/README.md) |
| PostgreSQL || [Docs](https://orm.drizzle.team/docs/quick-start) |
| MySQL || [Docs](https://orm.drizzle.team/docs/quick-start) |
| SQLite || [Docs](https://orm.drizzle.team/docs/quick-start) |
| [Cloudflare D1](https://developers.cloudflare.com/d1) || [Docs](/examples/cloudflare-d1/README.md) |
| [libSQL](https://libsql.org) || [Docs](/examples/libsql/README.md) |
| [Turso](https://turso.tech) || [Docs](/examples/libsql/README.md) |
Expand Down
1 change: 0 additions & 1 deletion changelogs/drizzle-orm/0.25.5.md

This file was deleted.

186 changes: 186 additions & 0 deletions changelogs/drizzle-orm/0.26.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Drizzle ORM 0.26.0 is here 🎉

## README docs are fully tranferred to web

The documentation has been completely reworked and updated with additional examples and explanations. You can find it here: https://orm.drizzle.team.

Furthermore, the entire documentation has been made open source, allowing you to edit and add any information you deem important for the community.

Visit https://github.com/drizzle-team/drizzle-orm-docs to access the open-sourced documentation.

Additionally, you can create specific documentation issues in this repository

## New Features

Introducing our first helper built on top of Drizzle Core API syntax: **the Relational Queries!** 🎉

With Drizzle RQ you can do:

1. Any amount of relations that will be mapped for you
2. Including or excluding! specific columns. You can also combine these options
3. Harness the flexibility of the `where` statements, allowing you to define custom conditions beyond the predefined ones available in the Drizzle Core API.
4. Expand the functionality by incorporating additional extras columns using SQL templates. For more examples, refer to the documentation.

Most importantly, regardless of the size of your query, Drizzle will always generate a **SINGLE optimized query**.

This efficiency extends to the usage of **Prepared Statements**, which are fully supported within the Relational Query Builder.

For more info: [Prepared Statements in Relational Query Builder](https://orm.drizzle.team/rqb#prepared-statements)


**Example of setting one-to-many relations**

> As you can observe, `relations` are a distinct concept that coexists alongside the main Drizzle schema. You have the flexibility to opt-in or opt-out of them at any time without affecting the `drizzle-kit` migrations or the logic for Core API's types and runtime.
```ts
import { integer, serial, text, pgTable } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});

export const usersConfig = relations(users, ({ many }) => ({
posts: many(posts),
}));

export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
authorId: integer('author_id').notNull(),
});

export const postsConfig = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```

**Example of querying you database**

Step 1: Provide all tables and relations to `drizzle` function

> `drizzle` import depends on the database driver you're using
```ts
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/...';

const db = drizzle(client, { schema });

await db.query.users.findMany(...);
```

If you have schema in multiple files

```ts
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/...';

const db = drizzle(client, { schema: { ...schema1, ...schema2 } });

await db.query.users.findMany(...);
```

Step 2: Query your database with Relational Query Builder

**Select all users**
```ts
const users = await db.query.users.findMany();
```
**Select first users**
> `.findFirst()` will add limit 1 to the query
```ts
const user = await db.query.users.findFirst();
```
**Select all users**
Get all posts with just `id`, `content` and include `comments`
```ts
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});
```
**Select all posts excluding `content` column**
```ts
const posts = await db.query.posts.findMany({
columns: {
content: false,
},
});
```

For more examples you can check [full docs](https://orm.drizzle.team/rqb) for Relational Queries

## Bug fixes

- 🐛 Fixed partial joins with prefixed tables (#542)

## Drizzle Kit updates

### New ways to define drizzle config file

You can now specify the configuration not only in the `.json` format but also in `.ts` and `.js` formats.

</br>

**TypeScript example**
```ts
import { Config } from "drizzle-kit";

export default {
schema: "",
connectionString: process.env.DB_URL,
out: "",
breakpoints: true
} satisfies Config;
```

**JavaScript example**
```js
/** @type { import("drizzle-kit").Config } */
export default {
schema: "",
connectionString: "",
out: "",
breakpoints: true
};
```

## New commands 🎉

### `drizzle-kit push:mysql`

You can now push your MySQL schema directly to the database without the need to create and manage migration files. This feature proves to be particularly useful for rapid local development and when working with PlanetScale databases.

By pushing the MySQL schema directly to the database, you can streamline the development process and avoid the overhead of managing migration files. This allows for more efficient iteration and quick deployment of schema changes during local development.

### How to setup your codebase for drizzle-kit push feature?

1. For this feature, you need to create a `drizzle.config.[ts|js|json]` file. We recommend using `.ts` or `.js` files as they allow you to easily provide the database connection information as secret variables

You'll need to specify `schema` and `connectionString`(or `db`, `port`, `host`, `password`, etc.) to make `drizzle-kit push:mysql` work

`drizzle.config.ts` example
```ts copy
import { Config } from "src";

export default {
schema: "./schema.ts",
connectionString: process.env.DB_URL,
} satisfies Config;
```

2. Run `drizzle-kit push:mysql`

3. If Drizzle detects any potential `data-loss` issues during a migration, it will prompt you to approve whether the data should be truncated or not in order to ensure a successful migration

4. Approve or reject the action that Drizzle needs to perform in order to push your schema changes to the database.

5. Done ✅
3 changes: 1 addition & 2 deletions drizzle-orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "drizzle-orm",
"version": "0.25.5",
"version": "0.26.0",
"description": "Drizzle ORM package for SQL databases",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -126,7 +126,6 @@
"@originjs/vite-plugin-commonjs": "^1.0.3",
"@planetscale/database": "^1.5.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-terser": "^0.4.1",
"@rollup/plugin-typescript": "^11.1.0",
"@types/better-sqlite3": "^7.6.2",
"@types/node": "^18.15.11",
Expand Down
6 changes: 2 additions & 4 deletions drizzle-orm/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json from '@rollup/plugin-json';
import terser from '@rollup/plugin-terser';
import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';
import { entries, external } from './rollup.common';
Expand All @@ -15,14 +14,14 @@ export default defineConfig([
output: [
{
format: 'esm',
dir: 'dist',
dir: 'dist.new',
entryFileNames: '[name].mjs',
chunkFileNames: '[name]-[hash].mjs',
sourcemap: true,
},
{
format: 'cjs',
dir: 'dist',
dir: 'dist.new',
entryFileNames: '[name].cjs',
chunkFileNames: '[name]-[hash].cjs',
sourcemap: true,
Expand All @@ -36,7 +35,6 @@ export default defineConfig([
typescript({
tsconfig: 'tsconfig.build.json',
}),
terser(),
],
},
]);
2 changes: 1 addition & 1 deletion drizzle-orm/rollup.dts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineConfig([
return acc;
}, {}),
output: {
dir: 'dist',
dir: 'dist.new',
},
external,
plugins: [
Expand Down
5 changes: 3 additions & 2 deletions drizzle-orm/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ function updateAndCopyPackageJson() {
{},
);

fs.writeJSONSync('dist/package.json', pkg, { spaces: 2 });
fs.writeJSONSync('dist.new/package.json', pkg, { spaces: 2 });
}

fs.removeSync('dist');
await $`rollup --config rollup.config.ts --configPlugin typescript`;
fs.copySync('../README.md', 'dist/README.md');
updateAndCopyPackageJson();
await $`tsc -p tsconfig.build.json --declaration --outDir dist-dts --emitDeclarationOnly`;
await $`resolve-tspaths --out dist-dts`;
await $`rollup --config rollup.dts.config.ts --configPlugin typescript`;
fs.removeSync('dist');
fs.renameSync('dist.new', 'dist');
60 changes: 54 additions & 6 deletions drizzle-orm/src/alias.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { AnyColumn } from './column';
import { Column } from './column';
import { type Relation } from './relations';
import { SQL, sql } from './sql';
import { Table } from './table';
import { type View, ViewBaseConfig } from './view';

Expand All @@ -18,7 +20,7 @@ export class ColumnAliasProxyHandler<TColumn extends AnyColumn> implements Proxy
export class TableAliasProxyHandler<T extends Table | View> implements ProxyHandler<T> {
constructor(private alias: string, private replaceOriginalName: boolean) {}

get(tableObj: T, prop: string | symbol): any {
get(target: T, prop: string | symbol): any {
if (prop === Table.Symbol.IsAlias) {
return true;
}
Expand All @@ -33,14 +35,14 @@ export class TableAliasProxyHandler<T extends Table | View> implements ProxyHand

if (prop === ViewBaseConfig) {
return {
...tableObj[ViewBaseConfig as keyof typeof tableObj],
...target[ViewBaseConfig as keyof typeof target],
name: this.alias,
isAlias: true,
};
}

if (prop === Table.Symbol.Columns) {
const columns = (tableObj as Table)[Table.Symbol.Columns];
const columns = (target as Table)[Table.Symbol.Columns];
if (!columns) {
return columns;
}
Expand All @@ -50,18 +52,64 @@ export class TableAliasProxyHandler<T extends Table | View> implements ProxyHand
Object.keys(columns).map((key) => {
proxiedColumns[key] = new Proxy(
columns[key]!,
new ColumnAliasProxyHandler(new Proxy(tableObj, this)),
new ColumnAliasProxyHandler(new Proxy(target, this)),
);
});

return proxiedColumns;
}

const value = tableObj[prop as keyof typeof tableObj];
const value = target[prop as keyof typeof target];
if (value instanceof Column) {
return new Proxy(value, new ColumnAliasProxyHandler(new Proxy(tableObj, this)));
return new Proxy(value, new ColumnAliasProxyHandler(new Proxy(target, this)));
}

return value;
}
}

export class RelationTableAliasProxyHandler<T extends Relation> implements ProxyHandler<T> {
constructor(private alias: string) {}

get(target: T, prop: string | symbol): any {
if (prop === 'sourceTable') {
return aliasedTable(target.sourceTable, this.alias);
}

return target[prop as keyof typeof target];
}
}

export function aliasedTable<T extends Table>(table: T, tableAlias: string): T {
return new Proxy(table, new TableAliasProxyHandler(tableAlias, false));
}

export function aliasedRelation<T extends Relation>(relation: T, tableAlias: string): T {
return new Proxy(relation, new RelationTableAliasProxyHandler(tableAlias));
}

export function aliasedTableColumn<T extends AnyColumn>(column: T, tableAlias: string): T {
return new Proxy(
column,
new ColumnAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false))),
);
}

export function mapColumnsInAliasedSQLToAlias(query: SQL.Aliased, alias: string): SQL.Aliased {
return new SQL.Aliased(mapColumnsInSQLToAlias(query.sql, alias), query.fieldAlias);
}

export function mapColumnsInSQLToAlias(query: SQL, alias: string): SQL {
return sql.fromList(query.queryChunks.map((c) => {
if (c instanceof Column) {
return aliasedTableColumn(c, alias);
}
if (c instanceof SQL) {
return mapColumnsInSQLToAlias(c, alias);
}
if (c instanceof SQL.Aliased) {
return mapColumnsInAliasedSQLToAlias(c, alias);
}
return c;
}));
}
Loading

0 comments on commit 474f1a3

Please sign in to comment.