mirror of
https://github.com/zadam/trilium.git
synced 2024-09-23 17:16:32 +08:00
basic note creation with becca
This commit is contained in:
parent
8d8d654fe8
commit
6c5b1420d2
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
const utils = require('../../utils');
|
||||
|
||||
let repo = null;
|
||||
|
||||
class AbstractEntity {
|
||||
beforeSaving() {
|
||||
this.generateIdIfNecessary();
|
||||
|
@ -27,7 +31,7 @@ class AbstractEntity {
|
|||
|
||||
get repository() {
|
||||
if (!repo) {
|
||||
repo = require('../services/repository');
|
||||
repo = require('../../repository');
|
||||
}
|
||||
|
||||
return repo;
|
||||
|
|
|
@ -113,7 +113,7 @@ class Attribute extends AbstractEntity {
|
|||
}
|
||||
}
|
||||
|
||||
get pojo() {
|
||||
getPojo() {
|
||||
return {
|
||||
attributeId: this.attributeId,
|
||||
noteId: this.noteId,
|
||||
|
|
|
@ -65,15 +65,25 @@ class Branch extends AbstractEntity {
|
|||
return this.becca.notes[this.parentNoteId];
|
||||
}
|
||||
|
||||
get pojo() {
|
||||
return {
|
||||
getPojo() {
|
||||
const pojo = {
|
||||
branchId: this.branchId,
|
||||
noteId: this.noteId,
|
||||
parentNoteId: this.parentNoteId,
|
||||
prefix: this.prefix,
|
||||
notePosition: this.notePosition,
|
||||
isExpanded: this.isExpanded
|
||||
isExpanded: this.isExpanded,
|
||||
utcDateModified: dateUtils.utcNowDateTime()
|
||||
};
|
||||
|
||||
// FIXME
|
||||
if (true || !pojo.branchId) {
|
||||
pojo.utcDateCreated = dateUtils.utcNowDateTime();
|
||||
}
|
||||
|
||||
this.utcDateModified = dateUtils.utcNowDateTime();
|
||||
|
||||
return pojo;
|
||||
}
|
||||
|
||||
createClone(parentNoteId, notePosition) {
|
||||
|
@ -96,13 +106,7 @@ class Branch extends AbstractEntity {
|
|||
this.isExpanded = false;
|
||||
}
|
||||
|
||||
if (!this.branchId) {
|
||||
this.utcDateCreated = dateUtils.utcNowDateTime();
|
||||
}
|
||||
|
||||
super.beforeSaving();
|
||||
|
||||
this.utcDateModified = dateUtils.utcNowDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class Note extends AbstractEntity {
|
|||
this.ownedAttributes = [];
|
||||
|
||||
/** @param {Attribute[]|null} */
|
||||
this.attributeCache = null;
|
||||
this.__attributeCache = null;
|
||||
/** @param {Attribute[]|null} */
|
||||
this.inheritableAttributeCache = null;
|
||||
|
||||
|
@ -89,6 +89,18 @@ class Note extends AbstractEntity {
|
|||
this.flatTextCache = null;
|
||||
}
|
||||
|
||||
getParentBranches() {
|
||||
return this.parentBranches;
|
||||
}
|
||||
|
||||
getParentNotes() {
|
||||
return this.parents;
|
||||
}
|
||||
|
||||
getChildrenNotes() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note content has quite special handling - it's not a separate entity, but a lazily loaded
|
||||
* part of Note entity with it's own sync. Reasons behind this hybrid design has been:
|
||||
|
@ -245,9 +257,26 @@ class Note extends AbstractEntity {
|
|||
return null;
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
get attributes() {
|
||||
return this.__getAttributes([]);
|
||||
/**
|
||||
* @param {string} [type] - (optional) attribute type to filter
|
||||
* @param {string} [name] - (optional) attribute name to filter
|
||||
* @returns {Attribute[]} all note's attributes, including inherited ones
|
||||
*/
|
||||
getAttributes(type, name) {
|
||||
this.__getAttributes([]);
|
||||
|
||||
if (type && name) {
|
||||
return this.__attributeCache.filter(attr => attr.type === type && attr.name === name);
|
||||
}
|
||||
else if (type) {
|
||||
return this.__attributeCache.filter(attr => attr.type === type);
|
||||
}
|
||||
else if (name) {
|
||||
return this.__attributeCache.filter(attr => attr.name === name);
|
||||
}
|
||||
else {
|
||||
return this.__attributeCache.slice();
|
||||
}
|
||||
}
|
||||
|
||||
__getAttributes(path) {
|
||||
|
@ -255,7 +284,7 @@ class Note extends AbstractEntity {
|
|||
return [];
|
||||
}
|
||||
|
||||
if (!this.attributeCache) {
|
||||
if (!this.__attributeCache) {
|
||||
const parentAttributes = this.ownedAttributes.slice();
|
||||
const newPath = [...path, this.noteId];
|
||||
|
||||
|
@ -277,7 +306,7 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
}
|
||||
|
||||
this.attributeCache = [];
|
||||
this.__attributeCache = [];
|
||||
|
||||
const addedAttributeIds = new Set();
|
||||
|
||||
|
@ -285,20 +314,20 @@ class Note extends AbstractEntity {
|
|||
if (!addedAttributeIds.has(attr.attributeId)) {
|
||||
addedAttributeIds.add(attr.attributeId);
|
||||
|
||||
this.attributeCache.push(attr);
|
||||
this.__attributeCache.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
this.inheritableAttributeCache = [];
|
||||
|
||||
for (const attr of this.attributeCache) {
|
||||
for (const attr of this.__attributeCache) {
|
||||
if (attr.isInheritable) {
|
||||
this.inheritableAttributeCache.push(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.attributeCache;
|
||||
return this.__attributeCache;
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
|
@ -315,21 +344,21 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
|
||||
hasAttribute(type, name) {
|
||||
return !!this.attributes.find(attr => attr.type === type && attr.name === name);
|
||||
return !!this.getAttributes().find(attr => attr.type === type && attr.name === name);
|
||||
}
|
||||
|
||||
getAttributeCaseInsensitive(type, name, value) {
|
||||
name = name.toLowerCase();
|
||||
value = value ? value.toLowerCase() : null;
|
||||
|
||||
return this.attributes.find(
|
||||
return this.getAttributes().find(
|
||||
attr => attr.type === type
|
||||
&& attr.name.toLowerCase() === name
|
||||
&& (!value || attr.value.toLowerCase() === value));
|
||||
}
|
||||
|
||||
getRelationTarget(name) {
|
||||
const relation = this.attributes.find(attr => attr.type === 'relation' && attr.name === name);
|
||||
const relation = this.getAttributes().find(attr => attr.type === 'relation' && attr.name === name);
|
||||
|
||||
return relation ? relation.targetNote : null;
|
||||
}
|
||||
|
@ -448,6 +477,54 @@ class Note extends AbstractEntity {
|
|||
return attr ? attr.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - label name to filter
|
||||
* @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones
|
||||
*/
|
||||
getLabels(name) {
|
||||
return this.getAttributes(LABEL, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - label name to filter
|
||||
* @returns {string[]} all note's label values, including inherited ones
|
||||
*/
|
||||
getLabelValues(name) {
|
||||
return this.getLabels(name).map(l => l.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - label name to filter
|
||||
* @returns {Attribute[]} all note's labels (attributes with type label), excluding inherited ones
|
||||
*/
|
||||
getOwnedLabels(name) {
|
||||
return this.getOwnedAttributes(LABEL, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - label name to filter
|
||||
* @returns {string[]} all note's label values, excluding inherited ones
|
||||
*/
|
||||
getOwnedLabelValues(name) {
|
||||
return this.getOwnedAttributes(LABEL, name).map(l => l.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - relation name to filter
|
||||
* @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
|
||||
*/
|
||||
getRelations(name) {
|
||||
return this.getAttributes(RELATION, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [name] - relation name to filter
|
||||
* @returns {Attribute[]} all note's relations (attributes with type relation), excluding inherited ones
|
||||
*/
|
||||
getOwnedRelations(name) {
|
||||
return this.getOwnedAttributes(RELATION, name);
|
||||
}
|
||||
|
||||
get isArchived() {
|
||||
return this.hasAttribute('label', 'archived');
|
||||
}
|
||||
|
@ -485,7 +562,7 @@ class Note extends AbstractEntity {
|
|||
|
||||
this.flatTextCache += this.title + ' ';
|
||||
|
||||
for (const attr of this.attributes) {
|
||||
for (const attr of this.getAttributes()) {
|
||||
// it's best to use space as separator since spaces are filtered from the search string by the tokenization into words
|
||||
this.flatTextCache += (attr.type === 'label' ? '#' : '~') + attr.name;
|
||||
|
||||
|
@ -505,7 +582,7 @@ class Note extends AbstractEntity {
|
|||
invalidateThisCache() {
|
||||
this.flatTextCache = null;
|
||||
|
||||
this.attributeCache = null;
|
||||
this.__attributeCache = null;
|
||||
this.inheritableAttributeCache = null;
|
||||
this.ancestorCache = null;
|
||||
}
|
||||
|
@ -604,7 +681,7 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
|
||||
get labelCount() {
|
||||
return this.attributes.filter(attr => attr.type === 'label').length;
|
||||
return this.getAttributes().filter(attr => attr.type === 'label').length;
|
||||
}
|
||||
|
||||
get ownedLabelCount() {
|
||||
|
@ -612,11 +689,11 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
|
||||
get relationCount() {
|
||||
return this.attributes.filter(attr => attr.type === 'relation' && !attr.isAutoLink()).length;
|
||||
return this.getAttributes().filter(attr => attr.type === 'relation' && !attr.isAutoLink()).length;
|
||||
}
|
||||
|
||||
get relationCountIncludingLinks() {
|
||||
return this.attributes.filter(attr => attr.type === 'relation').length;
|
||||
return this.getAttributes().filter(attr => attr.type === 'relation').length;
|
||||
}
|
||||
|
||||
get ownedRelationCount() {
|
||||
|
@ -636,11 +713,11 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
|
||||
get attributeCount() {
|
||||
return this.attributes.length;
|
||||
return this.getAttributes().length;
|
||||
}
|
||||
|
||||
get ownedAttributeCount() {
|
||||
return this.attributes.length;
|
||||
return this.getAttributes().length;
|
||||
}
|
||||
|
||||
get ancestors() {
|
||||
|
@ -715,8 +792,8 @@ class Note extends AbstractEntity {
|
|||
}
|
||||
}
|
||||
|
||||
get pojo() {
|
||||
return {
|
||||
getPojo() {
|
||||
const pojo = {
|
||||
noteId: this.noteId,
|
||||
title: this.title,
|
||||
isProtected: this.isProtected,
|
||||
|
@ -727,6 +804,18 @@ class Note extends AbstractEntity {
|
|||
utcDateCreated: this.utcDateCreated,
|
||||
utcDateModified: this.utcDateModified
|
||||
};
|
||||
|
||||
if (pojo.isProtected) {
|
||||
if (this.isDecrypted) {
|
||||
pojo.title = protectedSessionService.encrypt(pojo.title);
|
||||
}
|
||||
else {
|
||||
// updating protected note outside of protected session means we will keep original ciphertexts
|
||||
delete pojo.title;
|
||||
}
|
||||
}
|
||||
|
||||
return pojo;
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
|
@ -744,16 +833,8 @@ class Note extends AbstractEntity {
|
|||
this.utcDateModified = dateUtils.utcNowDateTime();
|
||||
}
|
||||
|
||||
updatePojo(pojo) {
|
||||
if (pojo.isProtected) {
|
||||
if (this.isDecrypted) {
|
||||
pojo.title = protectedSessionService.encrypt(pojo.title);
|
||||
}
|
||||
else {
|
||||
// updating protected note outside of protected session means we will keep original ciphertexts
|
||||
delete pojo.title;
|
||||
}
|
||||
}
|
||||
markAsDeleted() {
|
||||
sql.execute("UPDATE notes SET isDeleted = 1 WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ function buildRewardMap(note) {
|
|||
addToRewardMap(ancestorNote.title, 0.3);
|
||||
}
|
||||
|
||||
for (const branch of ancestorNote.parentBranches) {
|
||||
for (const branch of ancestorNote.getParentBranches()) {
|
||||
addToRewardMap(branch.prefix, 0.3);
|
||||
}
|
||||
}
|
||||
|
@ -84,11 +84,11 @@ function buildRewardMap(note) {
|
|||
addToRewardMap(note.title, 1);
|
||||
}
|
||||
|
||||
for (const branch of note.parentBranches) {
|
||||
for (const branch of note.getParentBranches()) {
|
||||
addToRewardMap(branch.prefix, 1);
|
||||
}
|
||||
|
||||
for (const attr of note.attributes) {
|
||||
for (const attr of note.getAttributes()) {
|
||||
if (attr.name.startsWith('child:')
|
||||
|| attr.name.startsWith('relation:')
|
||||
|| attr.name.startsWith('label:')) {
|
||||
|
@ -222,7 +222,7 @@ function splitToWords(text) {
|
|||
* that it doesn't actually need to be shown to the user.
|
||||
*/
|
||||
function hasConnectingRelation(sourceNote, targetNote) {
|
||||
return sourceNote.attributes.find(attr => attr.type === 'relation'
|
||||
return sourceNote.getAttributes().find(attr => attr.type === 'relation'
|
||||
&& ['includenotelink', 'imagelink'].includes(attr.name)
|
||||
&& attr.value === targetNote.noteId);
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ async function findSimilarNotes(noteId) {
|
|||
dateLimits = buildDateLimits(baseNote);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Date limits failed with ${e.message}, entity: ${JSON.stringify(baseNote.pojo)}`);
|
||||
throw new Error(`Date limits failed with ${e.message}, entity: ${JSON.stringify(baseNote.getPojo())}`);
|
||||
}
|
||||
|
||||
const rewardMap = buildRewardMap(baseNote);
|
||||
|
@ -298,7 +298,7 @@ async function findSimilarNotes(noteId) {
|
|||
score += gatherRewards(parentNote.title, 0.3);
|
||||
}
|
||||
|
||||
for (const branch of parentNote.parentBranches) {
|
||||
for (const branch of parentNote.getParentBranches()) {
|
||||
score += gatherRewards(branch.prefix, 0.3)
|
||||
+ gatherAncestorRewards(branch.parentNote);
|
||||
}
|
||||
|
@ -319,11 +319,11 @@ async function findSimilarNotes(noteId) {
|
|||
score += gatherRewards(candidateNote.title);
|
||||
}
|
||||
|
||||
for (const branch of candidateNote.parentBranches) {
|
||||
for (const branch of candidateNote.getParentBranches()) {
|
||||
score += gatherRewards(branch.prefix);
|
||||
}
|
||||
|
||||
for (const attr of candidateNote.attributes) {
|
||||
for (const attr of candidateNote.getAttributes()) {
|
||||
if (attr.name.startsWith('child:')
|
||||
|| attr.name.startsWith('relation:')
|
||||
|| attr.name.startsWith('label:')) {
|
||||
|
@ -342,7 +342,7 @@ async function findSimilarNotes(noteId) {
|
|||
let factor = 1;
|
||||
|
||||
if (!value.startsWith) {
|
||||
log.info(`Unexpected falsy value for attribute ${JSON.stringify(attr.pojo)}`);
|
||||
log.info(`Unexpected falsy value for attribute ${JSON.stringify(attr.getPojo())}`);
|
||||
continue;
|
||||
}
|
||||
else if (value.startsWith('http')) {
|
||||
|
@ -434,8 +434,6 @@ async function findSimilarNotes(noteId) {
|
|||
for (const {noteId} of results) {
|
||||
const note = becca.notes[noteId];
|
||||
|
||||
console.log("NOTE", note.pojo);
|
||||
|
||||
displayRewards = true;
|
||||
ancestorRewardCache = {}; // reset cache
|
||||
const totalReward = computeScore(note);
|
||||
|
|
|
@ -7,8 +7,11 @@ const eventService = require('./events');
|
|||
const repository = require('./repository');
|
||||
const cls = require('../services/cls');
|
||||
const Note = require('../entities/note');
|
||||
const BeccaNote = require('../services/becca/entities/note.js');
|
||||
const Branch = require('../entities/branch');
|
||||
const BeccaBranch = require('../services/becca/entities/branch.js');
|
||||
const Attribute = require('../entities/attribute');
|
||||
const BeccaAttribute = require('../services/becca/entities/attribute.js');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const log = require('../services/log');
|
||||
const utils = require('../services/utils');
|
||||
|
@ -67,7 +70,7 @@ function deriveMime(type, mime) {
|
|||
function copyChildAttributes(parentNote, childNote) {
|
||||
for (const attr of parentNote.getAttributes()) {
|
||||
if (attr.name.startsWith("child:")) {
|
||||
new Attribute({
|
||||
new BeccaAttribute({
|
||||
noteId: childNote.noteId,
|
||||
type: attr.type,
|
||||
name: attr.name.substr(6),
|
||||
|
@ -75,8 +78,6 @@ function copyChildAttributes(parentNote, childNote) {
|
|||
position: attr.position,
|
||||
isInheritable: attr.isInheritable
|
||||
}).save();
|
||||
|
||||
childNote.invalidateAttributeCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ function copyChildAttributes(parentNote, childNote) {
|
|||
* @return {{note: Note, branch: Branch}}
|
||||
*/
|
||||
function createNewNote(params) {
|
||||
const parentNote = repository.getNote(params.parentNoteId);
|
||||
const parentNote = becca.notes[params.parentNoteId];
|
||||
|
||||
if (!parentNote) {
|
||||
throw new Error(`Parent note "${params.parentNoteId}" not found.`);
|
||||
|
@ -110,7 +111,7 @@ function createNewNote(params) {
|
|||
}
|
||||
|
||||
return sql.transactional(() => {
|
||||
const note = new Note({
|
||||
const note = new BeccaNote(becca,{
|
||||
noteId: params.noteId, // optionally can force specific noteId
|
||||
title: params.title,
|
||||
isProtected: !!params.isProtected,
|
||||
|
@ -120,7 +121,7 @@ function createNewNote(params) {
|
|||
|
||||
note.setContent(params.content);
|
||||
|
||||
const branch = new Branch({
|
||||
const branch = new BeccaBranch(becca,{
|
||||
noteId: note.noteId,
|
||||
parentNoteId: params.parentNoteId,
|
||||
notePosition: params.notePosition !== undefined ? params.notePosition : getNewNotePosition(params.parentNoteId),
|
||||
|
@ -416,11 +417,12 @@ function saveLinks(note, content) {
|
|||
throw new Error("Unrecognized type " + note.type);
|
||||
}
|
||||
|
||||
const existingLinks = note.getLinks();
|
||||
const existingLinks = note.getRelations().filter(rel =>
|
||||
['internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'].includes(rel.name));
|
||||
|
||||
for (const foundLink of foundLinks) {
|
||||
const targetNote = repository.getNote(foundLink.value);
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
const targetNote = becca.notes[foundLink.value];
|
||||
if (!targetNote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -429,7 +431,7 @@ function saveLinks(note, content) {
|
|||
&& existingLink.name === foundLink.name);
|
||||
|
||||
if (!existingLink) {
|
||||
const newLink = new Attribute({
|
||||
const newLink = new BeccaAttribute({
|
||||
noteId: note.noteId,
|
||||
type: 'relation',
|
||||
name: foundLink.name,
|
||||
|
@ -438,10 +440,6 @@ function saveLinks(note, content) {
|
|||
|
||||
existingLinks.push(newLink);
|
||||
}
|
||||
else if (existingLink.isDeleted) {
|
||||
existingLink.isDeleted = false;
|
||||
existingLink.save();
|
||||
}
|
||||
// else the link exists so we don't need to do anything
|
||||
}
|
||||
|
||||
|
@ -451,8 +449,7 @@ function saveLinks(note, content) {
|
|||
&& existingLink.name === foundLink.name));
|
||||
|
||||
for (const unusedLink of unusedLinks) {
|
||||
unusedLink.isDeleted = true;
|
||||
unusedLink.save();
|
||||
unusedLink.markAsDeleted();
|
||||
}
|
||||
|
||||
return content;
|
||||
|
|
|
@ -94,11 +94,19 @@ function updateEntity(entity) {
|
|||
entity.beforeSaving();
|
||||
}
|
||||
|
||||
const clone = Object.assign({}, entity);
|
||||
let clone;
|
||||
|
||||
// this check requires that updatePojo is not static
|
||||
if (entity.updatePojo) {
|
||||
entity.updatePojo(clone);
|
||||
if (entity.getPojo) {
|
||||
clone = entity.getPojo();
|
||||
}
|
||||
else {
|
||||
// FIXME: delete this branch after migration to becca
|
||||
clone = Object.assign({}, entity);
|
||||
|
||||
// this check requires that updatePojo is not static
|
||||
if (entity.updatePojo) {
|
||||
entity.updatePojo(clone);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in clone) {
|
||||
|
|
Loading…
Reference in a new issue