diff --git a/src/services/note_cache/note_cache.js b/src/services/note_cache/note_cache.js index 12942ee7f..a39898750 100644 --- a/src/services/note_cache/note_cache.js +++ b/src/services/note_cache/note_cache.js @@ -27,6 +27,20 @@ class NoteCache { return this.attributeIndex[`${type}-${name}`] || []; } + /** @return {Attribute[]} */ + findAttributesWithPrefix(type, name) { + const resArr = []; + const key = `${type}-${name}`; + + for (const idx in this.attributeIndex) { + if (idx.startsWith(key)) { + resArr.push(this.attributeIndex[idx]); + } + } + + return resArr.flat(); + } + decryptProtectedNotes() { for (const note of Object.values(this.notes)) { note.decrypt(); diff --git a/src/services/search/expressions/attribute_exists.js b/src/services/search/expressions/attribute_exists.js index 196937777..4f117a1fc 100644 --- a/src/services/search/expressions/attribute_exists.js +++ b/src/services/search/expressions/attribute_exists.js @@ -4,13 +4,17 @@ const NoteSet = require('../note_set'); const noteCache = require('../../note_cache/note_cache'); class AttributeExistsExp { - constructor(attributeType, attributeName) { + constructor(attributeType, attributeName, prefixMatch) { this.attributeType = attributeType; this.attributeName = attributeName; + this.prefixMatch = prefixMatch; } execute(noteSet) { - const attrs = noteCache.findAttributes(this.attributeType, this.attributeName); + const attrs = this.prefixMatch + ? noteCache.findAttributesWithPrefix(this.attributeType, this.attributeName) + : noteCache.findAttributes(this.attributeType, this.attributeName); + const resultNoteSet = new NoteSet(); for (const attr of attrs) { diff --git a/src/services/search/parser.js b/src/services/search/parser.js index e92d07129..da81e9218 100644 --- a/src/services/search/parser.js +++ b/src/services/search/parser.js @@ -1,6 +1,5 @@ "use strict"; -const ParsingContext = require('./parsing_context'); const AndExp = require('./expressions/and'); const OrExp = require('./expressions/or'); const NotExp = require('./expressions/not'); @@ -63,7 +62,8 @@ function getExpression(tokens, parsingContext) { const comparator = comparatorBuilder(operator, comparedValue); if (!comparator) { - throw new Error(`Can't find operator '${operator}'`); + parsingContext.addError(`Can't find operator '${operator}'`); + continue; } expressions.push(new FieldComparisonExp(type, token.substr(1), comparator)); @@ -71,7 +71,7 @@ function getExpression(tokens, parsingContext) { i += 2; } else { - expressions.push(new AttributeExistsExp(type, token.substr(1))); + expressions.push(new AttributeExistsExp(type, token.substr(1), parsingContext.fuzzyAttributeSearch)); } } else if (['and', 'or'].includes(token.toLowerCase())) { @@ -79,14 +79,14 @@ function getExpression(tokens, parsingContext) { op = token.toLowerCase(); } else if (op !== token.toLowerCase()) { - throw new Error('Mixed usage of AND/OR - always use parenthesis to group AND/OR expressions.'); + parsingContext.addError('Mixed usage of AND/OR - always use parenthesis to group AND/OR expressions.'); } } else if (isOperator(token)) { - throw new Error(`Misplaced or incomplete expression "${token}"`); + parsingContext.addError(`Misplaced or incomplete expression "${token}"`); } else { - throw new Error(`Unrecognized expression "${token}"`); + parsingContext.addError(`Unrecognized expression "${token}"`); } if (!op && expressions.length > 1) { diff --git a/src/services/search/search.js b/src/services/search/search.js index 1eb6faf77..c5c2d5236 100644 --- a/src/services/search/search.js +++ b/src/services/search/search.js @@ -5,6 +5,7 @@ const parens = require('./parens'); const parser = require('./parser'); const NoteSet = require("./note_set"); const SearchResult = require("./search_result"); +const ParsingContext = require("./parsing_context"); const noteCache = require('../note_cache/note_cache'); const noteCacheService = require('../note_cache/note_cache_service'); const hoistedNoteService = require('../hoisted_note'); @@ -60,10 +61,8 @@ async function searchNotesForAutocomplete(query) { return []; } - const parsingContext = { - includeNoteContent: false, - highlightedTokens: [] - }; + const parsingContext = new ParsingContext(false); + parsingContext.fuzzyAttributeSearch = true; const expression = parseQueryToExpression(query, parsingContext);