123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- const SearchContext = require("../../src/services/search/search_context");
- const parse = require('../../src/services/search/services/parse');
- function tokens(toks, cur = 0) {
- return toks.map(arg => {
- if (Array.isArray(arg)) {
- return tokens(arg, cur);
- }
- else {
- cur += arg.length;
- return {
- token: arg,
- inQuotes: false,
- startIndex: cur - arg.length,
- endIndex: cur - 1
- };
- }
- });
- }
- function assertIsArchived(exp) {
- expect(exp.constructor.name).toEqual("PropertyComparisonExp");
- expect(exp.propertyName).toEqual("isArchived");
- expect(exp.operator).toEqual("=");
- expect(exp.comparedValue).toEqual("false");
- }
- describe("Parser", () => {
- it("fulltext parser without content", () => {
- const rootExp = parse({
- fulltextTokens: tokens(["hello", "hi"]),
- expressionTokens: [],
- searchContext: new SearchContext({includeNoteContent: false, excludeArchived: true})
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- expect(rootExp.subExpressions[0].constructor.name).toEqual("PropertyComparisonExp");
- expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
- expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
- expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]);
- });
- it("fulltext parser with content", () => {
- const rootExp = parse({
- fulltextTokens: tokens(["hello", "hi"]),
- expressionTokens: [],
- searchContext: new SearchContext({includeNoteContent: true})
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
- const subs = rootExp.subExpressions[1].subExpressions;
- expect(subs[0].constructor.name).toEqual("NoteFlatTextExp");
- expect(subs[0].tokens).toEqual(["hello", "hi"]);
- expect(subs[1].constructor.name).toEqual("NoteContentFulltextExp");
- expect(subs[1].tokens).toEqual(["hello", "hi"]);
- });
- it("simple label comparison", () => {
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#mylabel", "=", "text"]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
- expect(rootExp.subExpressions[1].attributeType).toEqual("label");
- expect(rootExp.subExpressions[1].attributeName).toEqual("mylabel");
- expect(rootExp.subExpressions[1].comparator).toBeTruthy();
- });
- it("simple attribute negation", () => {
- let rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#!mylabel"]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
- expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
- expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("label");
- expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("mylabel");
- rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["~!myrelation"]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
- expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
- expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("relation");
- expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("myrelation");
- });
- it("simple label AND", () => {
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "text", "and", "#second", "=", "text"]),
- searchContext: new SearchContext(true)
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
- const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
- expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
- expect(firstSub.attributeName).toEqual("first");
- expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
- expect(secondSub.attributeName).toEqual("second");
- });
- it("simple label AND without explicit AND", () => {
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "text", "#second", "=", "text"]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
- const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
- expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
- expect(firstSub.attributeName).toEqual("first");
- expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
- expect(secondSub.attributeName).toEqual("second");
- });
- it("simple label OR", () => {
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "text", "or", "#second", "=", "text"]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
- const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
- expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
- expect(firstSub.attributeName).toEqual("first");
- expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
- expect(secondSub.attributeName).toEqual("second");
- });
- it("fulltext and simple label", () => {
- const rootExp = parse({
- fulltextTokens: tokens(["hello"]),
- expressionTokens: tokens(["#mylabel", "=", "text"]),
- searchContext: new SearchContext({excludeArchived: true})
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- const [firstSub, secondSub, thirdSub] = rootExp.subExpressions;
- expect(firstSub.constructor.name).toEqual("PropertyComparisonExp");
- expect(firstSub.propertyName).toEqual('isArchived');
- expect(secondSub.constructor.name).toEqual("OrExp");
- expect(secondSub.subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
- expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]);
- expect(thirdSub.constructor.name).toEqual("LabelComparisonExp");
- expect(thirdSub.attributeName).toEqual("mylabel");
- });
- it("label sub-expression", () => {
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "text", "or", ["#second", "=", "text", "and", "#third", "=", "text"]]),
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
- const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
- expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
- expect(firstSub.attributeName).toEqual("first");
- expect(secondSub.constructor.name).toEqual("AndExp");
- const [firstSubSub, secondSubSub] = secondSub.subExpressions;
- expect(firstSubSub.constructor.name).toEqual("LabelComparisonExp");
- expect(firstSubSub.attributeName).toEqual("second");
- 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", () => {
- it("incomplete comparison", () => {
- const searchContext = new SearchContext();
- parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "="]),
- searchContext
- });
- expect(searchContext.error).toEqual('Misplaced or incomplete expression "="')
- });
- it("comparison between labels is impossible", () => {
- let searchContext = new SearchContext();
- searchContext.originalQuery = "#first = #second";
- parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "#second"]),
- searchContext
- });
- expect(searchContext.error).toEqual(`Error near token "#second" in "#first = #second", it's possible to compare with constant only.`);
- searchContext = new SearchContext();
- searchContext.originalQuery = "#first = note.relations.second";
- parse({
- fulltextTokens: [],
- expressionTokens: tokens(["#first", "=", "note", ".", "relations", "second"]),
- searchContext
- });
- expect(searchContext.error).toEqual(`Error near token "note" in "#first = note.relations.second", it's possible to compare with constant only.`);
- const rootExp = parse({
- fulltextTokens: [],
- expressionTokens: [
- { token: "#first", inQuotes: false },
- { token: "=", inQuotes: false },
- { token: "#second", inQuotes: true },
- ],
- searchContext: new SearchContext()
- });
- expect(rootExp.constructor.name).toEqual("AndExp");
- assertIsArchived(rootExp.subExpressions[0]);
- expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
- expect(rootExp.subExpressions[1].attributeType).toEqual("label");
- expect(rootExp.subExpressions[1].attributeName).toEqual("first");
- expect(rootExp.subExpressions[1].comparator).toBeTruthy();
- });
- it("searching by relation without note property", () => {
- const searchContext = new SearchContext();
- parse({
- fulltextTokens: [],
- expressionTokens: tokens(["~first", "=", "text", "-", "abc"]),
- searchContext
- });
- expect(searchContext.error).toEqual('Relation can be compared only with property, e.g. ~relation.title=hello in ""')
- });
- });
|