From 299008688d1682acea2a455c2261e7e5d74ac69b Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 19 Jan 2019 18:03:05 +0000 Subject: [PATCH] All: Search: Integration to CLI and mobile apps --- CliClient/app/command-search.js | 30 ---------------- ElectronClient/app/app.js | 10 ------ ElectronClient/app/gui/note-viewer/index.html | 34 ------------------- ReactNativeClient/lib/BaseApplication.js | 28 ++++++--------- .../lib/services/SearchEngine.js | 3 +- ReactNativeClient/lib/string-utils.js | 12 ++++--- 6 files changed, 20 insertions(+), 97 deletions(-) diff --git a/CliClient/app/command-search.js b/CliClient/app/command-search.js index f5f524a45..3f14157d4 100644 --- a/CliClient/app/command-search.js +++ b/CliClient/app/command-search.js @@ -41,7 +41,6 @@ class Command extends BaseCommand { title: pattern, query_pattern: pattern, query_folder_id: folder ? folder.id : '', - basic_search: true, type_: BaseModel.TYPE_SEARCH, }, }); @@ -50,35 +49,6 @@ class Command extends BaseCommand { type: 'SEARCH_SELECT', id: searchId, }); - - // let fields = Note.previewFields(); - // fields.push('body'); - // const notes = await Note.previews(folder ? folder.id : null, { - // fields: fields, - // anywherePattern: '*' + pattern + '*', - // }); - - // const fragmentLength = 50; - - // let parents = {}; - - // for (let i = 0; i < notes.length; i++) { - // const note = notes[i]; - // const parent = parents[note.parent_id] ? parents[note.parent_id] : await Folder.load(note.parent_id); - // parents[note.parent_id] = parent; - - // const idx = note.body.indexOf(pattern); - // let line = ''; - // if (idx >= 0) { - // let fragment = note.body.substr(Math.max(0, idx - fragmentLength / 2), fragmentLength); - // fragment = fragment.replace(/\n/g, ' '); - // line = sprintf('%s: %s / %s: %s', BaseModel.shortId(note.id), parent.title, note.title, fragment); - // } else { - // line = sprintf('%s: %s / %s', BaseModel.shortId(note.id), parent.title, note.title); - // } - - // this.stdout(line); - // } } } diff --git a/ElectronClient/app/app.js b/ElectronClient/app/app.js index 69b546418..686e7b87f 100644 --- a/ElectronClient/app/app.js +++ b/ElectronClient/app/app.js @@ -23,7 +23,6 @@ const DecryptionWorker = require('lib/services/DecryptionWorker'); const InteropService = require('lib/services/InteropService'); const InteropServiceHelper = require('./InteropServiceHelper.js'); const ResourceService = require('lib/services/ResourceService'); -const SearchEngine = require('lib/services/SearchEngine'); const ClipperServer = require('lib/ClipperServer'); const ExternalEditWatcher = require('lib/services/ExternalEditWatcher'); const { bridge } = require('electron').remote.require('./bridge'); @@ -198,11 +197,6 @@ class Application extends BaseApplication { this.updateEditorFont(); } - if (["NOTE_UPDATE_ONE", "NOTE_DELETE", "FOLDER_UPDATE_ONE", "FOLDER_DELETE"].indexOf(action.type) >= 0) { - if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(30 * 1000, { syncSteps: ["update_remote", "delete_remote"] }); - SearchEngine.instance().scheduleSyncTables(); - } - if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) { await AlarmService.updateNoteNotification(action.id, action.type === 'NOTE_DELETE'); } @@ -795,10 +789,6 @@ class Application extends BaseApplication { ResourceService.runInBackground(); - SearchEngine.instance().setDb(reg.db()); - SearchEngine.instance().setLogger(reg.logger()); - SearchEngine.instance().scheduleSyncTables(); - if (Setting.value('env') === 'dev') { AlarmService.updateAllNotifications(); } else { diff --git a/ElectronClient/app/gui/note-viewer/index.html b/ElectronClient/app/gui/note-viewer/index.html index a00380e32..c5a357e1c 100644 --- a/ElectronClient/app/gui/note-viewer/index.html +++ b/ElectronClient/app/gui/note-viewer/index.html @@ -266,40 +266,6 @@ }, { each: onEachElement, }); - - // if (typeof keyword === 'string') { - // keyword = { - // type: 'text', - // value: keyword, - // }; - // } - - // const isBasicSearch = ['ja', 'zh', 'ko'].indexOf(keyword.scriptType) >= 0; - - // if (keyword.type === 'regex') { - // const b = '[' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']+'; - - // // The capturing groups are a hack to go around the strange behaviour of the ignoreGroups property. What we want is to - // // exclude the first and last matches (the boundaries). What ignoreGroups does is ignore the first X groups. So - // // we put the first boundary and the keyword inside a group, that way the first groups is ignored (ignoreGroups = 1) - // // the second is included. And the last boundary is dropped because it's not in any group (it's important NOT to - // // put this one in a group because otherwise it cannot be excluded). - // let regexString = '(' + b + ')' + '(' + replaceRegexDiacritics(keyword.value) + ')' + b; - // if (isBasicSearch) regexString = keyword.value; - - // mark_.markRegExp(new RegExp(regexString, 'gmi'), { - // each: onEachElement, - // acrossElements: true, - // ignoreGroups: 1, - // }); - // } else { - // let accuracy = keyword.accuracy ? keyword.accuracy : 'exactly'; - // if (isBasicSearch) accuracy = 'partially'; - // mark_.mark([keyword.value], { - // each: onEachElement, - // accuracy: accuracy, - // }); - // } } ipcProxySendToHost('setMarkerCount', elementIndex); diff --git a/ReactNativeClient/lib/BaseApplication.js b/ReactNativeClient/lib/BaseApplication.js index 8dd82f64d..2720e2ba1 100644 --- a/ReactNativeClient/lib/BaseApplication.js +++ b/ReactNativeClient/lib/BaseApplication.js @@ -37,6 +37,7 @@ const ResourceFetcher = require('lib/services/ResourceFetcher'); const SearchEngineUtils = require('lib/services/SearchEngineUtils'); const DecryptionWorker = require('lib/services/DecryptionWorker'); const BaseService = require('lib/services/BaseService'); +const SearchEngine = require('lib/services/SearchEngine'); SyncTargetRegistry.addClass(SyncTargetFilesystem); SyncTargetRegistry.addClass(SyncTargetOneDrive); @@ -220,23 +221,7 @@ class BaseApplication { notes = await Tag.notes(parentId, options); } else if (parentType === BaseModel.TYPE_SEARCH) { const search = BaseModel.byId(state.searches, parentId); - - if (search.basic_search) { - // NOTE: Copied and pasted from ReactNativeClient/lib/components/screens/search.js - let p = search.query_pattern.split(' '); - let temp = []; - for (let i = 0; i < p.length; i++) { - let t = p[i].trim(); - if (!t) continue; - temp.push(t); - } - - notes = await Note.previews(null, { - anywherePattern: '*' + temp.join('*') + '*', - }); - } else { - notes = await SearchEngineUtils.notesForQuery(search.query_pattern); - } + notes = await SearchEngineUtils.notesForQuery(search.query_pattern); } } @@ -297,6 +282,11 @@ class BaseApplication { reduxSharedMiddleware(store, next, action); + if (this.hasGui() && ["NOTE_UPDATE_ONE", "NOTE_DELETE", "FOLDER_UPDATE_ONE", "FOLDER_DELETE"].indexOf(action.type) >= 0) { + if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(30 * 1000, { syncSteps: ["update_remote", "delete_remote"] }); + SearchEngine.instance().scheduleSyncTables(); + } + if (action.type == 'FOLDER_SELECT' || action.type === 'FOLDER_DELETE' || action.type === 'FOLDER_AND_NOTE_SELECT' || (action.type === 'SEARCH_UPDATE' && newState.notesParentType === 'Folder')) { Setting.setValue('activeFolderId', newState.selectedFolderId); this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null; @@ -543,6 +533,10 @@ class BaseApplication { ResourceFetcher.instance().setLogger(this.logger_); ResourceFetcher.instance().start(); + SearchEngine.instance().setDb(reg.db()); + SearchEngine.instance().setLogger(reg.logger()); + SearchEngine.instance().scheduleSyncTables(); + let currentFolderId = Setting.value('activeFolderId'); let currentFolder = null; if (currentFolderId) currentFolder = await Folder.load(currentFolderId); diff --git a/ReactNativeClient/lib/services/SearchEngine.js b/ReactNativeClient/lib/services/SearchEngine.js index 950b1d608..15a79dd1b 100644 --- a/ReactNativeClient/lib/services/SearchEngine.js +++ b/ReactNativeClient/lib/services/SearchEngine.js @@ -328,7 +328,8 @@ class SearchEngine { } normalizeText_(text) { - return removeDiacritics(text.normalize().toLowerCase()); + const normalizedText = text.normalize ? text.normalize() : text; + return removeDiacritics(normalizedText.toLowerCase()); } normalizeNote_(note) { diff --git a/ReactNativeClient/lib/string-utils.js b/ReactNativeClient/lib/string-utils.js index df538cc15..099e7f4ea 100644 --- a/ReactNativeClient/lib/string-utils.js +++ b/ReactNativeClient/lib/string-utils.js @@ -1,3 +1,5 @@ +const stringUtilsCommon = require('./string-utils-common.js'); + const defaultDiacriticsRemovalMap = [ {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, {'base':'AA','letters':/[\uA732]/g}, @@ -228,12 +230,12 @@ function surroundKeywords(keywords, text, prefix, suffix) { let regexString = keywords.map((k) => { if (k.type === 'regex') { - return k.valueRegex; + return stringUtilsCommon.replaceRegexDiacritics(k.valueRegex); } else { - return pregQuote(k); - } + return stringUtilsCommon.replaceRegexDiacritics(stringUtilsCommon.pregQuote(k.value)); + } }).join('|'); - regexString = '\\b(' + regexString + ')\\b' + regexString = '(' + regexString + ')' const re = new RegExp(regexString, 'gi'); return text.replace(re, prefix + '$1' + suffix); } @@ -251,5 +253,5 @@ function scriptType(s) { module.exports = Object.assign( { removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType }, - require('./string-utils-common.js'), + stringUtilsCommon, );