mirror of
https://github.com/zadam/trilium.git
synced 2025-06-04 08:58:40 +02:00
"smart" date values can now freely contain whitespaces
This commit is contained in:
parent
a6c79c934c
commit
46e373e822
@ -33,9 +33,6 @@ find $DIR/libraries -name "*.map" -type f -delete
|
||||
|
||||
rm -r $DIR/src/public/app
|
||||
|
||||
rm -r $DIR/node_modules/sqlite3/build
|
||||
rm -r $DIR/node_modules/sqlite3/deps
|
||||
|
||||
sed -i -e 's/app\/desktop.js/app-dist\/desktop.js/g' $DIR/src/views/desktop.ejs
|
||||
sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs
|
||||
sed -i -e 's/app\/setup.js/app-dist\/setup.js/g' $DIR/src/views/setup.ejs
|
||||
|
@ -65,6 +65,11 @@ describe("Lexer fulltext", () => {
|
||||
.toEqual(["what's", "u=p", "<b(r*t)h>"]);
|
||||
});
|
||||
|
||||
it("operator characters in expressions are separate tokens", () => {
|
||||
expect(lex("# abc+=-def**-+d").expressionTokens.map(t => t.token))
|
||||
.toEqual(["#", "abc", "+=-", "def", "**-+", "d"]);
|
||||
});
|
||||
|
||||
it("escaping special characters", () => {
|
||||
expect(lex("hello \\#\\~\\'").fulltextTokens.map(t => t.token))
|
||||
.toEqual(["hello", "#~'"]);
|
||||
|
@ -219,7 +219,7 @@ describe("Invalid expressions", () => {
|
||||
parsingContext
|
||||
});
|
||||
|
||||
expect(parsingContext.error).toEqual(`Error near token "note" in "#first = note.relations.s...", it's possible to compare with constant only.`);
|
||||
expect(parsingContext.error).toEqual(`Error near token "note" in "#first = note.relations.second", it's possible to compare with constant only.`);
|
||||
|
||||
const rootExp = parse({
|
||||
fulltextTokens: [],
|
||||
|
@ -121,11 +121,11 @@ describe("Search", () => {
|
||||
|
||||
const parsingContext = new ParsingContext();
|
||||
|
||||
let searchResults = searchService.findNotesWithQuery('#established <= 1955-01-01', parsingContext);
|
||||
let searchResults = searchService.findNotesWithQuery('#established <= "1955-01-01"', parsingContext);
|
||||
expect(searchResults.length).toEqual(1);
|
||||
expect(findNoteByTitle(searchResults, "Hungary")).toBeTruthy();
|
||||
|
||||
searchResults = searchService.findNotesWithQuery('#established > 1955-01-01', parsingContext);
|
||||
searchResults = searchService.findNotesWithQuery('#established > "1955-01-01"', parsingContext);
|
||||
expect(searchResults.length).toEqual(2);
|
||||
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
||||
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
||||
@ -154,20 +154,30 @@ describe("Search", () => {
|
||||
}
|
||||
|
||||
test("#year = YEAR", 1);
|
||||
test("#year = 'YEAR'", 0);
|
||||
test("#year >= YEAR", 1);
|
||||
test("#year <= YEAR", 1);
|
||||
test("#year < YEAR+1", 1);
|
||||
test("#year < YEAR + 1", 1);
|
||||
test("#year < year + 1", 1);
|
||||
test("#year > YEAR+1", 0);
|
||||
|
||||
test("#month = MONTH", 1);
|
||||
test("#month = month", 1);
|
||||
test("#month = 'MONTH'", 0);
|
||||
|
||||
test("#date = TODAY", 1);
|
||||
test("#date = today", 1);
|
||||
test("#date = 'today'", 0);
|
||||
test("#date > TODAY", 0);
|
||||
test("#date > TODAY-1", 1);
|
||||
test("#date > TODAY - 1", 1);
|
||||
test("#date < TODAY+1", 1);
|
||||
test("#date < TODAY + 1", 1);
|
||||
test("#date < 'TODAY + 1'", 1);
|
||||
|
||||
test("#dateTime <= NOW+10", 1);
|
||||
test("#dateTime <= NOW + 10", 1);
|
||||
test("#dateTime < NOW-10", 0);
|
||||
test("#dateTime >= NOW-10", 1);
|
||||
test("#dateTime < NOW-10", 0);
|
||||
|
@ -1,5 +1,3 @@
|
||||
const dayjs = require("dayjs");
|
||||
|
||||
const stringComparators = {
|
||||
"=": comparedValue => (val => val === comparedValue),
|
||||
"!=": comparedValue => (val => val !== comparedValue),
|
||||
@ -19,52 +17,9 @@ const numericComparators = {
|
||||
"<=": comparedValue => (val => parseFloat(val) <= comparedValue)
|
||||
};
|
||||
|
||||
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
|
||||
|
||||
function calculateSmartValue(v) {
|
||||
const match = smartValueRegex.exec(v);
|
||||
if (match === null) {
|
||||
return v;
|
||||
}
|
||||
|
||||
const keyword = match[1].toUpperCase();
|
||||
const num = match[2] ? parseInt(match[2].replace(/ /g, "")) : 0; // can contain spaces between sign and digits
|
||||
|
||||
let format, date;
|
||||
|
||||
if (keyword === 'NOW') {
|
||||
date = dayjs().add(num, 'second');
|
||||
format = "YYYY-MM-DD HH:mm:ss";
|
||||
}
|
||||
else if (keyword === 'TODAY') {
|
||||
date = dayjs().add(num, 'day');
|
||||
format = "YYYY-MM-DD";
|
||||
}
|
||||
else if (keyword === 'WEEK') {
|
||||
// FIXME: this will always use sunday as start of the week
|
||||
date = dayjs().startOf('week').add(7 * num, 'day');
|
||||
format = "YYYY-MM-DD";
|
||||
}
|
||||
else if (keyword === 'MONTH') {
|
||||
date = dayjs().add(num, 'month');
|
||||
format = "YYYY-MM";
|
||||
}
|
||||
else if (keyword === 'YEAR') {
|
||||
date = dayjs().add(num, 'year');
|
||||
format = "YYYY";
|
||||
}
|
||||
else {
|
||||
throw new Error("Unrecognized keyword: " + keyword);
|
||||
}
|
||||
|
||||
return date.format(format);
|
||||
}
|
||||
|
||||
function buildComparator(operator, comparedValue) {
|
||||
comparedValue = comparedValue.toLowerCase();
|
||||
|
||||
comparedValue = calculateSmartValue(comparedValue);
|
||||
|
||||
if (operator in numericComparators && !isNaN(comparedValue)) {
|
||||
return numericComparators[operator](parseFloat(comparedValue));
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ function lex(str) {
|
||||
let currentWord = '';
|
||||
|
||||
function isSymbolAnOperator(chr) {
|
||||
return ['=', '*', '>', '<', '!'].includes(chr);
|
||||
return ['=', '*', '>', '<', '!', "-", "+"].includes(chr);
|
||||
}
|
||||
|
||||
function isPreviousSymbolAnOperator() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const dayjs = require("dayjs");
|
||||
const AndExp = require('../expressions/and.js');
|
||||
const OrExp = require('../expressions/or.js');
|
||||
const NotExp = require('../expressions/not.js');
|
||||
@ -14,7 +15,7 @@ const NoteCacheFulltextExp = require('../expressions/note_cache_flat_text.js');
|
||||
const NoteContentProtectedFulltextExp = require('../expressions/note_content_protected_fulltext.js');
|
||||
const NoteContentUnprotectedFulltextExp = require('../expressions/note_content_unprotected_fulltext.js');
|
||||
const OrderByAndLimitExp = require('../expressions/order_by_and_limit.js');
|
||||
const comparatorBuilder = require('./build_comparator.js');
|
||||
const buildComparator = require('./build_comparator.js');
|
||||
const ValueExtractor = require('../value_extractor.js');
|
||||
|
||||
function getFulltext(tokens, parsingContext) {
|
||||
@ -41,7 +42,7 @@ function getFulltext(tokens, parsingContext) {
|
||||
|
||||
return new AndExp([
|
||||
textSearchExpression,
|
||||
new PropertyComparisonExp("isarchived", comparatorBuilder("=", "false"))
|
||||
new PropertyComparisonExp("isarchived", buildComparator("=", "false"))
|
||||
]);
|
||||
}
|
||||
|
||||
@ -69,6 +70,55 @@ function getExpression(tokens, parsingContext, level = 0) {
|
||||
+ (endIndex !== parsingContext.originalQuery.length ? "..." : "") + '"';
|
||||
}
|
||||
|
||||
function resolveConstantOperand() {
|
||||
const operand = tokens[i];
|
||||
|
||||
if (!operand.inQuotes
|
||||
&& (operand.token.startsWith('#') || operand.token.startsWith('~') || operand.token === 'note')) {
|
||||
parsingContext.addError(`Error near token "${operand.token}" in ${context(i)}, it's possible to compare with constant only.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (operand.inQuotes || !["now", "today", "month", "year"].includes(operand.token)) {
|
||||
return operand.token;
|
||||
}
|
||||
|
||||
let delta = 0;
|
||||
|
||||
if (i + 2 < tokens.length) {
|
||||
if (tokens[i + 1].token === '+') {
|
||||
delta += parseInt(tokens[i + 2].token);
|
||||
}
|
||||
else if (tokens[i + 1].token === '-') {
|
||||
delta -= parseInt(tokens[i + 2].token);
|
||||
}
|
||||
}
|
||||
|
||||
let format, date;
|
||||
|
||||
if (operand.token === 'now') {
|
||||
date = dayjs().add(delta, 'second');
|
||||
format = "YYYY-MM-DD HH:mm:ss";
|
||||
}
|
||||
else if (operand.token === 'today') {
|
||||
date = dayjs().add(delta, 'day');
|
||||
format = "YYYY-MM-DD";
|
||||
}
|
||||
else if (operand.token === 'month') {
|
||||
date = dayjs().add(delta, 'month');
|
||||
format = "YYYY-MM";
|
||||
}
|
||||
else if (operand.token === 'year') {
|
||||
date = dayjs().add(delta, 'year');
|
||||
format = "YYYY";
|
||||
}
|
||||
else {
|
||||
throw new Error("Unrecognized keyword: " + operand.token);
|
||||
}
|
||||
|
||||
return date.format(format);
|
||||
}
|
||||
|
||||
function parseNoteProperty() {
|
||||
if (tokens[i].token !== '.') {
|
||||
parsingContext.addError('Expected "." to separate field path');
|
||||
@ -150,7 +200,7 @@ function getExpression(tokens, parsingContext, level = 0) {
|
||||
const propertyName = tokens[i].token;
|
||||
const operator = tokens[i + 1].token;
|
||||
const comparedValue = tokens[i + 2].token;
|
||||
const comparator = comparatorBuilder(operator, comparedValue);
|
||||
const comparator = buildComparator(operator, comparedValue);
|
||||
|
||||
if (!comparator) {
|
||||
parsingContext.addError(`Can't find operator '${operator}' in ${context(i)}`);
|
||||
@ -186,11 +236,12 @@ function getExpression(tokens, parsingContext, level = 0) {
|
||||
|
||||
if (i < tokens.length - 2 && isOperator(tokens[i + 1].token)) {
|
||||
let operator = tokens[i + 1].token;
|
||||
const comparedValue = tokens[i + 2].token;
|
||||
|
||||
if (!tokens[i + 2].inQuotes
|
||||
&& (comparedValue.startsWith('#') || comparedValue.startsWith('~') || comparedValue === 'note')) {
|
||||
parsingContext.addError(`Error near token "${comparedValue}" in ${context(i)}, it's possible to compare with constant only.`);
|
||||
i += 2;
|
||||
|
||||
const comparedValue = resolveConstantOperand();
|
||||
|
||||
if (comparedValue === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -200,13 +251,11 @@ function getExpression(tokens, parsingContext, level = 0) {
|
||||
operator = '*=*';
|
||||
}
|
||||
|
||||
const comparator = comparatorBuilder(operator, comparedValue);
|
||||
const comparator = buildComparator(operator, comparedValue);
|
||||
|
||||
if (!comparator) {
|
||||
parsingContext.addError(`Can't find operator '${operator}' in ${context(i)}`);
|
||||
parsingContext.addError(`Can't find operator '${operator}' in ${context(i - 1)}`);
|
||||
} else {
|
||||
i += 2;
|
||||
|
||||
return new LabelComparisonExp('label', labelName, comparator);
|
||||
}
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user