2020-06-03 22:24:41 +08:00
|
|
|
const ParsingContext = require("../../src/services/search/parsing_context.js");
|
|
|
|
const parser = require('../../src/services/search/parser.js');
|
2020-05-20 06:03:33 +08:00
|
|
|
|
2020-07-20 05:19:45 +08:00
|
|
|
function tokens(...args) {
|
|
|
|
return args.map(arg => {
|
|
|
|
if (Array.isArray(arg)) {
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return {
|
|
|
|
token: arg,
|
|
|
|
inQuotes: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-05-20 06:03:33 +08:00
|
|
|
describe("Parser", () => {
|
|
|
|
it("fulltext parser without content", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
2020-07-20 05:19:45 +08:00
|
|
|
fulltextTokens: tokens("hello", "hi"),
|
2020-05-21 17:18:15 +08:00
|
|
|
expressionTokens: [],
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext({includeNoteContent: false})
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-20 06:03:33 +08:00
|
|
|
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(rootExp.constructor.name).toEqual("NoteCacheFulltextExp");
|
|
|
|
expect(rootExp.tokens).toEqual(["hello", "hi"]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("fulltext parser with content", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
2020-07-20 05:19:45 +08:00
|
|
|
fulltextTokens: tokens("hello", "hi"),
|
2020-05-21 17:18:15 +08:00
|
|
|
expressionTokens: [],
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext({includeNoteContent: true})
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("OrExp");
|
2020-07-19 21:25:24 +08:00
|
|
|
const subs = rootExp.subExpressions;
|
|
|
|
|
|
|
|
expect(subs[0].constructor.name).toEqual("NoteCacheFulltextExp");
|
|
|
|
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
2020-05-21 05:20:39 +08:00
|
|
|
|
2020-07-19 21:25:24 +08:00
|
|
|
expect(subs[1].constructor.name).toEqual("NoteContentProtectedFulltextExp");
|
|
|
|
expect(subs[1].tokens).toEqual(["hello", "hi"]);
|
2020-05-21 05:20:39 +08:00
|
|
|
|
2020-07-19 21:25:24 +08:00
|
|
|
expect(subs[2].constructor.name).toEqual("NoteContentUnprotectedFulltextExp");
|
|
|
|
expect(subs[2].tokens).toEqual(["hello", "hi"]);
|
2020-05-21 05:20:39 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it("simple label comparison", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#mylabel", "=", "text"),
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext()
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(rootExp.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(rootExp.attributeType).toEqual("label");
|
|
|
|
expect(rootExp.attributeName).toEqual("mylabel");
|
|
|
|
expect(rootExp.comparator).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
2020-07-19 21:25:24 +08:00
|
|
|
it("simple attribute negation", () => {
|
|
|
|
let rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#!mylabel"),
|
2020-07-19 21:25:24 +08:00
|
|
|
parsingContext: new ParsingContext()
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("NotExp");
|
|
|
|
expect(rootExp.subExpression.constructor.name).toEqual("AttributeExistsExp");
|
|
|
|
expect(rootExp.subExpression.attributeType).toEqual("label");
|
|
|
|
expect(rootExp.subExpression.attributeName).toEqual("mylabel");
|
|
|
|
|
|
|
|
rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("~!myrelation"),
|
2020-07-19 21:25:24 +08:00
|
|
|
parsingContext: new ParsingContext()
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("NotExp");
|
|
|
|
expect(rootExp.subExpression.constructor.name).toEqual("AttributeExistsExp");
|
|
|
|
expect(rootExp.subExpression.attributeType).toEqual("relation");
|
|
|
|
expect(rootExp.subExpression.attributeName).toEqual("myrelation");
|
|
|
|
});
|
|
|
|
|
2020-05-21 05:20:39 +08:00
|
|
|
it("simple label AND", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#first", "=", "text", "and", "#second", "=", "text"),
|
2020-05-21 17:46:01 +08:00
|
|
|
parsingContext: new ParsingContext(true)
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
|
|
const [firstSub, secondSub] = rootExp.subExpressions;
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("simple label AND without explicit AND", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#first", "=", "text", "#second", "=", "text"),
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext()
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
|
|
const [firstSub, secondSub] = rootExp.subExpressions;
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("simple label OR", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#first", "=", "text", "or", "#second", "=", "text"),
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext()
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("OrExp");
|
|
|
|
const [firstSub, secondSub] = rootExp.subExpressions;
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("fulltext and simple label", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
2020-07-20 05:19:45 +08:00
|
|
|
fulltextTokens: tokens("hello"),
|
|
|
|
expressionTokens: tokens("#mylabel", "=", "text"),
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext()
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
|
|
const [firstSub, secondSub] = rootExp.subExpressions;
|
|
|
|
|
|
|
|
expect(firstSub.constructor.name).toEqual("NoteCacheFulltextExp");
|
|
|
|
expect(firstSub.tokens).toEqual(["hello"]);
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(secondSub.attributeName).toEqual("mylabel");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("label sub-expression", () => {
|
2020-05-21 17:18:15 +08:00
|
|
|
const rootExp = parser({
|
|
|
|
fulltextTokens: [],
|
2020-07-20 05:19:45 +08:00
|
|
|
expressionTokens: tokens("#first", "=", "text", "or", tokens("#second", "=", "text", "and", "#third", "=", "text")),
|
2020-05-21 20:05:56 +08:00
|
|
|
parsingContext: new ParsingContext()
|
2020-05-21 17:18:15 +08:00
|
|
|
});
|
2020-05-21 05:20:39 +08:00
|
|
|
|
|
|
|
expect(rootExp.constructor.name).toEqual("OrExp");
|
|
|
|
const [firstSub, secondSub] = rootExp.subExpressions;
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
|
|
|
|
expect(secondSub.constructor.name).toEqual("AndExp");
|
|
|
|
const [firstSubSub, secondSubSub] = secondSub.subExpressions;
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(firstSubSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(firstSubSub.attributeName).toEqual("second");
|
|
|
|
|
2020-05-23 16:25:22 +08:00
|
|
|
expect(secondSubSub.constructor.name).toEqual("LabelComparisonExp");
|
2020-05-21 05:20:39 +08:00
|
|
|
expect(secondSubSub.attributeName).toEqual("third");
|
2020-05-20 06:03:33 +08:00
|
|
|
});
|
|
|
|
});
|
2020-07-20 05:19:45 +08:00
|
|
|
|
|
|
|
describe("Invalid tokens", () => {
|
|
|
|
it("incomplete comparison", () => {
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
|
|
|
|
parser({
|
|
|
|
fulltextTokens: [],
|
|
|
|
expressionTokens: tokens("#first", "="),
|
|
|
|
parsingContext
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(parsingContext.error).toEqual('Misplaced or incomplete expression "="')
|
|
|
|
});
|
|
|
|
});
|