Skip to content

Commit

Permalink
feat: add client for Google's BigQuery database (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
ForbesLindesay authored Aug 24, 2021
1 parent ffbdc8b commit 3f47e24
Show file tree
Hide file tree
Showing 31 changed files with 1,811 additions and 25 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Each database driver is published to npm as a separate module, so you don't need
<!-- VERSION_TABLE -->
Package Name | Version | Docs
-------------|---------|------
@databases/bigquery | [![NPM version](https://img.shields.io/npm/v/@databases/bigquery?style=for-the-badge)](https://www.npmjs.com/package/@databases/bigquery) | [https://www.atdatabases.org/docs/bigquery](https://www.atdatabases.org/docs/bigquery)
@databases/connection-pool | [![NPM version](https://img.shields.io/npm/v/@databases/connection-pool?style=for-the-badge)](https://www.npmjs.com/package/@databases/connection-pool) | [https://www.atdatabases.org/docs/connection-pool](https://www.atdatabases.org/docs/connection-pool)
@databases/escape-identifier | [![NPM version](https://img.shields.io/npm/v/@databases/escape-identifier?style=for-the-badge)](https://www.npmjs.com/package/@databases/escape-identifier) | [https://www.atdatabases.org/docs/escape-identifier](https://www.atdatabases.org/docs/escape-identifier)
@databases/expo | [![NPM version](https://img.shields.io/npm/v/@databases/expo?style=for-the-badge)](https://www.npmjs.com/package/@databases/expo) | [https://www.atdatabases.org/docs/websql](https://www.atdatabases.org/docs/websql)
Expand Down
37 changes: 37 additions & 0 deletions docs/bigquery-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
id: bigquery-client
title: BigQuery Node.js Client
sidebar_label: Client
---

To access BigQuery, you will need to create a BigQueryClient.

### `BigQueryClient.query(SQLQuery): Promise<any[]>`

Run an SQL Query and get a promise for an array of results.

### `BigQueryClient.queryStream(SQLQuery): AsyncIterable<any>`

Run an SQL Query and get an async iterable of the results. e.g.

```js
for await (const record of db.queryStream(sql`SELECT * FROM massive_table`)) {
console.log(result);
}
```

### `BigQueryClient.queryNodeStream(SQLQuery): ReadableStream`

Run an SQL Query and get a node.js readable stream of the results. e.g.

```js
const Stringifier = require('newline-json').Stringifier;

db.queryNodeStream(sql`SELECT * FROM massive_table`)
.pipe(new Stringifier())
.pipe(process.stdout);
```

### `BigQueryClient.dataset(name): BigQueryDataSet`

Get a BigQuery API that's scoped to a dataset.
27 changes: 27 additions & 0 deletions docs/bigquery-dataset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
id: bigquery-dataset
title: BigQuery Node.js DataSet
sidebar_label: DataSet
---

A BigQueryDataSet is a client for BigQuery that's scoped to a single data set.

### `BigQueryDataSet.query(SQLQuery): Promise<any[]>`

This is equivalent to `.query` on the BigQueryClient, except the query is automatically scoped to the dataset. See [`BigQueryClient.queryStream`](bigquery-client.md) for details.

### `BigQueryDataSet.queryStream(SQLQuery): AsyncIterable<any>`

This is equivalent to `.queryStream` on the BigQueryClient, except the query is automatically scoped to the dataset. See [`BigQueryClient.queryStream`](bigquery-client.md) for details.

### `BigQueryDataSet.queryNodeStream(SQLQuery): ReadableStream`

This is equivalent to `.queryNodeStream` on the BigQueryClient, except the query is automatically scoped to the dataset. See [`BigQueryClient.queryStream`](bigquery-client.md) for details.

### `BigQueryDataSet.createTable(name, options): Promise<BigQueryTable>`

Create a table and get a BigQuery Table API for inserting records into the table.

### `BigQueryDataSet.table(name): BigQueryTable`

Get a BigQuery Table API for inserting records into the table.
136 changes: 136 additions & 0 deletions docs/bigquery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
id: bigquery
title: BigQuery
sidebar_label: API
---

The `@databases/bigquery` library provides a safe and convenient API for querying Google's BigQuery in node.js.

## `connect(options)`

Creates a client for BigQuery. You can specify a `projectId` and `keyFilename` to authenticate, or you can authenticate using the gcloud CLI before initializing this library.

For all methods, see [BigQueryClient](bigquery-client.md)

## Creating BigQuery tables in Node.js

You can create BigQuery tables from the cloud console. If you need to create tables dynamically, you can use @databases:

```typescript
import connect, {
sql,
BigQueryTableType,
BigQueryPartitionType,
} from '@databases/bigquery';

const db = connect();

db.dataset(`my_dataset`)
.createTable(`my_table`, {
type: BigQueryTableType.Table,
fields: [
{name: `id`, type: `INT64`},
{name: `value`, type: `STRING`},
],
partition: {
type: BigQueryPartitionType.Time,
granularity: 'HOUR',
},
})
.catch((err) => console.error(err));
```

```javascript
const connect = require('@databases/bigquery');
const {
sql,
BigQueryTableType,
BigQueryPartitionType,
} = require('@databases/bigquery');

const db = connect();

db.dataset(`my_dataset`)
.createTable(`my_table`, {
type: BigQueryTableType.Table,
fields: [
{name: `id`, type: `INT64`},
{name: `value`, type: `STRING`},
],
partition: {
type: BigQueryPartitionType.Time,
granularity: 'HOUR',
},
})
.catch((err) => console.error(err));
```

## Inserting Records into BigQuery tables in Node.js

You can use `INSERT` statements to insert data into BigQuery tables, but it's often more efficient & convenient to use the `.insert` API

```typescript
import connect, {
sql,
BigQueryTableType,
BigQueryPartitionType,
} from '@databases/bigquery';

const db = connect();

db.dataset(`my_dataset`)
.table(`my_table`)
.insert([
{id: 1, value: 'hello'},
{id: 2, value: 'world'},
])
.catch((err) => console.error(err));
```

```javascript
const connect = require('@databases/bigquery');
const {
sql,
BigQueryTableType,
BigQueryPartitionType,
} = require('@databases/bigquery');

const db = connect();

db.dataset(`my_dataset`)
.table(`my_table`)
.insert([
{id: 1, value: 'hello'},
{id: 2, value: 'world'},
])
.catch((err) => console.error(err));
```

## Querying BigQuery in Node.js

```typescript
import connect, {sql} from '@databases/bigquery';

const db = connect();

db.query(sql`SELECT * FROM my_dataset.my_table;`).then(
(results) => console.log(results),
(err) => console.error(err),
);
```

```javascript
const connect = require('@databases/bigquery');
const {sql} = require('@databases/bigquery');

const db = connect();

db.query(sql`SELECT * FROM my_dataset.my_table;`).then(
(results) => console.log(results),
(err) => console.error(err),
);
```

For details on how to build queries, see [Building SQL Queries](sql.md)

> N.B. BigQuery is billed based on the bytes scanned by your query. The lack of indexes can make seemingly simple queries very expensive if your tables are sufficiently large.
5 changes: 5 additions & 0 deletions docs/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
"websql",
"websql-test"
],
"Google BigQuery": [
"bigquery",
"bigquery-client",
"bigquery-dataset"
],
"Utilities": [
"connection-pool",
"escape-identifier",
Expand Down
24 changes: 18 additions & 6 deletions docs/sqlite.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ N.B. you should only have one process connected to a given SQLite database at a

## Usage

```ts
```typescript
import connect, {sql} from '@databases/sqlite';
// or in CommonJS:
// const connect = require('@databases/sqlite');
Expand All @@ -19,8 +19,20 @@ import connect, {sql} from '@databases/sqlite';
const db = connect();

db.query(sql`SELECT * FROM users;`).then(
results => console.log(results),
err => console.error(err),
(results) => console.log(results),
(err) => console.error(err),
);
```

```javascript
const connect = require('@databases/sqlite');
const {sql} = require('@databases/sqlite');

const db = connect();

db.query(sql`SELECT * FROM users;`).then(
(results) => console.log(results),
(err) => console.error(err),
);
```

Expand All @@ -35,14 +47,14 @@ Create a database connection for a given database. You should only create one co
In memory:

```ts
import connect from '@databases/websql';
import connect from '@databases/sqlite';
const db = connect();
```

File system:

```ts
import connect from '@databases/websql';
import connect from '@databases/sqlite';
const db = connect(FILE_NAME);
```

Expand Down Expand Up @@ -75,7 +87,7 @@ A transaction wraps a regular task with additional queries:
3. it executes `ROLLBACK`, if the callback did throw an error or return a rejected promise

```ts
const result = await db.tx(async transaction => {
const result = await db.tx(async (transaction) => {
const resultA = await transaction.query(sql`SELECT 1 + 1 AS a`);
const resultB = await transaction.query(sql`SELECT 1 + 1 AS b`);
return resultA[0].a + resultB[0].b;
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
"prettier:check": "prettier --list-different \"packages/*/src/**/*.{ts,tsx}\"",
"prettier:write": "prettier --write \"packages/*/src/**/*.{ts,tsx}\"",
"pretest": "yarn build",
"test": "TZ=Australia/Adelaide jest",
"test": "TZ=Australia/Adelaide jest --selectProjects node --selectProjects pg --selectProjects mysql",
"test:node": "TZ=Australia/Adelaide jest --selectProjects node",
"test:pg": "TZ=Australia/Adelaide jest --selectProjects pg",
"test:mysql": "TZ=Australia/Adelaide jest --selectProjects mysql",
"test:bigquery": "TZ=Australia/Adelaide jest --selectProjects bigquery",
"posttest": "wsrun --parallel --exclude-missing test",
"watch:jest": "jest --watch",
"clean": "rimraf packages/*/lib && rimraf packages/*/.last_build && rimraf packages/*/.cache && rimraf packages/*/build && rimraf packages/*/node_modules && rimraf node_modules",
Expand All @@ -43,6 +44,11 @@
"globalSetup": "./packages/mysql-test/jest/globalSetup",
"globalTeardown": "./packages/mysql-test/jest/globalTeardown",
"preset": "ts-jest"
},
{
"displayName": "bigquery",
"testRegex": "/__tests__/.+\\.test\\.bigquery\\.(tsx?)$",
"preset": "ts-jest"
}
]
},
Expand Down
3 changes: 3 additions & 0 deletions packages/bigquery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @databases/bigquery

For documentation, see https://www.atdatabases.org/docs/bigquery
25 changes: 25 additions & 0 deletions packages/bigquery/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@databases/bigquery",
"version": "0.0.0",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@databases/escape-identifier": "^0.0.0",
"@databases/split-sql-query": "^0.0.0",
"@databases/sql": "^0.0.0",
"@google-cloud/bigquery": "^5.7.1",
"@types/big.js": "^6.1.1",
"assert-never": "^1.2.1",
"big.js": "^6.0.0"
},
"scripts": {},
"repository": "https://github.com/ForbesLindesay/atdatabases/tree/master/packages/bigquery",
"bugs": "https://github.com/ForbesLindesay/atdatabases/issues",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"homepage": "https://www.atdatabases.org/docs/bigquery"
}
Loading

0 comments on commit 3f47e24

Please sign in to comment.