refactor(server): remove now unnecessary attachment without size

This commit is contained in:
Elian Doran 2025-11-29 13:08:05 +02:00
parent 88b5e9db87
commit 7094f71e32
No known key found for this signature in database
7 changed files with 28 additions and 47 deletions

View File

@ -13,10 +13,6 @@ import BBlob from "./entities/bblob.js";
import BRecentNote from "./entities/brecent_note.js"; import BRecentNote from "./entities/brecent_note.js";
import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
interface AttachmentOpts {
includeContentLength?: boolean;
}
/** /**
* Becca is a backend cache of all notes, branches, and attributes. * Becca is a backend cache of all notes, branches, and attributes.
* There's a similar frontend cache Froca, and share cache Shaca. * There's a similar frontend cache Froca, and share cache Shaca.
@ -167,21 +163,18 @@ export default class Becca {
return revision; return revision;
} }
getAttachment(attachmentId: string, opts: AttachmentOpts = {}): BAttachment | null { getAttachment(attachmentId: string): BAttachment | null {
opts.includeContentLength = !!opts.includeContentLength; const query = /*sql*/`\
SELECT attachments.*, LENGTH(blobs.content) AS contentLength
const query = opts.includeContentLength FROM attachments
? /*sql*/`SELECT attachments.*, LENGTH(blobs.content) AS contentLength JOIN blobs USING (blobId)
FROM attachments WHERE attachmentId = ? AND isDeleted = 0`;
JOIN blobs USING (blobId)
WHERE attachmentId = ? AND isDeleted = 0`
: /*sql*/`SELECT * FROM attachments WHERE attachmentId = ? AND isDeleted = 0`;
return sql.getRows<AttachmentRow>(query, [attachmentId]).map((row) => new BAttachment(row))[0]; return sql.getRows<AttachmentRow>(query, [attachmentId]).map((row) => new BAttachment(row))[0];
} }
getAttachmentOrThrow(attachmentId: string, opts: AttachmentOpts = {}): BAttachment { getAttachmentOrThrow(attachmentId: string): BAttachment {
const attachment = this.getAttachment(attachmentId, opts); const attachment = this.getAttachment(attachmentId);
if (!attachment) { if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' has not been found.`); throw new NotFoundError(`Attachment '${attachmentId}' has not been found.`);
} }

View File

@ -61,10 +61,6 @@ interface ContentOpts {
forceFrontendReload?: boolean; forceFrontendReload?: boolean;
} }
interface AttachmentOpts {
includeContentLength?: boolean;
}
interface Relationship { interface Relationship {
parentNoteId: string; parentNoteId: string;
childNoteId: string; childNoteId: string;
@ -1102,31 +1098,23 @@ class BNote extends AbstractBeccaEntity<BNote> {
return sql.getRows<RevisionRow>("SELECT * FROM revisions WHERE noteId = ? ORDER BY revisions.utcDateCreated ASC", [this.noteId]).map((row) => new BRevision(row)); return sql.getRows<RevisionRow>("SELECT * FROM revisions WHERE noteId = ? ORDER BY revisions.utcDateCreated ASC", [this.noteId]).map((row) => new BRevision(row));
} }
getAttachments(opts: AttachmentOpts = {}) { getAttachments() {
opts.includeContentLength = !!opts.includeContentLength; const query = /*sql*/`\
// from testing, it looks like calculating length does not make a difference in performance even on large-ish DB SELECT attachments.*, LENGTH(blobs.content) AS contentLength
// given that we're always fetching attachments only for a specific note, we might just do it always FROM attachments
JOIN blobs USING (blobId)
const query = opts.includeContentLength WHERE ownerId = ? AND isDeleted = 0
? /*sql*/`SELECT attachments.*, LENGTH(blobs.content) AS contentLength ORDER BY position`;
FROM attachments
JOIN blobs USING (blobId)
WHERE ownerId = ? AND isDeleted = 0
ORDER BY position`
: /*sql*/`SELECT * FROM attachments WHERE ownerId = ? AND isDeleted = 0 ORDER BY position`;
return sql.getRows<AttachmentRow>(query, [this.noteId]).map((row) => new BAttachment(row)); return sql.getRows<AttachmentRow>(query, [this.noteId]).map((row) => new BAttachment(row));
} }
getAttachmentById(attachmentId: string, opts: AttachmentOpts = {}) { getAttachmentById(attachmentId: string) {
opts.includeContentLength = !!opts.includeContentLength; const query = /*sql*/`\
SELECT attachments.*, LENGTH(blobs.content) AS contentLength
const query = opts.includeContentLength FROM attachments
? /*sql*/`SELECT attachments.*, LENGTH(blobs.content) AS contentLength JOIN blobs USING (blobId)
FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`;
JOIN blobs USING (blobId)
WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`
: /*sql*/`SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`;
return sql.getRows<AttachmentRow>(query, [this.noteId, attachmentId]).map((row) => new BAttachment(row))[0]; return sql.getRows<AttachmentRow>(query, [this.noteId, attachmentId]).map((row) => new BAttachment(row))[0];
} }

View File

@ -92,7 +92,7 @@ function getAndCheckNote(noteId: string) {
} }
function getAndCheckAttachment(attachmentId: string) { function getAndCheckAttachment(attachmentId: string) {
const attachment = becca.getAttachment(attachmentId, { includeContentLength: true }); const attachment = becca.getAttachment(attachmentId);
if (attachment) { if (attachment) {
return attachment; return attachment;

View File

@ -185,7 +185,7 @@ function register(router: Router) {
eu.route(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => { eu.route(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId); const note = eu.getAndCheckNote(req.params.noteId);
const attachments = note.getAttachments({ includeContentLength: true }); const attachments = note.getAttachments();
res.json(attachments.map((attachment) => mappers.mapAttachmentToPojo(attachment))); res.json(attachments.map((attachment) => mappers.mapAttachmentToPojo(attachment)));
}); });

View File

@ -14,13 +14,13 @@ function getAttachmentBlob(req: Request) {
function getAttachments(req: Request) { function getAttachments(req: Request) {
const note = becca.getNoteOrThrow(req.params.noteId); const note = becca.getNoteOrThrow(req.params.noteId);
return note.getAttachments({ includeContentLength: true }); return note.getAttachments();
} }
function getAttachment(req: Request) { function getAttachment(req: Request) {
const { attachmentId } = req.params; const { attachmentId } = req.params;
return becca.getAttachmentOrThrow(attachmentId, { includeContentLength: true }); return becca.getAttachmentOrThrow(attachmentId);
} }
function getAllAttachments(req: Request) { function getAllAttachments(req: Request) {
@ -28,7 +28,7 @@ function getAllAttachments(req: Request) {
// one particular attachment is requested, but return all note's attachments // one particular attachment is requested, but return all note's attachments
const attachment = becca.getAttachmentOrThrow(attachmentId); const attachment = becca.getAttachmentOrThrow(attachmentId);
return attachment.getNote()?.getAttachments({ includeContentLength: true }) || []; return attachment.getNote()?.getAttachments() || [];
} }
function saveAttachment(req: Request) { function saveAttachment(req: Request) {

View File

@ -764,7 +764,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
note.setContent(newContent, { forceFrontendReload }); note.setContent(newContent, { forceFrontendReload });
if (attachments?.length > 0) { if (attachments?.length > 0) {
const existingAttachmentsByTitle = toMap(note.getAttachments({ includeContentLength: false }), "title"); const existingAttachmentsByTitle = toMap(note.getAttachments(), "title");
for (const { attachmentId, role, mime, title, position, content } of attachments) { for (const { attachmentId, role, mime, title, position, content } of attachments) {
const existingAttachment = existingAttachmentsByTitle.get(title); const existingAttachment = existingAttachmentsByTitle.get(title);

View File

@ -150,7 +150,7 @@ function fillInAdditionalProperties(entityChange: EntityChange) {
entityChange.entity = sql.getRow(/*sql*/`SELECT * FROM options WHERE name = ?`, [entityChange.entityId]); entityChange.entity = sql.getRow(/*sql*/`SELECT * FROM options WHERE name = ?`, [entityChange.entityId]);
} }
} else if (entityChange.entityName === "attachments") { } else if (entityChange.entityName === "attachments") {
entityChange.entity = becca.getAttachment(entityChange.entityId, { includeContentLength: true }); entityChange.entity = becca.getAttachment(entityChange.entityId);
if (!entityChange.entity) { if (!entityChange.entity) {
entityChange.entity = sql.getRow( entityChange.entity = sql.getRow(