WIP refactoring of data structures in note tree

This commit is contained in:
azivner 2018-03-24 20:41:27 -04:00
parent 7e524c0cd1
commit 511fb89af0
7 changed files with 817 additions and 109 deletions

14
.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="document.db" uuid="a2c75661-f9e2-478f-a69f-6a9409e69997">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url>
<driver-properties>
<property name="enable_load_extension" value="true" />
</driver-properties>
</data-source>
</component>
</project>

View File

@ -0,0 +1,588 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataSource name="document.db">
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.7">
<root id="1"/>
<schema id="2" parent="1" name="main">
<Current>1</Current>
<Visible>1</Visible>
</schema>
<collation id="3" parent="1" name="BINARY"/>
<collation id="4" parent="1" name="NOCASE"/>
<collation id="5" parent="1" name="RTRIM"/>
<table id="6" parent="2" name="api_tokens"/>
<table id="7" parent="2" name="attributes"/>
<table id="8" parent="2" name="event_log"/>
<table id="9" parent="2" name="images"/>
<table id="10" parent="2" name="note_images"/>
<table id="11" parent="2" name="note_revisions"/>
<table id="12" parent="2" name="note_tree"/>
<table id="13" parent="2" name="notes"/>
<table id="14" parent="2" name="options"/>
<table id="15" parent="2" name="recent_notes"/>
<table id="16" parent="2" name="source_ids"/>
<table id="17" parent="2" name="sqlite_master">
<System>1</System>
</table>
<table id="18" parent="2" name="sqlite_sequence">
<System>1</System>
</table>
<table id="19" parent="2" name="sync"/>
<column id="20" parent="6" name="apiTokenId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="21" parent="6" name="token">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="22" parent="6" name="dateCreated">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="23" parent="6" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>apiTokenId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="25" parent="6">
<ColNames>apiTokenId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
</key>
<column id="26" parent="7" name="attributeId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="27" parent="7" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="28" parent="7" name="name">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="29" parent="7" name="value">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="30" parent="7" name="position">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="31" parent="7" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="32" parent="7" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="33" parent="7" name="isDeleted">
<Position>8</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="34" parent="7" name="sqlite_autoindex_attributes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>attributeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="35" parent="7" name="IDX_attributes_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="36" parent="7" name="IDX_attributes_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations>
</ColumnCollations>
</index>
<key id="37" parent="7">
<ColNames>attributeId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName>
</key>
<column id="38" parent="8" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="39" parent="8" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="40" parent="8" name="comment">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="41" parent="8" name="dateAdded">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<key id="42" parent="8">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<foreign-key id="43" parent="8">
<ColNames>noteId</ColNames>
<RefTableName>notes</RefTableName>
<RefColNames>noteId</RefColNames>
</foreign-key>
<column id="44" parent="9" name="imageId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="45" parent="9" name="format">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="46" parent="9" name="checksum">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="47" parent="9" name="name">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="48" parent="9" name="data">
<Position>5</Position>
<DataType>BLOB|0s</DataType>
</column>
<column id="49" parent="9" name="isDeleted">
<Position>6</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="50" parent="9" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="51" parent="9" name="dateCreated">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="52" parent="9" name="sqlite_autoindex_images_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>imageId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="53" parent="9">
<ColNames>imageId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
</key>
<column id="54" parent="10" name="noteImageId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="55" parent="10" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="56" parent="10" name="imageId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="57" parent="10" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="58" parent="10" name="dateModified">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="59" parent="10" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="60" parent="10" name="sqlite_autoindex_note_images_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteImageId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="61" parent="10" name="IDX_note_images_noteId_imageId">
<ColNames>noteId
imageId</ColNames>
<ColumnCollations>
</ColumnCollations>
</index>
<index id="62" parent="10" name="IDX_note_images_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="63" parent="10" name="IDX_note_images_imageId">
<ColNames>imageId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="64" parent="10">
<ColNames>noteImageId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
</key>
<column id="65" parent="11" name="noteRevisionId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="66" parent="11" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="67" parent="11" name="title">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="68" parent="11" name="content">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="69" parent="11" name="isProtected">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="70" parent="11" name="dateModifiedFrom">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="71" parent="11" name="dateModifiedTo">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="72" parent="11" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="73" parent="11" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="74" parent="11" name="IDX_note_revisions_dateModifiedFrom">
<ColNames>dateModifiedFrom</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="75" parent="11" name="IDX_note_revisions_dateModifiedTo">
<ColNames>dateModifiedTo</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="76" parent="11">
<ColNames>noteRevisionId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
</key>
<column id="77" parent="12" name="noteTreeId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="78" parent="12" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="79" parent="12" name="parentNoteId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="80" parent="12" name="notePosition">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="81" parent="12" name="prefix">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="82" parent="12" name="isExpanded">
<Position>6</Position>
<DataType>BOOLEAN|0s</DataType>
</column>
<column id="83" parent="12" name="isDeleted">
<Position>7</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="84" parent="12" name="dateModified">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="85" parent="12" name="sqlite_autoindex_note_tree_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteTreeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="86" parent="12" name="IDX_note_tree_noteId_parentNoteId">
<ColNames>noteId
parentNoteId</ColNames>
<ColumnCollations>
</ColumnCollations>
</index>
<index id="87" parent="12" name="IDX_note_tree_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="88" parent="12">
<ColNames>noteTreeId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_tree_1</UnderlyingIndexName>
</key>
<column id="89" parent="13" name="noteId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="90" parent="13" name="title">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="91" parent="13" name="content">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="92" parent="13" name="isProtected">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="93" parent="13" name="isDeleted">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="94" parent="13" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="95" parent="13" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="96" parent="13" name="type">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text&apos;</DefaultExpression>
</column>
<column id="97" parent="13" name="mime">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text/html&apos;</DefaultExpression>
</column>
<index id="98" parent="13" name="sqlite_autoindex_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="99" parent="13" name="IDX_notes_isDeleted">
<ColNames>isDeleted</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="100" parent="13">
<ColNames>noteId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
</key>
<column id="101" parent="14" name="name">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="102" parent="14" name="value">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="103" parent="14" name="dateModified">
<Position>3</Position>
<DataType>INT|0s</DataType>
</column>
<column id="104" parent="14" name="isSynced">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<index id="105" parent="14" name="sqlite_autoindex_options_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="106" parent="14">
<ColNames>name</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
</key>
<column id="107" parent="15" name="noteTreeId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="108" parent="15" name="notePath">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="109" parent="15" name="dateAccessed">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="110" parent="15" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
</column>
<index id="111" parent="15" name="sqlite_autoindex_recent_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteTreeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="112" parent="15">
<ColNames>noteTreeId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
</key>
<column id="113" parent="16" name="sourceId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="114" parent="16" name="dateCreated">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="115" parent="16" name="sqlite_autoindex_source_ids_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>sourceId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="116" parent="16">
<ColNames>sourceId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
</key>
<column id="117" parent="17" name="type">
<Position>1</Position>
<DataType>text|0s</DataType>
</column>
<column id="118" parent="17" name="name">
<Position>2</Position>
<DataType>text|0s</DataType>
</column>
<column id="119" parent="17" name="tbl_name">
<Position>3</Position>
<DataType>text|0s</DataType>
</column>
<column id="120" parent="17" name="rootpage">
<Position>4</Position>
<DataType>integer|0s</DataType>
</column>
<column id="121" parent="17" name="sql">
<Position>5</Position>
<DataType>text|0s</DataType>
</column>
<column id="122" parent="18" name="name">
<Position>1</Position>
</column>
<column id="123" parent="18" name="seq">
<Position>2</Position>
</column>
<column id="124" parent="19" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="125" parent="19" name="entityName">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="126" parent="19" name="entityId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="127" parent="19" name="sourceId">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="128" parent="19" name="syncDate">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="129" parent="19" name="IDX_sync_entityName_entityId">
<ColNames>entityName
entityId</ColNames>
<ColumnCollations>
</ColumnCollations>
<Unique>1</Unique>
</index>
<index id="130" parent="19" name="IDX_sync_syncDate">
<ColNames>syncDate</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="131" parent="19">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
</database-model>
</dataSource>

