From 3d12341ff15502c18317e30941163d7054aaedcf Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 23 May 2020 10:36:49 +0200 Subject: [PATCH] added querying by children --- spec/search.spec.js | 157 ++++++++----------- src/services/search/expressions/parent_of.js | 36 +++++ src/services/search/parser.js | 7 + 3 files changed, 112 insertions(+), 88 deletions(-) create mode 100644 src/services/search/expressions/parent_of.js diff --git a/spec/search.spec.js b/spec/search.spec.js index e60ab658c..053c17c44 100644 --- a/spec/search.spec.js +++ b/spec/search.spec.js @@ -17,11 +17,9 @@ describe("Search", () => { }); it("simple path match", async () => { - rootNote.child( - note("Europe") - .child( - note("Austria") - ) + rootNote + .child(note("Europe") + .child(note("Austria")) ); const parsingContext = new ParsingContext(); @@ -32,11 +30,9 @@ describe("Search", () => { }); it("only end leafs are results", async () => { - rootNote.child( - note("Europe") - .child( - note("Austria") - ) + rootNote + .child(note("Europe") + .child(note("Austria")) ); const parsingContext = new ParsingContext(); @@ -47,12 +43,10 @@ describe("Search", () => { }); it("only end leafs are results", async () => { - rootNote.child( - note("Europe") - .child( - note("Austria") - .label('capital', 'Vienna') - ) + rootNote + .child(note("Europe") + .child(note("Austria") + .label('capital', 'Vienna')) ); const parsingContext = new ParsingContext(); @@ -63,17 +57,13 @@ describe("Search", () => { }); it("numeric label comparison", async () => { - rootNote.child( - note("Europe") + rootNote + .child(note("Europe") .label('country', '', true) - .child( - note("Austria") - .label('population', '8859000') - ) - .child( - note("Czech Republic") - .label('population', '10650000') - ) + .child(note("Austria") + .label('population', '8859000')) + .child(note("Czech Republic") + .label('population', '10650000')) ); const parsingContext = new ParsingContext(); @@ -84,21 +74,15 @@ describe("Search", () => { }); it("numeric label comparison fallback to string comparison", async () => { - rootNote.child( - note("Europe") + rootNote + .child(note("Europe") .label('country', '', true) - .child( - note("Austria") - .label('established', '1955-07-27') - ) - .child( - note("Czech Republic") - .label('established', '1993-01-01') - ) - .child( - note("Hungary") - .label('established', '..wrong..') - ) + .child(note("Austria") + .label('established', '1955-07-27')) + .child(note("Czech Republic") + .label('established', '1993-01-01')) + .child(note("Hungary") + .label('established', '..wrong..')) ); const parsingContext = new ParsingContext(); @@ -109,22 +93,16 @@ describe("Search", () => { }); it("logical or", async () => { - rootNote.child( - note("Europe") + rootNote + .child(note("Europe") .label('country', '', true) - .child( - note("Austria") - .label('languageFamily', 'germanic') - ) - .child( - note("Czech Republic") - .label('languageFamily', 'slavic') - ) - .child( - note("Hungary") - .label('languageFamily', 'finnougric') - ) - ); + .child(note("Austria") + .label('languageFamily', 'germanic')) + .child(note("Czech Republic") + .label('languageFamily', 'slavic')) + .child(note("Hungary") + .label('languageFamily', 'finnougric')) + ); const parsingContext = new ParsingContext(); @@ -135,18 +113,13 @@ describe("Search", () => { }); it("fuzzy attribute search", async () => { - rootNote.child( - note("Europe") + rootNote + .child(note("Europe") .label('country', '', true) - .child( - note("Austria") - .label('languageFamily', 'germanic') - ) - .child( - note("Czech Republic") - .label('languageFamily', 'slavic') - ) - ); + .child(note("Austria") + .label('languageFamily', 'germanic')) + .child(note("Czech Republic") + .label('languageFamily', 'slavic'))); let parsingContext = new ParsingContext({fuzzyAttributeSearch: false}); @@ -167,15 +140,10 @@ describe("Search", () => { }); it("filter by note property", async () => { - rootNote.child( - note("Europe") - .child( - note("Austria") - ) - .child( - note("Czech Republic") - ) - ); + rootNote + .child(note("Europe") + .child(note("Austria")) + .child(note("Czech Republic"))); const parsingContext = new ParsingContext(); @@ -185,19 +153,12 @@ describe("Search", () => { }); it("filter by note's parent", async () => { - rootNote.child( - note("Europe") - .child( - note("Austria") - ) - .child( - note("Czech Republic") - ) - ) - .child( - note("Asia") - .child(note('Taiwan')) - ); + rootNote + .child(note("Europe") + .child(note("Austria")) + .child(note("Czech Republic"))) + .child(note("Asia") + .child(note('Taiwan'))); const parsingContext = new ParsingContext(); @@ -210,6 +171,26 @@ describe("Search", () => { expect(searchResults.length).toEqual(1); expect(findNoteByTitle(searchResults, "Taiwan")).toBeTruthy(); }); + + it("filter by note's child", async () => { + rootNote + .child(note("Europe") + .child(note("Austria")) + .child(note("Czech Republic"))) + .child(note("Oceania") + .child(note('Australia'))); + + const parsingContext = new ParsingContext(); + + let searchResults = await searchService.findNotesWithQuery('# note.child.title =* Aust', parsingContext); + expect(searchResults.length).toEqual(2); + expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy(); + expect(findNoteByTitle(searchResults, "Oceania")).toBeTruthy(); + + searchResults = await searchService.findNotesWithQuery('# note.child.title =* Aust AND note.child.title *= republic', parsingContext); + expect(searchResults.length).toEqual(1); + expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy(); + }); }); /** @return {Note} */ diff --git a/src/services/search/expressions/parent_of.js b/src/services/search/expressions/parent_of.js new file mode 100644 index 000000000..e7924feb7 --- /dev/null +++ b/src/services/search/expressions/parent_of.js @@ -0,0 +1,36 @@ +"use strict"; + +const Expression = require('./expression'); +const NoteSet = require('../note_set'); + +class ParentOfExp extends Expression { + constructor(subExpression) { + super(); + + this.subExpression = subExpression; + } + + execute(inputNoteSet, searchContext) { + const subInputNoteSet = new NoteSet(); + + for (const note of inputNoteSet.notes) { + subInputNoteSet.addAll(note.children); + } + + const subResNoteSet = this.subExpression.execute(subInputNoteSet, searchContext); + + const resNoteSet = new NoteSet(); + + for (const childNote of subResNoteSet.notes) { + for (const parentNote of childNote.parents) { + if (inputNoteSet.hasNote(parentNote)) { + resNoteSet.add(parentNote); + } + } + } + + return resNoteSet; + } +} + +module.exports = ParentOfExp; diff --git a/src/services/search/parser.js b/src/services/search/parser.js index 427b8e456..9ca4b149c 100644 --- a/src/services/search/parser.js +++ b/src/services/search/parser.js @@ -4,6 +4,7 @@ const AndExp = require('./expressions/and'); const OrExp = require('./expressions/or'); const NotExp = require('./expressions/not'); const ChildOfExp = require('./expressions/child_of'); +const ParentOfExp = require('./expressions/parent_of'); const PropertyComparisonExp = require('./expressions/property_comparison'); const AttributeExistsExp = require('./expressions/attribute_exists'); const LabelComparisonExp = require('./expressions/label_comparison'); @@ -56,6 +57,12 @@ function getExpression(tokens, parsingContext) { return new ChildOfExp(parseNoteProperty()); } + if (tokens[i] === 'child') { + i += 1; + + return new ParentOfExp(parseNoteProperty()); + } + if (tokens[i] === 'title') { const propertyName = tokens[i]; const operator = tokens[i + 1];