1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Laurent Cozic
29426814e4 item type 2022-08-04 10:49:19 +02:00
Laurent Cozic
a5e18200e8 Merge branch 'dev' into note_link_indexer 2022-07-30 14:46:44 +02:00
Laurent Cozic
2c464e89e6 update db 2022-07-12 15:25:33 +01:00
Laurent Cozic
68764bd82e update db 2022-07-12 15:18:43 +01:00
6 changed files with 224 additions and 199 deletions

View File

@@ -351,7 +351,7 @@ export default class JoplinDatabase extends Database {
// must be set in the synchronizer too.
// Note: v16 and v17 don't do anything. They were used to debug an issue.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41];
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@@ -910,6 +910,22 @@ export default class JoplinDatabase extends Database {
queries.push('ALTER TABLE `folders` ADD COLUMN icon TEXT NOT NULL DEFAULT ""');
}
if (targetVersion === 42) {
queries.push('ALTER TABLE `note_resources` ADD COLUMN item_type INT NOT NULL DEFAULT 0');
queries.push('UPDATE note_resources SET item_type = 4'); // 4 = Resource
queries = queries.concat(
this.alterColumnQueries('note_resources', {
id: 'INTEGER PRIMARY KEY',
note_id: 'TEXT NOT NULL',
resource_id: 'TEXT NOT NULL',
is_associated: 'INT NOT NULL',
last_seen_time: 'INT NOT NULL',
item_type: 'INT NOT NULL',
})
);
}
const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] };
queries.push(updateVersionQuery);

View File

