diff --git a/lib/shared/search-index.js b/lib/shared/search-index.js --- a/lib/shared/search-index.js +++ b/lib/shared/search-index.js @@ -26,24 +26,28 @@ this.partialTextIndex = {}; } + addEntryHandler(id: string, value: string): void { + if (this.fullTextIndex[value] === undefined) { + this.fullTextIndex[value] = new Set(); + } + this.fullTextIndex[value].add(id); + let partialString = ''; + for (let i = 0; i < value.length; i++) { + const char = value[i]; + partialString += char; + // TODO probably should do some stopwords here + if (this.partialTextIndex[partialString] === undefined) { + this.partialTextIndex[partialString] = new Set(); + } + this.partialTextIndex[partialString].add(id); + } + } + addEntry(id: string, rawText: string) { const keywords = this.tokenize(rawText); for (const keyword of keywords) { const value = keyword.value.toLowerCase(); - if (this.fullTextIndex[value] === undefined) { - this.fullTextIndex[value] = new Set(); - } - this.fullTextIndex[value].add(id); - let partialString = ''; - for (let i = 0; i < value.length; i++) { - const char = value[i]; - partialString += char; - // TODO probably should do some stopwords here - if (this.partialTextIndex[partialString] === undefined) { - this.partialTextIndex[partialString] = new Set(); - } - this.partialTextIndex[partialString].add(id); - } + this.addEntryHandler(id, value); } } diff --git a/lib/shared/sentence-prefix-search-index.js b/lib/shared/sentence-prefix-search-index.js new file mode 100644 --- /dev/null +++ b/lib/shared/sentence-prefix-search-index.js @@ -0,0 +1,46 @@ +// @flow + +import Tokenizer from 'tokenize-text'; + +import SearchIndex from './search-index.js'; + +class SentencePrefixSearchIndex extends SearchIndex { + constructor() { + super(); + this.tokenize = new Tokenizer().re(/\S+/); + } + + addEntry(id: string, rawText: string) { + const keywords = this.tokenize(rawText); + for (const keyword of keywords) { + const value = rawText.slice(keyword.index).toLowerCase(); + this.addEntryHandler(id, value); + } + } + + getSearchResults(query: string): string[] { + const possibleMatches = new Set(); + const transformedQuery = query.toLowerCase(); + + if (this.partialTextIndex[transformedQuery]) { + for (const id of this.partialTextIndex[transformedQuery]) { + possibleMatches.add(id); + } + } + + const slicedQuery = transformedQuery.slice(0, -1); + if ( + query.endsWith(' ') && + this.fullTextIndex[slicedQuery] && + !this.partialTextIndex[transformedQuery] + ) { + for (const id of this.fullTextIndex[slicedQuery]) { + possibleMatches.delete(id); + } + } + + return Array.from(possibleMatches); + } +} + +export default SentencePrefixSearchIndex;