cleanup of labels and relations from backend

This commit is contained in:
azivner 2018-08-07 13:33:10 +02:00
parent 3491235533
commit 1c0fd243d1
19 changed files with 107 additions and 419 deletions

View File

@ -65,30 +65,70 @@ class Note extends Entity {
return null; return null;
} }
async getLabels() { async getOwnedAttributes() {
return await repository.getEntities("SELECT * FROM labels WHERE noteId = ? AND isDeleted = 0", [this.noteId]); return await repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ?`, [this.noteId]);
} }
// WARNING: this doesn't take into account the possibility to have multi-valued labels! async getAttributes() {
async getLabelMap() { const attributes = await repository.getEntities(`
const map = {}; WITH RECURSIVE tree(noteId, level) AS (
SELECT ?, 0
UNION
SELECT branches.parentNoteId, tree.level + 1 FROM branches
JOIN tree ON branches.noteId = tree.noteId
JOIN notes ON notes.noteId = branches.parentNoteId
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
)
SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?)
ORDER BY level, noteId, position`, [this.noteId, this.noteId]);
// attributes are ordered so that "closest" attributes are first
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
for (const label of await this.getLabels()) { const filteredAttributes = attributes.filter((attr, index) => {
map[label.name] = label.value; if (attr.isDefinition()) {
const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
// keep only if this element is the first definition for this type & name
return firstDefinitionIndex === index;
}
else {
const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name);
if (!definitionAttr) {
return true;
} }
return map; const definition = definitionAttr.value;
if (definition.multiplicityType === 'multivalue') {
return true;
}
else {
const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
// in case of single-valued attribute we'll keep it only if it's first (closest)
return firstAttrIndex === index;
}
}
});
for (const attr of filteredAttributes) {
attr.isOwned = attr.noteId === this.noteId;
}
return filteredAttributes;
} }
async hasLabel(name) { async hasLabel(name) {
const map = await this.getLabelMap(); return !!await this.getLabel(name);
return map.hasOwnProperty(name);
} }
// WARNING: this doesn't take into account the possibility to have multi-valued labels! // WARNING: this doesn't take into account the possibility to have multi-valued labels!
async getLabel(name) { async getLabel(name) {
return await repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]); const attributes = await this.getAttributes();
return attributes.find(attr => attr.type === 'label' && attr.name === name);
} }
async getRevisions() { async getRevisions() {

View File

@ -6,7 +6,9 @@ const repository = require('../../services/repository');
const Attribute = require('../../entities/attribute'); const Attribute = require('../../entities/attribute');
async function getEffectiveNoteAttributes(req) { async function getEffectiveNoteAttributes(req) {
return await attributeService.getEffectiveAttributes(req.params.noteId); const note = await repository.getNote(req.params.noteId);
return await note.getAttributes();
} }
async function updateNoteAttribute(req) { async function updateNoteAttribute(req) {
@ -87,7 +89,9 @@ async function updateNoteAttributes(req) {
await attributeEntity.save(); await attributeEntity.save();
} }
return await attributeService.getEffectiveAttributes(noteId); const note = await repository.getNote(noteId);
return await note.getAttributes();
} }
async function getAttributeNames(req) { async function getAttributeNames(req) {

View File

@ -113,15 +113,18 @@ async function exportToTar(branchId, res) {
prefix: branch.prefix, prefix: branch.prefix,
type: note.type, type: note.type,
mime: note.mime, mime: note.mime,
labels: (await note.getLabels()).map(label => { attributes: (await note.getOwnedAttributes()).map(attribute => {
return { return {
name: label.name, type: attribute.type,
value: label.value name: attribute.name,
value: attribute.value,
isInheritable: attribute.isInheritable,
position: attribute.position
}; };
}) })
}; };
if (metadata.labels.find(label => label.name === 'excludeFromExport')) { if (metadata.attributes.find(attributes => attributes.type === 'label' && attributes.name === 'excludeFromExport')) {
return; return;
} }

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const noteService = require('../../services/notes'); const noteService = require('../../services/notes');
const labelService = require('../../services/labels'); const attributeService = require('../../services/attributes');
const protectedSessionService = require('../../services/protected_session'); const protectedSessionService = require('../../services/protected_session');
const repository = require('../../services/repository'); const repository = require('../../services/repository');
@ -26,8 +26,8 @@ async function uploadFile(req) {
mime: file.mimetype mime: file.mimetype
}); });
await labelService.createLabel(note.noteId, "originalFileName", originalName); await attributeService.createLabel(note.noteId, "originalFileName", originalName);
await labelService.createLabel(note.noteId, "fileSize", size); await attributeService.createLabel(note.noteId, "fileSize", size);
return { return {
noteId: note.noteId noteId: note.noteId
@ -47,8 +47,8 @@ async function downloadFile(req, res) {
return; return;
} }
const labelMap = await note.getLabelMap(); const originalFileName = await note.getLabel('originalFileName');
const fileName = labelMap.originalFileName || note.title; const fileName = originalFileName.value || note.title;
res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"'); res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"');
res.setHeader('Content-Type', note.mime); res.setHeader('Content-Type', note.mime);

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const repository = require('../../services/repository'); const repository = require('../../services/repository');
const labelService = require('../../services/labels'); const attributeService = require('../../services/attributes');
const noteService = require('../../services/notes'); const noteService = require('../../services/notes');
const Branch = require('../../entities/branch'); const Branch = require('../../entities/branch');
const tar = require('tar-stream'); const tar = require('tar-stream');
@ -187,8 +187,8 @@ async function importNotes(files, parentNoteId, noteIdMap) {
noteIdMap[file.meta.noteId] = note.noteId; noteIdMap[file.meta.noteId] = note.noteId;
for (const label of file.meta.labels) { for (const attribute of file.meta.attributes) {
await labelService.createLabel(note.noteId, label.name, label.value); await attributeService.createAttribute(attribute);
} }
if (file.children.length > 0) { if (file.children.length > 0) {

View File

@ -1,70 +0,0 @@
"use strict";
const sql = require('../../services/sql');
const labelService = require('../../services/labels');
const repository = require('../../services/repository');
const Label = require('../../entities/label');
async function getNoteLabels(req) {
const noteId = req.params.noteId;
return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
}
async function updateNoteLabels(req) {
const noteId = req.params.noteId;
const labels = req.body;
for (const label of labels) {
let labelEntity;
if (label.labelId) {
labelEntity = await repository.getLabel(label.labelId);
}
else {
// if it was "created" and then immediatelly deleted, we just don't create it at all
if (label.isDeleted) {
continue;
}
labelEntity = new Label();
labelEntity.noteId = noteId;
}
labelEntity.name = label.name;
labelEntity.value = label.value;
labelEntity.position = label.position;
labelEntity.isDeleted = label.isDeleted;
await labelEntity.save();
}
return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
}
async function getAllLabelNames() {
const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0");
for (const label of labelService.BUILTIN_LABELS) {
if (!names.includes(label)) {
names.push(label);
}
}
names.sort();
return names;
}
async function getValuesForLabel(req) {
const labelName = req.params.labelName;
return await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]);
}
module.exports = {
getNoteLabels,
updateNoteLabels,
getAllLabelNames,
getValuesForLabel
};

View File

@ -1,64 +0,0 @@
"use strict";
const sql = require('../../services/sql');
const relationService = require('../../services/relations');
const repository = require('../../services/repository');
const Relation = require('../../entities/relation');
async function getNoteRelations(req) {
const noteId = req.params.noteId;
return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]);
}
async function updateNoteRelations(req) {
const noteId = req.params.noteId;
const relations = req.body;
for (const relation of relations) {
let relationEntity;
if (relation.relationId) {
relationEntity = await repository.getRelation(relation.relationId);
}
else {
// if it was "created" and then immediatelly deleted, we just don't create it at all
if (relation.isDeleted) {
continue;
}
relationEntity = new Relation();
relationEntity.sourceNoteId = noteId;
}
relationEntity.name = relation.name;
relationEntity.targetNoteId = relation.targetNoteId;
relationEntity.isInheritable = relation.isInheritable;
relationEntity.position = relation.position;
relationEntity.isDeleted = relation.isDeleted;
await relationEntity.save();
}
return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]);
}
async function getAllRelationNames() {
const names = await sql.getColumn("SELECT DISTINCT name FROM relations WHERE isDeleted = 0");
for (const relationName of relationService.BUILTIN_RELATIONS) {
if (!names.includes(relationName)) {
names.push(relationName);
}
}
names.sort();
return names;
}
module.exports = {
getNoteRelations,
updateNoteRelations,
getAllRelationNames
};

View File

@ -1,6 +1,5 @@
"use strict"; "use strict";
const labelService = require('../../services/labels');
const scriptService = require('../../services/script'); const scriptService = require('../../services/script');
const attributeService = require('../../services/attributes'); const attributeService = require('../../services/attributes');
const repository = require('../../services/repository'); const repository = require('../../services/repository');
@ -21,7 +20,7 @@ async function run(req) {
} }
async function getStartupBundles() { async function getStartupBundles() {
const notes = await labelService.getNotesWithLabel("run", "frontendStartup"); const notes = await attributeService.getNotesWithLabel("run", "frontendStartup");
const bundles = []; const bundles = [];
@ -38,9 +37,10 @@ async function getStartupBundles() {
async function getRelationBundles(req) { async function getRelationBundles(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const note = await repository.getNote(noteId);
const relationName = req.params.relationName; const relationName = req.params.relationName;
const attributes = await attributeService.getEffectiveAttributes(noteId); const attributes = await note.getAttributes();
const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName); const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName);
const targetNoteIds = filtered.map(relation => relation.value); const targetNoteIds = filtered.map(relation => relation.value);
const uniqueNoteIds = Array.from(new Set(targetNoteIds)); const uniqueNoteIds = Array.from(new Set(targetNoteIds));

View File

@ -2,7 +2,7 @@
const sourceIdService = require('../services/source_id'); const sourceIdService = require('../services/source_id');
const sql = require('../services/sql'); const sql = require('../services/sql');
const labelService = require('../services/labels'); const attributeService = require('../services/attributes');
const config = require('../services/config'); const config = require('../services/config');
const optionService = require('../services/options'); const optionService = require('../services/options');
@ -18,7 +18,7 @@ async function index(req, res) {
async function getAppCss() { async function getAppCss() {
let css = ''; let css = '';
const notes = labelService.getNotesWithLabel('appCss'); const notes = attributeService.getNotesWithLabel('appCss');
for (const note of await notes) { for (const note of await notes) {
css += `/* ${note.noteId} */ css += `/* ${note.noteId} */

View File

@ -43,12 +43,17 @@ async function getNoteWithLabel(name, value) {
return notes.length > 0 ? notes[0] : null; return notes.length > 0 ? notes[0] : null;
} }
async function createAttribute(noteId, name, value = "") { async function createLabel(noteId, name, value = "") {
return await new Attribute({ return await createAttribute({
noteId: noteId, noteId: noteId,
type: 'label',
name: name, name: name,
value: value value: value
}).save(); });
}
async function createAttribute(attribute) {
return await new Attribute(attribute).save();
} }
async function getAttributeNames(type, nameLike) { async function getAttributeNames(type, nameLike) {
@ -70,62 +75,11 @@ async function getAttributeNames(type, nameLike) {
return names; return names;
} }
async function getEffectiveAttributes(noteId) {
const attributes = await repository.getEntities(`
WITH RECURSIVE tree(noteId, level) AS (
SELECT ?, 0
UNION
SELECT branches.parentNoteId, tree.level + 1 FROM branches
JOIN tree ON branches.noteId = tree.noteId
JOIN notes ON notes.noteId = branches.parentNoteId
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
)
SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?)
ORDER BY level, noteId, position`, [noteId, noteId]);
// attributes are ordered so that "closest" attributes are first
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
const filteredAttributes = attributes.filter((attr, index) => {
if (attr.isDefinition()) {
const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
// keep only if this element is the first definition for this type & name
return firstDefinitionIndex === index;
}
else {
const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name);
if (!definitionAttr) {
return true;
}
const definition = definitionAttr.value;
if (definition.multiplicityType === 'multivalue') {
return true;
}
else {
const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
// in case of single-valued attribute we'll keep it only if it's first (closest)
return firstAttrIndex === index;
}
}
});
for (const attr of filteredAttributes) {
attr.isOwned = attr.noteId === noteId;
}
return filteredAttributes;
}
module.exports = { module.exports = {
getNotesWithLabel, getNotesWithLabel,
getNoteWithLabel, getNoteWithLabel,
createLabel,
createAttribute, createAttribute,
getAttributeNames, getAttributeNames,
getEffectiveAttributes,
BUILTIN_ATTRIBUTES BUILTIN_ATTRIBUTES
}; };

View File

@ -1,8 +1,7 @@
"use strict"; "use strict";
const sql = require('./sql');
const noteService = require('./notes'); const noteService = require('./notes');
const labelService = require('./labels'); const attributeService = require('./attributes');
const dateUtils = require('./date_utils'); const dateUtils = require('./date_utils');
const repository = require('./repository'); const repository = require('./repository');
@ -32,7 +31,7 @@ async function getNoteStartingWith(parentNoteId, startsWith) {
async function getRootCalendarNote() { async function getRootCalendarNote() {
// some caching here could be useful (e.g. in CLS) // some caching here could be useful (e.g. in CLS)
let rootNote = await labelService.getNoteWithLabel(CALENDAR_ROOT_LABEL); let rootNote = await attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
if (!rootNote) { if (!rootNote) {
rootNote = (await noteService.createNewNote('root', { rootNote = (await noteService.createNewNote('root', {
@ -41,8 +40,8 @@ async function getRootCalendarNote() {
isProtected: false isProtected: false
})).note; })).note;
await labelService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL);
await labelService.createLabel(rootNote.noteId, 'sorted'); await attributeService.createLabel(rootNote.noteId, 'sorted');
} }
return rootNote; return rootNote;
@ -51,7 +50,7 @@ async function getRootCalendarNote() {
async function getYearNote(dateTimeStr, rootNote) { async function getYearNote(dateTimeStr, rootNote) {
const yearStr = dateTimeStr.substr(0, 4); const yearStr = dateTimeStr.substr(0, 4);
let yearNote = await labelService.getNoteWithLabel(YEAR_LABEL, yearStr); let yearNote = await attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
if (!yearNote) { if (!yearNote) {
yearNote = await getNoteStartingWith(rootNote.noteId, yearStr); yearNote = await getNoteStartingWith(rootNote.noteId, yearStr);
@ -60,8 +59,8 @@ async function getYearNote(dateTimeStr, rootNote) {
yearNote = await createNote(rootNote.noteId, yearStr); yearNote = await createNote(rootNote.noteId, yearStr);
} }
await labelService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr); await attributeService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr);
await labelService.createLabel(yearNote.noteId, 'sorted'); await attributeService.createLabel(yearNote.noteId, 'sorted');
} }
return yearNote; return yearNote;
@ -71,7 +70,7 @@ async function getMonthNote(dateTimeStr, rootNote) {
const monthStr = dateTimeStr.substr(0, 7); const monthStr = dateTimeStr.substr(0, 7);
const monthNumber = dateTimeStr.substr(5, 2); const monthNumber = dateTimeStr.substr(5, 2);
let monthNote = await labelService.getNoteWithLabel(MONTH_LABEL, monthStr); let monthNote = await attributeService.getNoteWithLabel(MONTH_LABEL, monthStr);
if (!monthNote) { if (!monthNote) {
const yearNote = await getYearNote(dateTimeStr, rootNote); const yearNote = await getYearNote(dateTimeStr, rootNote);
@ -86,8 +85,8 @@ async function getMonthNote(dateTimeStr, rootNote) {
monthNote = await createNote(yearNote.noteId, noteTitle); monthNote = await createNote(yearNote.noteId, noteTitle);
} }
await labelService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr); await attributeService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr);
await labelService.createLabel(monthNote.noteId, 'sorted'); await attributeService.createLabel(monthNote.noteId, 'sorted');
} }
return monthNote; return monthNote;
@ -99,7 +98,7 @@ async function getDateNote(dateTimeStr) {
const dateStr = dateTimeStr.substr(0, 10); const dateStr = dateTimeStr.substr(0, 10);
const dayNumber = dateTimeStr.substr(8, 2); const dayNumber = dateTimeStr.substr(8, 2);
let dateNote = await labelService.getNoteWithLabel(DATE_LABEL, dateStr); let dateNote = await attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
if (!dateNote) { if (!dateNote) {
const monthNote = await getMonthNote(dateTimeStr, rootNote); const monthNote = await getMonthNote(dateTimeStr, rootNote);
@ -114,7 +113,7 @@ async function getDateNote(dateTimeStr) {
dateNote = await createNote(monthNote.noteId, noteTitle); dateNote = await createNote(monthNote.noteId, noteTitle);
} }
await labelService.createLabel(dateNote.noteId, DATE_LABEL, dateStr); await attributeService.createLabel(dateNote.noteId, DATE_LABEL, dateStr);
} }
return dateNote; return dateNote;

View File

@ -1,13 +1,13 @@
const eventService = require('./events'); const eventService = require('./events');
const scriptService = require('./script'); const scriptService = require('./script');
const relationService = require('./relations');
const treeService = require('./tree'); const treeService = require('./tree');
const messagingService = require('./messaging'); const messagingService = require('./messaging');
eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
const relations = await relationService.getEffectiveRelations(note.noteId, 'runOnNoteTitleChange'); const attributes = await note.getAttributes();
const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === 'runOnNoteTitleChange');
for (const relation of relations) { for (const relation of runRelations) {
const scriptNote = await relation.getTargetNote(); const scriptNote = await relation.getTargetNote();
await scriptService.executeNote(scriptNote, scriptNote, note); await scriptService.executeNote(scriptNote, scriptNote, note);

View File

@ -1,52 +0,0 @@
"use strict";
const repository = require('./repository');
const Label = require('../entities/label');
const BUILTIN_LABELS = [
'disableVersioning',
'calendarRoot',
'archived',
'excludeFromExport',
'run',
'manualTransactionHandling',
'disableInclusion',
'appCss',
'hideChildrenOverview'
];
async function getNotesWithLabel(name, value) {
let notes;
if (value !== undefined) {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)
WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ? AND labels.value = ?`, [name, value]);
}
else {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)
WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ?`, [name]);
}
return notes;
}
async function getNoteWithLabel(name, value) {
const notes = await getNotesWithLabel(name, value);
return notes.length > 0 ? notes[0] : null;
}
async function createLabel(noteId, name, value = "") {
return await new Label({
noteId: noteId,
name: name,
value: value
}).save();
}
module.exports = {
getNotesWithLabel,
getNoteWithLabel,
createLabel,
BUILTIN_LABELS
};

View File

@ -2,7 +2,7 @@ const sql = require('./sql');
const optionService = require('./options'); const optionService = require('./options');
const dateUtils = require('./date_utils'); const dateUtils = require('./date_utils');
const syncTableService = require('./sync_table'); const syncTableService = require('./sync_table');
const labelService = require('./labels'); const attributeService = require('./attributes');
const eventService = require('./events'); const eventService = require('./events');
const repository = require('./repository'); const repository = require('./repository');
const Note = require('../entities/note'); const Note = require('../entities/note');
@ -93,9 +93,10 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
const {note, branch} = await createNewNote(parentNoteId, noteData); const {note, branch} = await createNewNote(parentNoteId, noteData);
// FIXME: need to make this more generic for all kinds of attributes
if (extraOptions.labels) { if (extraOptions.labels) {
for (const labelName in extraOptions.labels) { for (const labelName in extraOptions.labels) {
await labelService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]); await attributeService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]);
} }
} }
@ -168,8 +169,6 @@ async function saveNoteImages(note) {
} }
async function saveNoteRevision(note) { async function saveNoteRevision(note) {
const labelsMap = await note.getLabelMap();
const now = new Date(); const now = new Date();
const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval')); const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval'));
@ -181,7 +180,7 @@ async function saveNoteRevision(note) {
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime(); const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime();
if (note.type !== 'file' if (note.type !== 'file'
&& labelsMap.disableVersioning !== 'true' && await note.hasLabel('disableVersioning')
&& !existingnoteRevisionId && !existingnoteRevisionId
&& msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {

View File

@ -1,67 +0,0 @@
"use strict";
const repository = require('./repository');
const Relation = require('../entities/relation');
const BUILTIN_RELATIONS = [
'runOnNoteView',
'runOnNoteTitleChange'
];
async function getNotesWithRelation(name, targetNoteId) {
let notes;
if (targetNoteId !== undefined) {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId
WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ? AND relations.targetNoteId = ?`, [name, targetNoteId]);
}
else {
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId
WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ?`, [name]);
}
return notes;
}
async function getNoteWithRelation(name, value) {
const notes = await getNotesWithRelation(name, value);
return notes.length > 0 ? notes[0] : null;
}
async function createRelation(sourceNoteId, name, targetNoteId) {
return await new Relation({
sourceNoteId: sourceNoteId,
name: name,
targetNoteId: targetNoteId
}).save();
}
async function getEffectiveRelations(noteId, relationName) {
const relations = await repository.getEntities(`
WITH RECURSIVE tree(noteId) AS (
SELECT ?
UNION
SELECT branches.parentNoteId FROM branches
JOIN tree ON branches.noteId = tree.noteId
JOIN notes ON notes.noteId = branches.parentNoteId
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
)
SELECT relations.* FROM relations JOIN tree ON relations.sourceNoteId = tree.noteId
WHERE relations.isDeleted = 0 AND (relations.isInheritable = 1 OR relations.sourceNoteId = ?)`, [noteId, noteId]);
if (relationName) {
return relations.filter(relation => relation.name === relationName);
}
else {
return relations;
}
}
module.exports = {
BUILTIN_RELATIONS,
getNotesWithRelation,
getNoteWithRelation,
createRelation,
getEffectiveRelations
};

View File

@ -41,14 +41,6 @@ async function getAttribute(attributeId) {
return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]); return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]);
} }
async function getLabel(labelId) {
return await getEntity("SELECT * FROM labels WHERE labelId = ?", [labelId]);
}
async function getRelation(relationId) {
return await getEntity("SELECT * FROM relations WHERE relationId = ?", [relationId]);
}
async function getOption(name) { async function getOption(name) {
return await getEntity("SELECT * FROM options WHERE name = ?", [name]); return await getEntity("SELECT * FROM options WHERE name = ?", [name]);
} }
@ -61,6 +53,7 @@ async function updateEntity(entity) {
const clone = Object.assign({}, entity); const clone = Object.assign({}, entity);
delete clone.jsonContent; delete clone.jsonContent;
delete clone.isOwned;
for (const key in clone) { for (const key in clone) {
if (clone[key] !== null && typeof clone[key] === 'object') { if (clone[key] !== null && typeof clone[key] === 'object') {
@ -86,8 +79,6 @@ module.exports = {
getBranch, getBranch,
getImage, getImage,
getAttribute, getAttribute,
getLabel,
getRelation,
getOption, getOption,
updateEntity, updateEntity,
setEntityConstructor setEntityConstructor

View File

@ -235,8 +235,6 @@ const primaryKeys = {
"recent_notes": "branchId", "recent_notes": "branchId",
"images": "imageId", "images": "imageId",
"note_images": "noteImageId", "note_images": "noteImageId",
"labels": "labelId",
"relations": "relationId",
"api_tokens": "apiTokenId", "api_tokens": "apiTokenId",
"options": "name" "options": "name"
}; };

View File

@ -1,7 +1,6 @@
const sql = require('./sql'); const sql = require('./sql');
const sourceIdService = require('./source_id'); const sourceIdService = require('./source_id');
const dateUtils = require('./date_utils'); const dateUtils = require('./date_utils');
const syncOptions = require('./sync_options');
const log = require('./log'); const log = require('./log');
const cls = require('./cls'); const cls = require('./cls');
const eventService = require('./events'); const eventService = require('./events');
@ -42,14 +41,6 @@ async function addAttributeSync(attributeId, sourceId) {
await addEntitySync("attributes", attributeId, sourceId); await addEntitySync("attributes", attributeId, sourceId);
} }
async function addLabelSync(labelId, sourceId) {
await addEntitySync("labels", labelId, sourceId);
}
async function addRelationSync(relationId, sourceId) {
await addEntitySync("relations", relationId, sourceId);
}
async function addApiTokenSync(apiTokenId, sourceId) { async function addApiTokenSync(apiTokenId, sourceId) {
await addEntitySync("api_tokens", apiTokenId, sourceId); await addEntitySync("api_tokens", apiTokenId, sourceId);
} }
@ -109,8 +100,6 @@ async function fillAllSyncRows() {
await fillSyncRows("images", "imageId"); await fillSyncRows("images", "imageId");
await fillSyncRows("note_images", "noteImageId"); await fillSyncRows("note_images", "noteImageId");
await fillSyncRows("attributes", "attributeId"); await fillSyncRows("attributes", "attributeId");
await fillSyncRows("labels", "labelId");
await fillSyncRows("relations", "relationId");
await fillSyncRows("api_tokens", "apiTokenId"); await fillSyncRows("api_tokens", "apiTokenId");
await fillSyncRows("options", "name", 'isSynced = 1'); await fillSyncRows("options", "name", 'isSynced = 1');
} }
@ -125,8 +114,6 @@ module.exports = {
addImageSync, addImageSync,
addNoteImageSync, addNoteImageSync,
addAttributeSync, addAttributeSync,
addLabelSync,
addRelationSync,
addApiTokenSync, addApiTokenSync,
addEntitySync, addEntitySync,
cleanupSyncRowsForMissingEntities, cleanupSyncRowsForMissingEntities,

View File

@ -33,12 +33,6 @@ async function updateEntity(sync, entity, sourceId) {
else if (entityName === 'attributes') { else if (entityName === 'attributes') {
await updateAttribute(entity, sourceId); await updateAttribute(entity, sourceId);
} }
else if (entityName === 'labels') {
await updateLabel(entity, sourceId);
}
else if (entityName === 'relations') {
await updateRelation(entity, sourceId);
}
else if (entityName === 'api_tokens') { else if (entityName === 'api_tokens') {
await updateApiToken(entity, sourceId); await updateApiToken(entity, sourceId);
} }
@ -191,34 +185,6 @@ async function updateAttribute(entity, sourceId) {
} }
} }
async function updateLabel(entity, sourceId) {
const origLabel = await sql.getRow("SELECT * FROM labels WHERE labelId = ?", [entity.labelId]);
if (!origLabel || origLabel.dateModified <= entity.dateModified) {
await sql.transactional(async () => {
await sql.replace("labels", entity);
await syncTableService.addLabelSync(entity.labelId, sourceId);
});
log.info("Update/sync label " + entity.labelId);
}
}
async function updateRelation(entity, sourceId) {
const origRelation = await sql.getRow("SELECT * FROM relations WHERE relationId = ?", [entity.relationId]);
if (!origRelation || origRelation.dateModified <= entity.dateModified) {
await sql.transactional(async () => {
await sql.replace("relations", entity);
await syncTableService.addRelationSync(entity.relationId, sourceId);
});
log.info("Update/sync relation " + entity.relationId);
}
}
async function updateApiToken(entity, sourceId) { async function updateApiToken(entity, sourceId) {
const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]); const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]);