refactoring to ParserContext

This commit is contained in:
zadam 2020-05-21 11:46:01 +02:00
parent a8d12f723f
commit 75d8627f1c
4 changed files with 48 additions and 26 deletions

View File

@ -1,3 +1,4 @@
const ParsingContext = require("../src/services/search/parsing_context");
const parser = require('../src/services/search/parser'); const parser = require('../src/services/search/parser');
describe("Parser", () => { describe("Parser", () => {
@ -5,7 +6,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: ["hello", "hi"], fulltextTokens: ["hello", "hi"],
expressionTokens: [], expressionTokens: [],
includingNoteContent: false parsingContext: new ParsingContext(false)
}); });
expect(rootExp.constructor.name).toEqual("NoteCacheFulltextExp"); expect(rootExp.constructor.name).toEqual("NoteCacheFulltextExp");
@ -16,7 +17,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: ["hello", "hi"], fulltextTokens: ["hello", "hi"],
expressionTokens: [], expressionTokens: [],
includingNoteContent: true parsingContext: new ParsingContext(true)
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("OrExp");
@ -33,7 +34,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: [], fulltextTokens: [],
expressionTokens: ["#mylabel", "=", "text"], expressionTokens: ["#mylabel", "=", "text"],
includingNoteContent: true parsingContext: new ParsingContext(true)
}); });
expect(rootExp.constructor.name).toEqual("FieldComparisonExp"); expect(rootExp.constructor.name).toEqual("FieldComparisonExp");
@ -46,7 +47,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: [], fulltextTokens: [],
expressionTokens: ["#first", "=", "text", "AND", "#second", "=", "text"], expressionTokens: ["#first", "=", "text", "AND", "#second", "=", "text"],
includingNoteContent: true parsingContext: new ParsingContext(true)
}); });
expect(rootExp.constructor.name).toEqual("AndExp"); expect(rootExp.constructor.name).toEqual("AndExp");
@ -63,7 +64,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: [], fulltextTokens: [],
expressionTokens: ["#first", "=", "text", "#second", "=", "text"], expressionTokens: ["#first", "=", "text", "#second", "=", "text"],
includingNoteContent: true parsingContext: new ParsingContext(true)
}); });
expect(rootExp.constructor.name).toEqual("AndExp"); expect(rootExp.constructor.name).toEqual("AndExp");
@ -80,7 +81,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: [], fulltextTokens: [],
expressionTokens: ["#first", "=", "text", "OR", "#second", "=", "text"], expressionTokens: ["#first", "=", "text", "OR", "#second", "=", "text"],
includingNoteContent: true parsingContext: new ParsingContext(true)
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("OrExp");
@ -97,7 +98,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: ["hello"], fulltextTokens: ["hello"],
expressionTokens: ["#mylabel", "=", "text"], expressionTokens: ["#mylabel", "=", "text"],
includingNoteContent: false parsingContext: new ParsingContext(false)
}); });
expect(rootExp.constructor.name).toEqual("AndExp"); expect(rootExp.constructor.name).toEqual("AndExp");
@ -114,7 +115,7 @@ describe("Parser", () => {
const rootExp = parser({ const rootExp = parser({
fulltextTokens: [], fulltextTokens: [],
expressionTokens: ["#first", "=", "text", "OR", ["#second", "=", "text", "AND", "#third", "=", "text"]], expressionTokens: ["#first", "=", "text", "OR", ["#second", "=", "text", "AND", "#third", "=", "text"]],
includingNoteContent: false parsingContext: new ParsingContext(false)
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("OrExp");

View File

@ -1,3 +1,6 @@
"use strict";
const ParsingContext = require('./parsing_context');
const AndExp = require('./expressions/and'); const AndExp = require('./expressions/and');
const OrExp = require('./expressions/or'); const OrExp = require('./expressions/or');
const NotExp = require('./expressions/not'); const NotExp = require('./expressions/not');
@ -7,13 +10,13 @@ const NoteCacheFulltextExp = require('./expressions/note_cache_fulltext');
const NoteContentFulltextExp = require('./expressions/note_content_fulltext'); const NoteContentFulltextExp = require('./expressions/note_content_fulltext');
const comparatorBuilder = require('./comparator_builder'); const comparatorBuilder = require('./comparator_builder');
function getFulltext(tokens, includingNoteContent, highlightedTokens) { function getFulltext(tokens, parsingContext) {
highlightedTokens.push(...tokens); parsingContext.highlightedTokens.push(...tokens);
if (tokens.length === 0) { if (tokens.length === 0) {
return null; return null;
} }
else if (includingNoteContent) { else if (parsingContext.includeNoteContent) {
return new OrExp([ return new OrExp([
new NoteCacheFulltextExp(tokens), new NoteCacheFulltextExp(tokens),
new NoteContentFulltextExp(tokens) new NoteContentFulltextExp(tokens)
@ -28,7 +31,7 @@ function isOperator(str) {
return str.match(/^[=<>*]+$/); return str.match(/^[=<>*]+$/);
} }
function getExpression(tokens, highlightedTokens) { function getExpression(tokens, parsingContext) {
if (tokens.length === 0) { if (tokens.length === 0) {
return null; return null;
} }
@ -44,18 +47,18 @@ function getExpression(tokens, highlightedTokens) {
} }
if (Array.isArray(token)) { if (Array.isArray(token)) {
expressions.push(getExpression(token, highlightedTokens)); expressions.push(getExpression(token, parsingContext));
} }
else if (token.startsWith('#') || token.startsWith('@')) { else if (token.startsWith('#') || token.startsWith('@')) {
const type = token.startsWith('#') ? 'label' : 'relation'; const type = token.startsWith('#') ? 'label' : 'relation';
highlightedTokens.push(token.substr(1)); parsingContext.highlightedTokens.push(token.substr(1));
if (i < tokens.length - 2 && isOperator(tokens[i + 1])) { if (i < tokens.length - 2 && isOperator(tokens[i + 1])) {
const operator = tokens[i + 1]; const operator = tokens[i + 1];
const comparedValue = tokens[i + 2]; const comparedValue = tokens[i + 2];
highlightedTokens.push(comparedValue); parsingContext.highlightedTokens.push(comparedValue);
const comparator = comparatorBuilder(operator, comparedValue); const comparator = comparatorBuilder(operator, comparedValue);
@ -99,12 +102,10 @@ function getExpression(tokens, highlightedTokens) {
} }
} }
function parse({fulltextTokens, expressionTokens, includingNoteContent, highlightedTokens}) { function parse({fulltextTokens, expressionTokens, parsingContext}) {
highlightedTokens = highlightedTokens || [];
return AndExp.of([ return AndExp.of([
getFulltext(fulltextTokens, includingNoteContent, highlightedTokens), getFulltext(fulltextTokens, parsingContext),
getExpression(expressionTokens, highlightedTokens) getExpression(expressionTokens, parsingContext)
]); ]);
} }

View File

@ -0,0 +1,18 @@
"use strict";
class ParsingContext {
constructor(includeNoteContent) {
this.includeNoteContent = includeNoteContent;
this.highlightedTokens = [];
this.error = null;
}
addError(error) {
// we record only the first error, subsequent ones are usually consequence of the first
if (!this.error) {
this.error = error;
}
}
}
module.exports = ParsingContext;

View File

@ -42,15 +42,14 @@ async function findNotesWithExpression(expression) {
return searchResults; return searchResults;
} }
function parseQueryToExpression(query, highlightedTokens) { function parseQueryToExpression(query, parsingContext) {
const {fulltextTokens, expressionTokens} = lexer(query); const {fulltextTokens, expressionTokens} = lexer(query);
const structuredExpressionTokens = parens(expressionTokens); const structuredExpressionTokens = parens(expressionTokens);
const expression = parser({ const expression = parser({
fulltextTokens, fulltextTokens,
expressionTokens: structuredExpressionTokens, expressionTokens: structuredExpressionTokens,
includingNoteContent: false, parsingContext
highlightedTokens
}); });
return expression; return expression;
@ -61,9 +60,12 @@ async function searchNotesForAutocomplete(query) {
return []; return [];
} }
const highlightedTokens = []; const parsingContext = {
includeNoteContent: false,
highlightedTokens: []
};
const expression = parseQueryToExpression(query, highlightedTokens); const expression = parseQueryToExpression(query, parsingContext);
if (!expression) { if (!expression) {
return []; return [];
@ -73,7 +75,7 @@ async function searchNotesForAutocomplete(query) {
searchResults = searchResults.slice(0, 200); searchResults = searchResults.slice(0, 200);
highlightSearchResults(searchResults, highlightedTokens); highlightSearchResults(searchResults, parsingContext.highlightedTokens);
return searchResults.map(result => { return searchResults.map(result => {
return { return {