View File

@ -0,0 +1,2 @@
#n:main
!<md> [0, 0, null, null, -2147483648, -2147483648]

9
.idea/jsLinters/jslint.xml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSLintConfiguration">
<option devel="true" />
<option es6="true" />
<option maxerr="50" />
<option node="true" />
</component>
</project>

3
.idea/misc.xml generated
View File

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager"> <component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>

View File

@ -1,6 +1,118 @@
"use strict"; "use strict";
class TreeCache {
constructor(noteRows, noteTreeRows) {
this.parents = [];
this.children = [];
this.childParentToNoteTree = {};
this.notes = {};
for (const noteRow of noteRows) {
const note = new NoteShort(this, noteRow);
this.notes[note.noteId] = note;
}
this.noteTree = {};
for (const noteTreeRow of noteTreeRows) {
const nt = new NoteTree(this, noteTreeRow);
this.noteTree[nt.noteTreeId] = nt;
this.addNoteTree(nt);
}
}
getNote(noteId) {
return this.notes[noteId];
}
addNoteTree(nt) {
this.parents[nt.noteId] = this.parents[nt.noteId] || [];
this.parents[nt.noteId].push(this.notes[nt.parentNoteId]);
this.children[nt.parentNoteId] = this.children[nt.parentNoteId] || [];
this.children[nt.parentNoteId].push(this.notes[nt.noteId]);
this.childParentToNoteTree[nt.noteId + '-' + nt.parentNoteId] = nt;
}
add(note, nt) {
this.notes[note.noteId] = note;
}
async getNoteTree(childNoteId, parentNoteId) {
return this.childParentToNoteTree[childNoteId + '-' + parentNoteId];
}
}
class NoteShort {
constructor(treeCache, row) {
this.treeCache = treeCache;
this.noteId = row.noteId;
this.title = row.title;
this.isProtected = row.isProtected;
this.type = row.type;
this.mime = row.mime;
this.hideInAutocomplete = row.hideInAutocomplete;
}
async getNoteTrees() {
const noteTrees = [];
for (const parent of this.treeCache.parents[this.noteId]) {
noteTrees.push(await this.treeCache.getNoteTree(this.noteId, p.noteId));
}
return noteTrees;
}
async getChildNoteTrees() {
const noteTrees = [];
for (const child of this.treeCache.children[this.noteId]) {
noteTrees.push(await this.treeCache.getNoteTree(child.noteId, this.noteId));
}
return noteTrees;
}
async getParentNotes() {
return this.treeCache.parents[this.noteId] || [];
}
async getChildNotes() {
return this.treeCache.children[this.noteId] || [];
}
get toString() {
return `Note(noteId=${this.noteId}, title=${this.title})`;
}
}
class NoteTree {
constructor(treeCache, row) {
this.treeCache = treeCache;
this.noteTreeId = row.noteTreeId;
this.noteId = row.noteId;
this.note = null;
this.parentNoteId = row.parentNoteId;
this.notePosition = row.notePosition;
this.prefix = row.prefix;
this.isExpanded = row.isExpanded;
}
async getNote() {
return this.treeCache.getNote(this.noteId);
}
get toString() {
return `NoteTree(noteTreeId=${this.noteTreeId})`;
}
}
const noteTree = (function() { const noteTree = (function() {
let treeCache;
const $tree = $("#tree"); const $tree = $("#tree");
const $parentList = $("#parent-list"); const $parentList = $("#parent-list");
const $parentListList = $("#parent-list-inner"); const $parentListList = $("#parent-list-inner");
@ -11,18 +123,14 @@ const noteTree = (function() {
let instanceName = null; // should have better place let instanceName = null; // should have better place
let startNotePath = null; let startNotePath = null;
let notesTreeMap = {};
let parentToChildren = {}; /** @type {Object.<string, NoteShort>} */
let childToParents = {}; let noteMap = {};
/** @type {Object.<string, NoteTree>} */
let parentChildToNoteTreeId = {}; let noteTreeMap = {};
let noteIdToNote = {};
let hiddenInAutocomplete = {};
function getNote(noteId) { function getNote(noteId) {
const note = noteIdToNote[noteId]; const note = noteMap[noteId];
if (!note) { if (!note) {
throwError("Can't find title for noteId='" + noteId + "'"); throwError("Can't find title for noteId='" + noteId + "'");
@ -34,6 +142,8 @@ const noteTree = (function() {
function getNoteTreeId(parentNoteId, childNoteId) { function getNoteTreeId(parentNoteId, childNoteId) {
assertArguments(parentNoteId, childNoteId); assertArguments(parentNoteId, childNoteId);
const key = parentNoteId + "-" + childNoteId; const key = parentNoteId + "-" + childNoteId;
// this can return undefined and client code should deal with it somehow // this can return undefined and client code should deal with it somehow
@ -44,13 +154,13 @@ const noteTree = (function() {
function getNoteTitle(noteId, parentNoteId = null) { function getNoteTitle(noteId, parentNoteId = null) {
assertArguments(noteId); assertArguments(noteId);
let title = getNote(noteId).title; let title = treeCache.getNote(noteId).title;
if (parentNoteId !== null) { if (parentNoteId !== null) {
const noteTreeId = getNoteTreeId(parentNoteId, noteId); const noteTreeId = getNoteTreeId(parentNoteId, noteId);
if (noteTreeId) { if (noteTreeId) {
const noteTree = notesTreeMap[noteTreeId]; const noteTree = noteTreeMap[noteTreeId];
if (noteTree.prefix) { if (noteTree.prefix) {
title = noteTree.prefix + ' - ' + title; title = noteTree.prefix + ' - ' + title;
@ -75,7 +185,7 @@ const noteTree = (function() {
function getNodesByNoteTreeId(noteTreeId) { function getNodesByNoteTreeId(noteTreeId) {
assertArguments(noteTreeId); assertArguments(noteTreeId);
const noteTree = notesTreeMap[noteTreeId]; const noteTree = noteTreeMap[noteTreeId];
return getNodesByNoteId(noteTree.noteId).filter(node => node.data.noteTreeId === noteTreeId); return getNodesByNoteId(noteTree.noteId).filter(node => node.data.noteTreeId === noteTreeId);
} }
@ -90,14 +200,14 @@ const noteTree = (function() {
function setPrefix(noteTreeId, prefix) { function setPrefix(noteTreeId, prefix) {
assertArguments(noteTreeId); assertArguments(noteTreeId);
notesTreeMap[noteTreeId].prefix = prefix; noteTreeMap[noteTreeId].prefix = prefix;
getNodesByNoteTreeId(noteTreeId).map(node => setNodeTitleWithPrefix(node)); getNodesByNoteTreeId(noteTreeId).map(node => setNodeTitleWithPrefix(node));
} }
function setNodeTitleWithPrefix(node) { function setNodeTitleWithPrefix(node) {
const noteTitle = getNoteTitle(node.data.noteId); const noteTitle = getNoteTitle(node.data.noteId);
const noteTree = notesTreeMap[node.data.noteTreeId]; const noteTree = noteTreeMap[node.data.noteTreeId];
const prefix = noteTree.prefix; const prefix = noteTree.prefix;
@ -109,61 +219,38 @@ const noteTree = (function() {
function removeParentChildRelation(parentNoteId, childNoteId) { function removeParentChildRelation(parentNoteId, childNoteId) {
assertArguments(parentNoteId, childNoteId); assertArguments(parentNoteId, childNoteId);
const key = parentNoteId + "-" + childNoteId; const parentNote = noteMap[parentNoteId];
const childNote = noteMap[childNoteId];
delete parentChildToNoteTreeId[key]; parentNote.children = parentNote.children.filter(note => note.noteId !== childNoteId);
childNote.parents = childNote.parents.filter(note => note.noteId !== parentNoteId);
parentToChildren[parentNoteId] = parentToChildren[parentNoteId].filter(noteId => noteId !== childNoteId); childNote.noteTree = childNote.noteTree.filter(nt => nt.parentNoteId !== parentNoteId);
childToParents[childNoteId] = childToParents[childNoteId].filter(noteId => noteId !== parentNoteId);
} }
function setParentChildRelation(noteTreeId, parentNoteId, childNoteId) { function setParentChildRelation(noteTreeId, parentNoteId, childNoteId) {
assertArguments(noteTreeId, parentNoteId, childNoteId); assertArguments(noteTreeId, parentNoteId, childNoteId);
const key = parentNoteId + "-" + childNoteId; const parentNote = noteMap[parentNoteId];
const childNote = noteMap[childNoteId];
parentChildToNoteTreeId[key] = noteTreeId; // FIXME: assert
if (!parentToChildren[parentNoteId]) { childNote.parents.push(parentNote);
parentToChildren[parentNoteId] = []; parentNote.children.push(childNote);
}
parentToChildren[parentNoteId].push(childNoteId); const noteTree = noteTreeMap[noteTreeId];
childNote.noteTree.push(noteTree);
if (!childToParents[childNoteId]) {
childToParents[childNoteId] = [];
}
childToParents[childNoteId].push(parentNoteId);
} }
function prepareNoteTree(notes) { async function prepareNoteTree(noteRows, noteTreeRows) {
assertArguments(notes); assertArguments(noteRows);
parentToChildren = {}; treeCache = new TreeCache(noteRows, noteTreeRows);
childToParents = {};
notesTreeMap = {};
for (const note of notes) { return await prepareNoteTreeInner(treeCache.getNote('root'));
notesTreeMap[note.noteTreeId] = note;
noteIdToNote[note.noteId] = {
noteId: note.noteId,
title: note.title,
isProtected: note.isProtected,
type: note.type,
mime: note.mime
};
delete note.title; // this should not be used. Use noteIdToNote instead
setParentChildRelation(note.noteTreeId, note.parentNoteId, note.noteId);
}
return prepareNoteTreeInner('root');
} }
function getExtraClasses(note) { async function getExtraClasses(note) {
assertArguments(note); assertArguments(note);
const extraClasses = []; const extraClasses = [];
@ -172,7 +259,7 @@ const noteTree = (function() {
extraClasses.push("protected"); extraClasses.push("protected");
} }
if (childToParents[note.noteId].length > 1) { if ((await note.getParentNotes()).length > 1) {
extraClasses.push("multiple-parents"); extraClasses.push("multiple-parents");
} }
@ -181,42 +268,40 @@ const noteTree = (function() {
return extraClasses.join(" "); return extraClasses.join(" ");
} }
function prepareNoteTreeInner(parentNoteId) { async function prepareNoteTreeInner(parentNote) {
assertArguments(parentNoteId); assertArguments(parentNote);
const childNoteIds = parentToChildren[parentNoteId]; const childrenNoteTrees = await parentNote.getChildNoteTrees();
if (!childNoteIds) {
messaging.logError("No children for " + parentNoteId + ". This shouldn't happen."); if (!childrenNoteTrees) {
messaging.logError(`No children for ${parentNote}. This shouldn't happen.`);
return; return;
} }
const noteList = []; const noteList = [];
for (const noteId of childNoteIds) { for (const noteTree of childrenNoteTrees) {
const note = getNote(noteId); const note = await noteTree.getNote();
const noteTreeId = getNoteTreeId(parentNoteId, noteId);
const noteTree = notesTreeMap[noteTreeId];
const title = (noteTree.prefix ? (noteTree.prefix + " - ") : "") + note.title; const title = (noteTree.prefix ? (noteTree.prefix + " - ") : "") + note.title;
const node = { const node = {
noteId: noteId, noteId: note.noteId,
parentNoteId: noteTree.parentNoteId, parentNoteId: noteTree.parentNoteId,
noteTreeId: noteTree.noteTreeId, noteTreeId: noteTree.noteTreeId,
isProtected: note.isProtected, isProtected: note.isProtected,
title: escapeHtml(title), title: escapeHtml(title),
extraClasses: getExtraClasses(note), extraClasses: getExtraClasses(note),
refKey: noteId, refKey: note.noteId,
expanded: note.type !== 'search' && noteTree.isExpanded expanded: note.type !== 'search' && noteTree.isExpanded
}; };
const hasChildren = parentToChildren[noteId] && parentToChildren[noteId].length > 0; const hasChildren = (await note.getChildNotes()).length > 0;
if (hasChildren || note.type === 'search') { if (hasChildren || note.type === 'search') {
node.folder = true; node.folder = true;
if (node.expanded && note.type !== 'search') { if (node.expanded && note.type !== 'search') {
node.children = prepareNoteTreeInner(noteId); node.children = await prepareNoteTreeInner(note);
} }
else { else {
node.lazy = true; node.lazy = true;
@ -609,7 +694,7 @@ const noteTree = (function() {
init: (event, data) => { init: (event, data) => {
const noteId = treeUtils.getNoteIdFromNotePath(startNotePath); const noteId = treeUtils.getNoteIdFromNotePath(startNotePath);
if (noteIdToNote[noteId] === undefined) { if (noteMap[noteId] === undefined) {
// note doesn't exist so don't try to activate it // note doesn't exist so don't try to activate it
startNotePath = null; startNotePath = null;
} }
@ -638,7 +723,7 @@ const noteTree = (function() {
mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead) mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
}, },
dnd: dragAndDropSetup, dnd: dragAndDropSetup,
lazyLoad: function(event, data){ lazyLoad: async function(event, data){
const noteId = data.node.data.noteId; const noteId = data.node.data.noteId;
const note = getNote(noteId); const note = getNote(noteId);
@ -646,7 +731,7 @@ const noteTree = (function() {
data.result = loadSearchNote(noteId); data.result = loadSearchNote(noteId);
} }
else { else {
data.result = prepareNoteTreeInner(noteId); data.result = await prepareNoteTreeInner(note);
} }
}, },
clones: { clones: {
@ -667,7 +752,7 @@ const noteTree = (function() {
for (const noteId of noteIds) { for (const noteId of noteIds) {
const noteTreeId = "virt" + randomString(10); const noteTreeId = "virt" + randomString(10);
notesTreeMap[noteTreeId] = { noteTreeMap[noteTreeId] = {
noteTreeId: noteTreeId, noteTreeId: noteTreeId,
noteId: noteId, noteId: noteId,
parentNoteId: searchNoteId, parentNoteId: searchNoteId,
@ -678,7 +763,7 @@ const noteTree = (function() {
setParentChildRelation(noteTreeId, searchNoteId, noteId); setParentChildRelation(noteTreeId, searchNoteId, noteId);
} }
return prepareNoteTreeInner(searchNoteId); return await prepareNoteTreeInner(searchNoteId);
} }
function getTree() { function getTree() {
@ -705,13 +790,7 @@ const noteTree = (function() {
startNotePath = getNotePathFromAddress(); startNotePath = getNotePathFromAddress();
} }
hiddenInAutocomplete = {}; return await prepareNoteTree(resp.notes, resp.noteTree);
for (const noteId of resp.hiddenInAutocomplete) {
hiddenInAutocomplete[noteId] = true;
}
return prepareNoteTree(resp.notes);
} }
$(() => loadTree().then(noteTree => initFancyTree(noteTree))); $(() => loadTree().then(noteTree => initFancyTree(noteTree)));
@ -827,9 +906,9 @@ const noteTree = (function() {
setParentChildRelation(result.noteTreeId, parentNoteId, result.noteId); setParentChildRelation(result.noteTreeId, parentNoteId, result.noteId);
notesTreeMap[result.noteTreeId] = result; noteTreeMap[result.noteTreeId] = result;
noteIdToNote[result.noteId] = { noteMap[result.noteId] = {
noteId: result.noteId, noteId: result.noteId,
title: result.title, title: result.title,
isProtected: result.isProtected, isProtected: result.isProtected,
@ -889,11 +968,7 @@ const noteTree = (function() {
} }
function getNoteTree(noteTreeId) { function getNoteTree(noteTreeId) {
return notesTreeMap[noteTreeId]; return noteTreeMap[noteTreeId];
}
function getNote(noteId) {
return noteIdToNote[noteId];
} }
$(document).bind('keydown', 'ctrl+o', e => { $(document).bind('keydown', 'ctrl+o', e => {

View File

@ -12,39 +12,56 @@ const sync_table = require('../../services/sync_table');
const wrap = require('express-promise-wrap').wrap; const wrap = require('express-promise-wrap').wrap;
router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => { router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => {
const notes = await sql.getRows(` const noteTree = await sql.getRows(`
SELECT SELECT
note_tree.*, noteTreeId,
notes.title, noteId,
notes.isProtected, parentNoteId,
notes.type notePosition,
FROM prefix,
isExpanded
FROM
note_tree note_tree
JOIN
notes ON notes.noteId = note_tree.noteId
WHERE WHERE
notes.isDeleted = 0 isDeleted = 0
AND note_tree.isDeleted = 0
ORDER BY ORDER BY
notePosition`); notePosition`);
let notes = [{
noteId: 'root',
title: 'root',
isProtected: false,
type: 'none',
mime: 'none'
}];
notes = notes.concat(await sql.getRows(`
SELECT
notes.noteId,
notes.title,
notes.isProtected,
notes.type,
notes.mime,
hideInAutocomplete.attributeId AS 'hideInAutocomplete'
FROM
notes
LEFT JOIN attributes AS hideInAutocomplete ON hideInAutocomplete.noteId = notes.noteId
AND hideInAutocomplete.name = 'hide_in_autocomplete'
AND hideInAutocomplete.isDeleted = 0
WHERE
notes.isDeleted = 0`));
protected_session.decryptNotes(req, notes); protected_session.decryptNotes(req, notes);
const hiddenInAutocomplete = await sql.getColumn(` notes.forEach(note => {
SELECT note.hideInAutocomplete = !!note.hideInAutocomplete;
DISTINCT noteId note.isProtected = !!note.isProtected;
FROM });
attributes
JOIN notes USING(noteId)
WHERE
attributes.name = 'hide_in_autocomplete'
AND attributes.isDeleted = 0
AND notes.isDeleted = 0`);
res.send({ res.send({
instanceName: config.General ? config.General.instanceName : null, instanceName: config.General ? config.General.instanceName : null,
noteTree: noteTree,
notes: notes, notes: notes,
hiddenInAutocomplete: hiddenInAutocomplete,
start_note_path: await options.getOption('start_note_path') start_note_path: await options.getOption('start_note_path')
}); });
})); }));