diff --git a/package-lock.json b/package-lock.json index 2c239ffd0..fe701219e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "trilium", - "version": "0.54.2", + "version": "0.54.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "trilium", - "version": "0.54.2", + "version": "0.54.3", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/spec/search/parser.spec.js b/spec/search/parser.spec.js index 05df9ef40..6eadd2ec1 100644 --- a/spec/search/parser.spec.js +++ b/spec/search/parser.spec.js @@ -211,6 +211,35 @@ describe("Parser", () => { expect(secondSubSub.constructor.name).toEqual("LabelComparisonExp"); expect(secondSubSub.attributeName).toEqual("third"); }); + + it("label sub-expression without explicit operator", () => { + const rootExp = parse({ + fulltextTokens: [], + expressionTokens: tokens(["#first", ["#second", "or", "#third"], "#fourth"]), + searchContext: new SearchContext() + }); + + expect(rootExp.constructor.name).toEqual("AndExp"); + assertIsArchived(rootExp.subExpressions[0]); + + expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp"); + const [firstSub, secondSub, thirdSub] = rootExp.subExpressions[1].subExpressions; + + expect(firstSub.constructor.name).toEqual("AttributeExistsExp"); + expect(firstSub.attributeName).toEqual("first"); + + expect(secondSub.constructor.name).toEqual("OrExp"); + const [firstSubSub, secondSubSub] = secondSub.subExpressions; + + expect(firstSubSub.constructor.name).toEqual("AttributeExistsExp"); + expect(firstSubSub.attributeName).toEqual("second"); + + expect(secondSubSub.constructor.name).toEqual("AttributeExistsExp"); + expect(secondSubSub.attributeName).toEqual("third"); + + expect(thirdSub.constructor.name).toEqual("AttributeExistsExp"); + expect(thirdSub.attributeName).toEqual("fourth"); + }); }); describe("Invalid expressions", () => { diff --git a/src/services/search/services/parse.js b/src/services/search/services/parse.js index 8ab99fe31..ee23f3807 100644 --- a/src/services/search/services/parse.js +++ b/src/services/search/services/parse.js @@ -39,8 +39,12 @@ function getFulltext(tokens, searchContext) { } } -function isOperator(str) { - return str.match(/^[!=<>*%]+$/); +function isOperator(token) { + if (Array.isArray(token)) { + return false; + } + + return token.token.match(/^[!=<>*%]+$/); } function getExpression(tokens, searchContext, level = 0) { @@ -129,16 +133,16 @@ function getExpression(tokens, searchContext, level = 0) { i += 1; - const operator = tokens[i].token; + const operator = tokens[i]; if (!isOperator(operator)) { - searchContext.addError(`After content expected operator, but got "${tokens[i].token}" in ${context(i)}`); + searchContext.addError(`After content expected operator, but got "${operator.token}" in ${context(i)}`); return; } i++; - return new NoteContentFulltextExp(operator, {tokens: [tokens[i].token], raw }); + return new NoteContentFulltextExp(operator.token, {tokens: [tokens[i].token], raw }); } if (tokens[i].token === 'parents') { @@ -228,7 +232,7 @@ function getExpression(tokens, searchContext, level = 0) { function parseLabel(labelName) { searchContext.highlightedTokens.push(labelName); - if (i < tokens.length - 2 && isOperator(tokens[i + 1].token)) { + if (i < tokens.length - 2 && isOperator(tokens[i + 1])) { let operator = tokens[i + 1].token; i += 2; @@ -265,7 +269,7 @@ function getExpression(tokens, searchContext, level = 0) { return new RelationWhereExp(relationName, parseNoteProperty()); } - else if (i < tokens.length - 2 && isOperator(tokens[i + 1].token)) { + else if (i < tokens.length - 2 && isOperator(tokens[i + 1])) { searchContext.addError(`Relation can be compared only with property, e.g. ~relation.title=hello in ${context(i)}`); return null; @@ -384,7 +388,7 @@ function getExpression(tokens, searchContext, level = 0) { searchContext.addError('Mixed usage of AND/OR - always use parenthesis to group AND/OR expressions.'); } } - else if (isOperator(token)) { + else if (isOperator({token: token})) { searchContext.addError(`Misplaced or incomplete expression "${token}"`); } else {