import searchService = require('../../src/services/search/services/search'); import BNote = require('../../src/becca/entities/bnote'); import BBranch = require('../../src/becca/entities/bbranch'); import SearchContext = require('../../src/services/search/search_context'); import dateUtils = require('../../src/services/date_utils'); import becca = require('../../src/becca/becca'); // const { NoteBuilder, findNoteByTitle, note } = require("./becca_mocking"); import becca_mocking = require('./becca_mocking'); describe('Search', () => { let rootNote: any; beforeEach(() => { becca.reset(); rootNote = new becca_mocking.NoteBuilder(new BNote({ noteId: 'root', title: 'root', type: 'text' })); new BBranch({ branchId: 'none_root', noteId: 'root', parentNoteId: 'none', notePosition: 10, }); }); xit('simple path match', () => { rootNote.child(becca_mocking.note('Europe').child(becca_mocking.note('Austria'))); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('europe austria', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); xit('normal search looks also at attributes', () => { const austria = becca_mocking.note('Austria'); const vienna = becca_mocking.note('Vienna'); rootNote.child(austria.relation('capital', vienna.note)).child(vienna.label('inhabitants', '1888776')); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('capital', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('inhabitants', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Vienna')).toBeTruthy(); }); xit('normal search looks also at type and mime', () => { rootNote .child(becca_mocking.note('Effective Java', { type: 'book', mime: '' })) .child(becca_mocking.note('Hello World.java', { type: 'code', mime: 'text/x-java' })); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('book', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Effective Java')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('text', searchContext); // should match mime expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Hello World.java')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('java', searchContext); expect(searchResults.length).toEqual(2); }); xit('only end leafs are results', () => { rootNote.child(becca_mocking.note('Europe').child(becca_mocking.note('Austria'))); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('europe', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Europe')).toBeTruthy(); }); xit('only end leafs are results', () => { rootNote.child(becca_mocking.note('Europe').child(becca_mocking.note('Austria').label('capital', 'Vienna'))); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('Vienna', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('label comparison with short syntax', () => { rootNote.child( becca_mocking .note('Europe') .child(becca_mocking.note('Austria').label('capital', 'Vienna')) .child(becca_mocking.note('Czech Republic').label('capital', 'Prague')) ); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('#capital=Vienna', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); // case sensitivity: searchResults = searchService.findResultsWithQuery('#CAPITAL=VIENNA', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('#caPItal=vienNa', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('label comparison with full syntax', () => { rootNote.child( becca_mocking .note('Europe') .child(becca_mocking.note('Austria').label('capital', 'Vienna')) .child(becca_mocking.note('Czech Republic').label('capital', 'Prague')) ); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.labels.capital=Prague', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); }); it('numeric label comparison', () => { rootNote.child( becca_mocking .note('Europe') .label('country', '', true) .child(becca_mocking.note('Austria').label('population', '8859000')) .child(becca_mocking.note('Czech Republic').label('population', '10650000')) ); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('#country #population >= 10000000', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); }); xit('inherited label comparison', () => { rootNote.child( becca_mocking .note('Europe') .label('country', '', true) .child(becca_mocking.note('Austria')) .child(becca_mocking.note('Czech Republic')) ); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('austria #country', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('numeric label comparison fallback to string comparison', () => { // dates should not be coerced into numbers which would then give wrong numbers rootNote.child( becca_mocking .note('Europe') .label('country', '', true) .child(becca_mocking.note('Austria').label('established', '1955-07-27')) .child(becca_mocking.note('Czech Republic').label('established', '1993-01-01')) .child(becca_mocking.note('Hungary').label('established', '1920-06-04')) ); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('#established <= "1955-01-01"', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Hungary')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('#established > "1955-01-01"', searchContext); expect(searchResults.length).toEqual(2); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); }); it('smart date comparisons', () => { // dates should not be coerced into numbers which would then give wrong numbers rootNote.child( becca_mocking .note('My note', { dateCreated: dateUtils.localNowDateTime() }) .label('year', new Date().getFullYear().toString()) .label('month', dateUtils.localNowDate().substr(0, 7)) .label('date', dateUtils.localNowDate()) .label('dateTime', dateUtils.localNowDateTime()) ); const searchContext = new SearchContext(); function test(query: string, expectedResultCount: number) { const searchResults = searchService.findResultsWithQuery(query, searchContext); expect(searchResults.length) .withContext(`While searching for ${query} got unexpected result: [${searchResults.join(", ")}]`) .toEqual(expectedResultCount); if (expectedResultCount === 1) { expect(becca_mocking.findNoteByTitle(searchResults, 'My note')).toBeTruthy(); } } 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('note.dateCreated =* month', 2); 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); }); it('logical or', () => { rootNote.child( becca_mocking .note('Europe') .label('country', '', true) .child(becca_mocking.note('Austria').label('languageFamily', 'germanic')) .child(becca_mocking.note('Czech Republic').label('languageFamily', 'slavic')) .child(becca_mocking.note('Hungary').label('languageFamily', 'finnougric')) ); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('#languageFamily = slavic OR #languageFamily = germanic', searchContext); expect(searchResults.length).toEqual(2); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('fuzzy attribute search', () => { rootNote.child( becca_mocking .note('Europe') .label('country', '', true) .child(becca_mocking.note('Austria').label('languageFamily', 'germanic')) .child(becca_mocking.note('Czech Republic').label('languageFamily', 'slavic')) ); let searchContext = new SearchContext({ fuzzyAttributeSearch: false }); let searchResults = searchService.findResultsWithQuery('#language', searchContext); expect(searchResults.length).toEqual(0); searchResults = searchService.findResultsWithQuery('#languageFamily=ger', searchContext); expect(searchResults.length).toEqual(0); searchContext = new SearchContext({ fuzzyAttributeSearch: true }); searchResults = searchService.findResultsWithQuery('#language', searchContext); expect(searchResults.length).toEqual(2); searchResults = searchService.findResultsWithQuery('#languageFamily=ger', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('filter by note property', () => { rootNote.child(becca_mocking.note('Europe').child(becca_mocking.note('Austria')).child(becca_mocking.note('Czech Republic'))); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('# note.title =* czech', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); }); it("filter by note's parent", () => { rootNote .child( becca_mocking .note('Europe') .child(becca_mocking.note('Austria')) .child(becca_mocking.note('Czech Republic').child(becca_mocking.note('Prague'))) ) .child(becca_mocking.note('Asia').child(becca_mocking.note('Taiwan'))); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe', searchContext); expect(searchResults.length).toEqual(2); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('# note.parents.title = Asia', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Taiwan')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('# note.parents.parents.title = Europe', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Prague')).toBeTruthy(); }); it("filter by note's ancestor", () => { rootNote .child( becca_mocking .note('Europe') .child(becca_mocking.note('Austria')) .child(becca_mocking.note('Czech Republic').child(becca_mocking.note('Prague').label('city'))) ) .child(becca_mocking.note('Asia').child(becca_mocking.note('Taiwan').child(becca_mocking.note('Taipei').label('city')))); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Europe', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Prague')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Asia', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Taipei')).toBeTruthy(); }); it("filter by note's child", () => { rootNote .child( becca_mocking .note('Europe') .child(becca_mocking.note('Austria').child(becca_mocking.note('Vienna'))) .child(becca_mocking.note('Czech Republic').child(becca_mocking.note('Prague'))) ) .child(becca_mocking.note('Oceania').child(becca_mocking.note('Australia'))); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.children.title =* Aust', searchContext); expect(searchResults.length).toEqual(2); expect(becca_mocking.findNoteByTitle(searchResults, 'Europe')).toBeTruthy(); expect(becca_mocking.findNoteByTitle(searchResults, 'Oceania')).toBeTruthy(); searchResults = searchService.findResultsWithQuery( '# note.children.title =* Aust AND note.children.title *= republic', searchContext ); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Europe')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('# note.children.children.title = Prague', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Europe')).toBeTruthy(); }); it("filter by relation's note properties using short syntax", () => { const austria = becca_mocking.note('Austria'); const portugal = becca_mocking.note('Portugal'); rootNote.child( becca_mocking .note('Europe') .child(austria) .child(becca_mocking.note('Czech Republic').relation('neighbor', austria.note)) .child(portugal) .child(becca_mocking.note('Spain').relation('neighbor', portugal.note)) ); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# ~neighbor.title = Austria', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('# ~neighbor.title = Portugal', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Spain')).toBeTruthy(); }); it("filter by relation's note properties using long syntax", () => { const austria = becca_mocking.note('Austria'); const portugal = becca_mocking.note('Portugal'); rootNote.child( becca_mocking .note('Europe') .child(austria) .child(becca_mocking.note('Czech Republic').relation('neighbor', austria.note)) .child(portugal) .child(becca_mocking.note('Spain').relation('neighbor', portugal.note)) ); const searchContext = new SearchContext(); const searchResults = searchService.findResultsWithQuery('# note.relations.neighbor.title = Austria', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); }); it('filter by multiple level relation', () => { const austria = becca_mocking.note('Austria'); const slovakia = becca_mocking.note('Slovakia'); const italy = becca_mocking.note('Italy'); const ukraine = becca_mocking.note('Ukraine'); rootNote.child( becca_mocking .note('Europe') .child(austria.relation('neighbor', italy.note).relation('neighbor', slovakia.note)) .child(becca_mocking.note('Czech Republic').relation('neighbor', austria.note).relation('neighbor', slovakia.note)) .child(slovakia.relation('neighbor', ukraine.note)) .child(ukraine) ); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.relations.neighbor.relations.neighbor.title = Italy', searchContext); expect(searchResults.length).toEqual(1); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); searchResults = searchService.findResultsWithQuery('# note.relations.neighbor.relations.neighbor.title = Ukraine', searchContext); expect(searchResults.length).toEqual(2); expect(becca_mocking.findNoteByTitle(searchResults, 'Czech Republic')).toBeTruthy(); expect(becca_mocking.findNoteByTitle(searchResults, 'Austria')).toBeTruthy(); }); it('test note properties', () => { const austria = becca_mocking.note('Austria'); austria.relation('myself', austria.note); austria.label('capital', 'Vienna'); austria.label('population', '8859000'); rootNote .child(becca_mocking.note('Asia')) .child( becca_mocking.note('Europe').child(austria.child(becca_mocking.note('Vienna')).child(becca_mocking.note('Sebastian Kurz'))) ) .child(becca_mocking.note('Mozart').child(austria)); austria.note.isProtected = false; austria.note.dateCreated = '2020-05-14 12:11:42.001+0200'; austria.note.dateModified = '2020-05-14 13:11:42.001+0200'; austria.note.utcDateCreated = '2020-05-14 10:11:42.001Z'; austria.note.utcDateModified = '2020-05-14 11:11:42.001Z'; // austria.note.contentLength = 1001; const searchContext = new SearchContext(); function test(propertyName: string, value: string, expectedResultCount: number) { const searchResults = searchService.findResultsWithQuery(`# note.${propertyName} = ${value}`, searchContext); expect(searchResults.length).toEqual(expectedResultCount); } test('type', 'text', 7); test('TYPE', 'TEXT', 7); test('type', 'code', 0); test('mime', 'text/html', 6); test('mime', 'application/json', 0); test('isProtected', 'false', 7); test('isProtected', 'FALSE', 7); test('isProtected', 'true', 0); test('isProtected', 'TRUE', 0); test('dateCreated', "'2020-05-14 12:11:42.001+0200'", 1); test('dateCreated', 'wrong', 0); test('dateModified', "'2020-05-14 13:11:42.001+0200'", 1); test('dateModified', 'wrong', 0); test('utcDateCreated', "'2020-05-14 10:11:42.001Z'", 1); test('utcDateCreated', 'wrong', 0); test('utcDateModified', "'2020-05-14 11:11:42.001Z'", 1); test('utcDateModified', 'wrong', 0); test('parentCount', '2', 1); test('parentCount', '3', 0); test('childrenCount', '2', 1); test('childrenCount', '10', 0); test('attributeCount', '3', 1); test('attributeCount', '4', 0); test('labelCount', '2', 1); test('labelCount', '3', 0); test('relationCount', '1', 1); test('relationCount', '2', 0); }); it('test order by', () => { const italy = becca_mocking.note('Italy').label('capital', 'Rome'); const slovakia = becca_mocking.note('Slovakia').label('capital', 'Bratislava'); const austria = becca_mocking.note('Austria').label('capital', 'Vienna'); const ukraine = becca_mocking.note('Ukraine').label('capital', 'Kiev'); rootNote.child(becca_mocking.note('Europe').child(ukraine).child(slovakia).child(austria).child(italy)); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe orderBy note.title', searchContext); expect(searchResults.length).toEqual(4); expect(becca.notes[searchResults[0].noteId].title).toEqual('Austria'); expect(becca.notes[searchResults[1].noteId].title).toEqual('Italy'); expect(becca.notes[searchResults[2].noteId].title).toEqual('Slovakia'); expect(becca.notes[searchResults[3].noteId].title).toEqual('Ukraine'); searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe orderBy note.labels.capital', searchContext); expect(searchResults.length).toEqual(4); expect(becca.notes[searchResults[0].noteId].title).toEqual('Slovakia'); expect(becca.notes[searchResults[1].noteId].title).toEqual('Ukraine'); expect(becca.notes[searchResults[2].noteId].title).toEqual('Italy'); expect(becca.notes[searchResults[3].noteId].title).toEqual('Austria'); searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe orderBy note.labels.capital DESC', searchContext); expect(searchResults.length).toEqual(4); expect(becca.notes[searchResults[0].noteId].title).toEqual('Austria'); expect(becca.notes[searchResults[1].noteId].title).toEqual('Italy'); expect(becca.notes[searchResults[2].noteId].title).toEqual('Ukraine'); expect(becca.notes[searchResults[3].noteId].title).toEqual('Slovakia'); searchResults = searchService.findResultsWithQuery( '# note.parents.title = Europe orderBy note.labels.capital DESC limit 2', searchContext ); expect(searchResults.length).toEqual(2); expect(becca.notes[searchResults[0].noteId].title).toEqual('Austria'); expect(becca.notes[searchResults[1].noteId].title).toEqual('Italy'); searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1', searchContext); expect(searchResults.length).toEqual(1); searchResults = searchService.findResultsWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1000', searchContext); expect(searchResults.length).toEqual(4); }); it('test not(...)', () => { const italy = becca_mocking.note('Italy').label('capital', 'Rome'); const slovakia = becca_mocking.note('Slovakia').label('capital', 'Bratislava'); rootNote.child(becca_mocking.note('Europe').child(slovakia).child(italy)); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# not(#capital) and note.noteId != root', searchContext); expect(searchResults.length).toEqual(1); expect(becca.notes[searchResults[0].noteId].title).toEqual('Europe'); searchResults = searchService.findResultsWithQuery('#!capital and note.noteId != root', searchContext); expect(searchResults.length).toEqual(1); expect(becca.notes[searchResults[0].noteId].title).toEqual('Europe'); }); xit('test note.text *=* something', () => { const italy = becca_mocking.note('Italy').label('capital', 'Rome'); const slovakia = becca_mocking.note('Slovakia').label('capital', 'Bratislava'); rootNote.child(becca_mocking.note('Europe').child(slovakia).child(italy)); const searchContext = new SearchContext(); let searchResults = searchService.findResultsWithQuery('# note.text *=* vaki and note.noteId != root', searchContext); expect(searchResults.length).toEqual(1); expect(becca.notes[searchResults[0].noteId].title).toEqual('Slovakia'); }); xit('test that fulltext does not match archived notes', () => { const italy = becca_mocking.note('Italy').label('capital', 'Rome'); const slovakia = becca_mocking.note('Slovakia').label('capital', 'Bratislava'); rootNote .child( becca_mocking .note('Reddit') .label('archived', '', true) .child(becca_mocking.note('Post X')) .child(becca_mocking.note('Post Y')) ) .child(becca_mocking.note('Reddit is bad')); const searchContext = new SearchContext({ includeArchivedNotes: false }); let searchResults = searchService.findResultsWithQuery('reddit', searchContext); expect(searchResults.length).toEqual(1); expect(becca.notes[searchResults[0].noteId].title).toEqual('Reddit is bad'); }); // FIXME: test what happens when we order without any filter criteria // it("comparison between labels", () => { // rootNote // .child(becca_mocking.note("Europe") // .child(becca_mocking.note("Austria") // .label('capital', 'Vienna') // .label('largestCity', 'Vienna')) // .child(becca_mocking.note("Canada") // .label('capital', 'Ottawa') // .label('largestCity', 'Toronto')) // .child(becca_mocking.note("Czech Republic") // .label('capital', 'Prague') // .label('largestCity', 'Prague')) // ); // // const searchContext = new SearchContext(); // // const searchResults = searchService.findResultsWithQuery('#capital = #largestCity', searchContext); // expect(searchResults.length).toEqual(2); // expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy(); // expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy(); // }) });