add possibility to debug search queries by logging expression tree, #1655

This commit is contained in:
zadam 2021-02-18 22:10:49 +01:00
parent 0c9a11db6f
commit 859465841d
9 changed files with 65 additions and 14 deletions

View file

@ -0,0 +1,35 @@
import AbstractSearchOption from "./abstract_search_option.js";
const TPL = `
<tr data-search-option-conf="debug">
<td colSpan="2">
<span class="bx bx-bug"></span>
Debug
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>Debug will print extra debugging information into the console to aid in debugging complex queries.</p>
<p>To access the debug information, execute query and click on "Show backend log" in top left corner.</p>
</div>
</div>
<span class="bx bx-x icon-action search-option-del"></span>
</td>
</tr>`;
export default class Debug extends AbstractSearchOption {
static get optionName() { return "debug" };
static get attributeType() { return "label" };
static async create(noteId) {
await AbstractSearchOption.setAttribute(noteId,'label', 'debug');
}
doRender() {
return $(TPL);
}
}

View file

@ -20,6 +20,7 @@ import OrderBy from "../search_options/order_by.js";
import SearchScript from "../search_options/search_script.js";
import Limit from "../search_options/limit.js";
import DeleteNoteRevisionsSearchAction from "../search_actions/delete_note_revisions.js";
import Debug from "../search_options/debug.js";
const TPL = `
<div class="search-definition-widget">
@ -113,6 +114,11 @@ const TPL = `
limit
</button>
<button type="button" class="btn btn-sm" data-search-option-add="debug" title="Debug will print extra debugging information into the console to aid in debugging complex queries">
<span class="bx bx-bug"></span>
debug
</button>
<div class="dropdown" style="display: inline-block;">
<button class="btn btn-sm dropdown-toggle action-add-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="bx bxs-zap"></span>
@ -173,7 +179,8 @@ const OPTION_CLASSES = [
FastSearch,
IncludeArchivedNotes,
OrderBy,
Limit
Limit,
Debug
];
const ACTION_CLASSES = {};

View file

@ -24,6 +24,7 @@ async function search(note) {
orderBy: note.getLabelValue('orderBy'),
orderDirection: note.getLabelValue('orderDirection'),
limit: note.getLabelValue('limit'),
debug: note.hasLabel('debug'),
fuzzyAttributeSearch: false
});

View file

@ -10,6 +10,7 @@ class AncestorExp extends Expression {
super();
this.ancestorNoteId = ancestorNoteId;
this.ancestorDepth = ancestorDepth; // for DEBUG mode
this.ancestorDepthComparator = this.getComparator(ancestorDepth);
}

View file

@ -1,6 +1,10 @@
"use strict";
class Expression {
constructor() {
this.name = this.constructor.name; // for DEBUG mode to have expression name as part of dumped JSON
}
/**
* @param {NoteSet} inputNoteSet
* @param {object} executionContext

View file

@ -2,6 +2,7 @@
const Expression = require('./expression');
const NoteSet = require('../note_set');
const buildComparator = require("../services/build_comparator.js");
/**
* Search string is lower cased for case insensitive comparison. But when retrieving properties
@ -33,11 +34,13 @@ class PropertyComparisonExp extends Expression {
return name in PROP_MAPPING;
}
constructor(searchContext, propertyName, comparator) {
constructor(searchContext, propertyName, operator, comparedValue) {
super();
this.propertyName = PROP_MAPPING[propertyName];
this.comparator = comparator;
this.operator = operator; // for DEBUG mode
this.comparedValue = comparedValue; // for DEBUG mode
this.comparator = buildComparator(operator, comparedValue);
if (['contentsize', 'notesize', 'revisioncount'].includes(this.propertyName)) {
searchContext.dbLoadNeeded = true;

View file

@ -11,6 +11,7 @@ class SearchContext {
this.orderBy = params.orderBy;
this.orderDirection = params.orderDirection;
this.limit = params.limit;
this.debug = params.debug;
this.fuzzyAttributeSearch = !!params.fuzzyAttributeSearch;
this.highlightedTokens = [];
this.originalQuery = "";

View file

@ -194,7 +194,7 @@ function getExpression(tokens, searchContext, level = 0) {
i += 2;
return new OrExp([
new PropertyComparisonExp(searchContext, 'title', buildComparator('*=*', tokens[i].token)),
new PropertyComparisonExp(searchContext, 'title', '*=*', tokens[i].token),
new NoteContentProtectedFulltextExp('*=*', [tokens[i].token]),
new NoteContentUnprotectedFulltextExp('*=*', [tokens[i].token])
]);
@ -208,14 +208,7 @@ function getExpression(tokens, searchContext, level = 0) {
const comparedValue = resolveConstantOperand();
const comparator = buildComparator(operator, comparedValue);
if (!comparator) {
searchContext.addError(`Can't find operator '${operator}' in ${context(i - 2)}`);
return;
}
return new PropertyComparisonExp(searchContext, propertyName, comparator);
return new PropertyComparisonExp(searchContext, propertyName, operator, comparedValue);
}
searchContext.addError(`Unrecognized note property "${tokens[i].token}" in ${context(i)}`);
@ -411,8 +404,8 @@ function getExpression(tokens, searchContext, level = 0) {
function parse({fulltextTokens, expressionTokens, searchContext}) {
let exp = AndExp.of([
searchContext.includeArchivedNotes ? null : new PropertyComparisonExp(searchContext, "isarchived", buildComparator("=", "false")),
searchContext.ancestorNoteId ? new AncestorExp(searchContext.ancestorNoteId, searchContext.ancestorDepth) : null,
searchContext.includeArchivedNotes ? null : new PropertyComparisonExp(searchContext, "isarchived", "=", "false"),
(searchContext.ancestorNoteId && searchContext.ancestorNoteId !== 'root') ? new AncestorExp(searchContext.ancestorNoteId, searchContext.ancestorDepth) : null,
getFulltext(fulltextTokens, searchContext),
getExpression(expressionTokens, searchContext)
]);

View file

@ -127,6 +127,12 @@ function parseQueryToExpression(query, searchContext) {
originalQuery: query
});
if (searchContext.debug) {
log.info(`Fulltext tokens: ` + JSON.stringify(fulltextTokens));
log.info(`Expression tokens: ` + JSON.stringify(structuredExpressionTokens, null, 4));
log.info("Expression tree: " + JSON.stringify(expression, null, 4));
}
return expression;
}