111 lines
2.9 KiB
JavaScript
111 lines
2.9 KiB
JavaScript
import QueryStream from 'pg-query-stream';
|
|
import { pipeline } from 'node:stream/promises';
|
|
import { Readable } from 'node:stream';
|
|
import { query, getPool } from './index.js';
|
|
import { batch } from '../lib/streams.js';
|
|
|
|
const SELECT_NOTES = 'SELECT id, text, x, y, author, color FROM notes ORDER BY id';
|
|
|
|
// Postgres caps a statement at 65535 bind parameters; six columns per note
|
|
// leaves 10922 as the hard ceiling.
|
|
const INSERT_BATCH_SIZE = 1000;
|
|
|
|
export const getAllNotes = async () => {
|
|
const { rows } = await query(SELECT_NOTES);
|
|
return rows;
|
|
};
|
|
|
|
/**
|
|
* Streams every note as an object-mode Readable. The pooled client is released on end, error, or
|
|
* destruction by consumer.
|
|
*
|
|
* @returns {Promise<import('node:stream').Readable>}
|
|
*/
|
|
export const streamAllNotes = async () => {
|
|
const client = await getPool().connect();
|
|
|
|
let released = false;
|
|
const release = () => {
|
|
if (released) return;
|
|
released = true;
|
|
client.release();
|
|
};
|
|
|
|
try {
|
|
const rows = client.query(new QueryStream(SELECT_NOTES));
|
|
rows.once('end', release);
|
|
rows.once('error', release);
|
|
rows.once('close', release);
|
|
return rows;
|
|
} catch (err) {
|
|
release();
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
export const getNoteById = async (id) => {
|
|
const { rows } = await query(
|
|
'SELECT id, text, x, y, author, color FROM notes WHERE id = $1',
|
|
[id]
|
|
);
|
|
return rows[0] || null;
|
|
};
|
|
|
|
export const createNote = async (note) => {
|
|
const { rows } = await query(
|
|
`INSERT INTO notes (id, text, x, y, author, color)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
RETURNING id, text, x, y, author, color`,
|
|
[note.id, note.text, note.x ?? 0, note.y ?? 0, note.author, note.color ?? 'yellow']
|
|
);
|
|
return rows[0];
|
|
};
|
|
|
|
const insertNoteBatch = async (notes) => {
|
|
const values = [];
|
|
const placeholders = [];
|
|
|
|
notes.forEach((note, i) => {
|
|
const offset = i * 6;
|
|
placeholders.push(
|
|
`($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6})`
|
|
);
|
|
values.push(
|
|
note.id,
|
|
note.text,
|
|
note.x ?? 0,
|
|
note.y ?? 0,
|
|
note.author,
|
|
note.color ?? 'yellow'
|
|
);
|
|
});
|
|
|
|
const { rows } = await query(
|
|
`INSERT INTO notes (id, text, x, y, author, color)
|
|
VALUES ${placeholders.join(', ')}
|
|
RETURNING id, text, x, y, author, color`,
|
|
values
|
|
);
|
|
return rows;
|
|
};
|
|
|
|
export const createNotes = async (notes) => {
|
|
if (!notes || notes.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const inserted = [];
|
|
|
|
await pipeline(
|
|
Readable.from(notes, { objectMode: true }),
|
|
batch(INSERT_BATCH_SIZE),
|
|
async (batches) => {
|
|
for await (const chunk of batches) {
|
|
inserted.push(...await insertNoteBatch(chunk));
|
|
}
|
|
}
|
|
);
|
|
|
|
return inserted;
|
|
};
|