@@ -5,7 +5,7 @@ import Setting from './Setting';
import shim from '../shim';
import time from '../time';
import markdownUtils from '../markdownUtils';
import { NoteEntity } from '../services/database/types';
import { BaseItemEntity, NoteEntity } from '../services/database/types';
import Tag from './Tag';
const { sprintf } = require('sprintf-js');
import Resource from './Resource';
@@ -119,7 +119,7 @@ export default class Note extends BaseItem {
return unique(itemIds);
}
static async linkedItems(body: string) {
static async linkedItems(body: string):Promise<BaseItemEntity[]> {
const itemIds = this.linkedItemIds(body);
const r = await BaseItem.loadItemsByIds(itemIds);
return r;

View File

@@ -1,10 +1,11 @@
import BaseModel from '../BaseModel';
import { SqlQuery } from '../database';
import BaseItem from './BaseItem';
import { BaseItemEntity } from '../services/database/types';
// - If is_associated = 1, note_resources indicates which note_id is currently associated with the given resource_id
// - If is_associated = 0, note_resources indicates which note_id *was* associated with the given resource_id
// - last_seen_time tells the last time that reosurce was associated with this note.
// - last_seen_time tells the last time that resource was associated with this note.
// - If last_seen_time is 0, it means the resource has never been associated with any note.
export default class NoteResource extends BaseModel {
@@ -76,25 +77,28 @@ export default class NoteResource extends BaseModel {
return rows.map((r: any) => r.note_id);
}
static async setAssociatedResources(noteId: string, resourceIds: string[]) {
static async setAssociatedItems(noteId: string, items: BaseItemEntity[]) {
const existingRows = await this.modelSelectAll('SELECT * FROM note_resources WHERE note_id = ?', [noteId]);
const notProcessedResourceIds = resourceIds.slice();
const notProcessedItems = items.slice();
const queries = [];
for (let i = 0; i < existingRows.length; i++) {
const row = existingRows[i];
const resourceIndex = resourceIds.indexOf(row.resource_id);
const resourceIndex = items.findIndex(i => i.id === row.resource_id);
if (resourceIndex >= 0) {
queries.push({ sql: 'UPDATE note_resources SET last_seen_time = ?, is_associated = 1 WHERE id = ?', params: [Date.now(), row.id] });
notProcessedResourceIds.splice(notProcessedResourceIds.indexOf(row.resource_id), 1);
notProcessedItems.splice(notProcessedItems.indexOf(row.resource_id), 1);
} else {
queries.push({ sql: 'UPDATE note_resources SET is_associated = 0 WHERE id = ?', params: [row.id] });
}
}
for (let i = 0; i < notProcessedResourceIds.length; i++) {
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: [noteId, notProcessedResourceIds[i], 1, Date.now()] });
for (let i = 0; i < notProcessedItems.length; i++) {
queries.push({
sql: 'INSERT INTO note_resources (note_id, resource_id, item_type, is_associated, last_seen_time) VALUES (?, ?, ?, ?)',
params: [noteId, notProcessedItems[i].id, notProcessedItems[i].type_, 1, Date.now()]
});
}
await this.db().transactionExecBatch(queries);
@@ -136,6 +140,7 @@ export default class NoteResource extends BaseModel {
`
SELECT resource_id, sum(is_associated)
FROM note_resources
WHERE item_type = 4
GROUP BY resource_id
HAVING sum(is_associated) <= 0
AND last_seen_time < ?

View File

@@ -80,7 +80,7 @@ export default class ResourceService extends BaseService {
break;
}
await this.setAssociatedResources(note.id, note.body);
await this.setAssociatedItems(note.id, note.body);
} else {
this.logger().warn(`ResourceService::indexNoteResources: A change was recorded for a note that has been deleted: ${change.item_id}`);
}
@@ -110,9 +110,9 @@ export default class ResourceService extends BaseService {
this.logger().info('ResourceService::indexNoteResources: Completed');
}
public async setAssociatedResources(noteId: string, noteBody: string) {
const resourceIds = await Note.linkedResourceIds(noteBody);
await NoteResource.setAssociatedResources(noteId, resourceIds);
public async setAssociatedItems(noteId: string, noteBody: string) {
const items = await Note.linkedItems(noteBody);
await NoteResource.setAssociatedItems(noteId, items);
}
public async deleteOrphanResources(expiryDelay: number = null) {
@@ -126,7 +126,7 @@ export default class ResourceService extends BaseService {
const note = await Note.load(results[0].id);
if (note) {
this.logger().info(sprintf('ResourceService::deleteOrphanResources: Skipping deletion of resource %s because it is still referenced in note %s. Re-indexing note content to fix the issue.', resourceId, note.id));
await this.setAssociatedResources(note.id, note.body);
await this.setAssociatedItems(note.id, note.body);
}
} else {
await Resource.delete(resourceId);

View File

@@ -52,6 +52,8 @@ export const defaultFolderIcon = () => {
// AUTO-GENERATED BY packages/tools/generate-database-types.js
/*
@@ -59,225 +61,225 @@ export const defaultFolderIcon = () => {
* Rerun sql-ts to regenerate this file.
*/
export interface AlarmEntity {
"id"?: number | null
"note_id"?: string
"trigger_time"?: number
"type_"?: number
'id'?: number | null;
'note_id'?: string;
'trigger_time'?: number;
'type_'?: number;
}
export interface DeletedItemEntity {
"id"?: number | null
"item_type"?: number
"item_id"?: string
"deleted_time"?: number
"sync_target"?: number
"type_"?: number
'id'?: number | null;
'item_type'?: number;
'item_id'?: string;
'deleted_time'?: number;
'sync_target'?: number;
'type_'?: number;
}
export interface FolderEntity {
"id"?: string | null
"title"?: string
"created_time"?: number
"updated_time"?: number
"user_created_time"?: number
"user_updated_time"?: number
"encryption_cipher_text"?: string
"encryption_applied"?: number
"parent_id"?: string
"is_shared"?: number
"share_id"?: string
"master_key_id"?: string
"icon"?: string
"type_"?: number
'id'?: string | null;
'title'?: string;
'created_time'?: number;
'updated_time'?: number;
'user_created_time'?: number;
'user_updated_time'?: number;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'parent_id'?: string;
'is_shared'?: number;
'share_id'?: string;
'master_key_id'?: string;
'icon'?: string;
'type_'?: number;
}
export interface ItemChangeEntity {
"id"?: number | null
"item_type"?: number
"item_id"?: string
"type"?: number
"created_time"?: number
"source"?: number
"before_change_item"?: string
"type_"?: number
'id'?: number | null;
'item_type'?: number;
'item_id'?: string;
'type'?: number;
'created_time'?: number;
'source'?: number;
'before_change_item'?: string;
'type_'?: number;
}
export interface KeyValueEntity {
"id"?: number | null
"key"?: string
"value"?: string
"type"?: number
"updated_time"?: number
"type_"?: number
'id'?: number | null;
'key'?: string;
'value'?: string;
'type'?: number;
'updated_time'?: number;
'type_'?: number;
}
export interface MigrationEntity {
"id"?: number | null
"number"?: number
"updated_time"?: number
"created_time"?: number
"type_"?: number
'id'?: number | null;
'number'?: number;
'updated_time'?: number;
'created_time'?: number;
'type_'?: number;
}
export interface NoteResourceEntity {
"id"?: number | null
"note_id"?: string
"resource_id"?: string
"is_associated"?: number
"last_seen_time"?: number
"type_"?: number
'id'?: number | null;
'note_id'?: string;
'resource_id'?: string;
'is_associated'?: number;
'last_seen_time'?: number;
'type_'?: number;
}
export interface NoteTagEntity {
"id"?: string | null
"note_id"?: string
"tag_id"?: string
"created_time"?: number
"updated_time"?: number
"user_created_time"?: number
"user_updated_time"?: number
"encryption_cipher_text"?: string
"encryption_applied"?: number
"is_shared"?: number
"type_"?: number
'id'?: string | null;
'note_id'?: string;
'tag_id'?: string;
'created_time'?: number;
'updated_time'?: number;
'user_created_time'?: number;
'user_updated_time'?: number;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'is_shared'?: number;
'type_'?: number;
}
export interface NoteEntity {
"id"?: string | null
"parent_id"?: string
"title"?: string
"body"?: string
"created_time"?: number
"updated_time"?: number
"is_conflict"?: number
"latitude"?: number
"longitude"?: number
"altitude"?: number
"author"?: string
"source_url"?: string
"is_todo"?: number
"todo_due"?: number
"todo_completed"?: number
"source"?: string
"source_application"?: string
"application_data"?: string
"order"?: number
"user_created_time"?: number
"user_updated_time"?: number
"encryption_cipher_text"?: string
"encryption_applied"?: number
"markup_language"?: number
"is_shared"?: number
"share_id"?: string
"conflict_original_id"?: string
"master_key_id"?: string
"type_"?: number
'id'?: string | null;
'parent_id'?: string;
'title'?: string;
'body'?: string;
'created_time'?: number;
'updated_time'?: number;
'is_conflict'?: number;
'latitude'?: number;
'longitude'?: number;
'altitude'?: number;
'author'?: string;
'source_url'?: string;
'is_todo'?: number;
'todo_due'?: number;
'todo_completed'?: number;
'source'?: string;
'source_application'?: string;
'application_data'?: string;
'order'?: number;
'user_created_time'?: number;
'user_updated_time'?: number;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'markup_language'?: number;
'is_shared'?: number;
'share_id'?: string;
'conflict_original_id'?: string;
'master_key_id'?: string;
'type_'?: number;
}
export interface NotesNormalizedEntity {
"id"?: string
"title"?: string
"body"?: string
"user_created_time"?: number
"user_updated_time"?: number
"is_todo"?: number
"todo_completed"?: number
"parent_id"?: string
"latitude"?: number
"longitude"?: number
"altitude"?: number
"source_url"?: string
"todo_due"?: number
"type_"?: number
'id'?: string;
'title'?: string;
'body'?: string;
'user_created_time'?: number;
'user_updated_time'?: number;
'is_todo'?: number;
'todo_completed'?: number;
'parent_id'?: string;
'latitude'?: number;
'longitude'?: number;
'altitude'?: number;
'source_url'?: string;
'todo_due'?: number;
'type_'?: number;
}
export interface ResourceLocalStateEntity {
"id"?: number | null
"resource_id"?: string
"fetch_status"?: number
"fetch_error"?: string
"type_"?: number
'id'?: number | null;
'resource_id'?: string;
'fetch_status'?: number;
'fetch_error'?: string;
'type_'?: number;
}
export interface ResourceEntity {
"id"?: string | null
"title"?: string
"mime"?: string
"filename"?: string
"created_time"?: number
"updated_time"?: number
"user_created_time"?: number
"user_updated_time"?: number
"file_extension"?: string
"encryption_cipher_text"?: string
"encryption_applied"?: number
"encryption_blob_encrypted"?: number
"size"?: number
"is_shared"?: number
"share_id"?: string
"master_key_id"?: string
"type_"?: number
'id'?: string | null;
'title'?: string;
'mime'?: string;
'filename'?: string;
'created_time'?: number;
'updated_time'?: number;
'user_created_time'?: number;
'user_updated_time'?: number;
'file_extension'?: string;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'encryption_blob_encrypted'?: number;
'size'?: number;
'is_shared'?: number;
'share_id'?: string;
'master_key_id'?: string;
'type_'?: number;
}
export interface ResourcesToDownloadEntity {
"id"?: number | null
"resource_id"?: string
"updated_time"?: number
"created_time"?: number
"type_"?: number
'id'?: number | null;
'resource_id'?: string;
'updated_time'?: number;
'created_time'?: number;
'type_'?: number;
}
export interface RevisionEntity {
"id"?: string | null
"parent_id"?: string
"item_type"?: number
"item_id"?: string
"item_updated_time"?: number
"title_diff"?: string
"body_diff"?: string
"metadata_diff"?: string
"encryption_cipher_text"?: string
"encryption_applied"?: number
"updated_time"?: number
"created_time"?: number
"type_"?: number
'id'?: string | null;
'parent_id'?: string;
'item_type'?: number;
'item_id'?: string;
'item_updated_time'?: number;
'title_diff'?: string;
'body_diff'?: string;
'metadata_diff'?: string;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'updated_time'?: number;
'created_time'?: number;
'type_'?: number;
}
export interface SettingEntity {
"key"?: string | null
"value"?: string | null
"type_"?: number
'key'?: string | null;
'value'?: string | null;
'type_'?: number;
}
export interface SyncItemEntity {
"id"?: number | null
"sync_target"?: number
"sync_time"?: number
"item_type"?: number
"item_id"?: string
"sync_disabled"?: number
"sync_disabled_reason"?: string
"force_sync"?: number
"item_location"?: number
"type_"?: number
'id'?: number | null;
'sync_target'?: number;
'sync_time'?: number;
'item_type'?: number;
'item_id'?: string;
'sync_disabled'?: number;
'sync_disabled_reason'?: string;
'force_sync'?: number;
'item_location'?: number;
'type_'?: number;
}
export interface TableFieldEntity {
"id"?: number | null
"table_name"?: string
"field_name"?: string
"field_type"?: number
"field_default"?: string | null
"type_"?: number
'id'?: number | null;
'table_name'?: string;
'field_name'?: string;
'field_type'?: number;
'field_default'?: string | null;
'type_'?: number;
}
export interface TagEntity {
"id"?: string | null
"title"?: string
"created_time"?: number
"updated_time"?: number
"user_created_time"?: number
"user_updated_time"?: number
"encryption_cipher_text"?: string
"encryption_applied"?: number
"is_shared"?: number
"parent_id"?: string
"type_"?: number
'id'?: string | null;
'title'?: string;
'created_time'?: number;
'updated_time'?: number;
'user_created_time'?: number;
'user_updated_time'?: number;
'encryption_cipher_text'?: string;
'encryption_applied'?: number;
'is_shared'?: number;
'parent_id'?: string;
'type_'?: number;
}
export interface TagsWithNoteCountEntity {
"id"?: string | null
"title"?: string | null
"created_time"?: number | null
"updated_time"?: number | null
"note_count"?: any | null
"todo_completed_count"?: any | null
"type_"?: number
'id'?: string | null;
'title'?: string | null;
'created_time'?: number | null;
'updated_time'?: number | null;
'note_count'?: any | null;
'todo_completed_count'?: any | null;
'type_'?: number;
}
export interface VersionEntity {
"version"?: number
"table_fields_version"?: number
"type_"?: number
'version'?: number;
'table_fields_version'?: number;
'type_'?: number;
}

View File

@@ -6,12 +6,14 @@ const fs = require('fs-extra');
async function main() {
// Run the CLI app once so as to generate the database file
process.chdir(`${rootDir}/packages/app-cli`);
const sqliteFilePath = `${require('os').homedir()}/.config/joplindev/database.sqlite`;
await fs.remove(sqliteFilePath);
await execCommand2('yarn start version');
const sqlTsConfig = {
'client': 'sqlite3',
'connection': {
'filename': `${require('os').homedir()}/.config/joplindev/database.sqlite`,
'filename': sqliteFilePath,
},
'tableNameCasing': 'pascal',
'singularTableNames': true,