@@ -28,6 +28,11 @@ const searchServiceCache: Record<string, SearchService<Quote>> = {};
2828const pageSize = 100 ;
2929let currentPageNumber = 1 ;
3030let usingCustomLength = true ;
31+ let quotes : Quote [ ] ;
32+
33+ async function updateQuotes ( ) : Promise < void > {
34+ ( { quotes } = await QuotesController . getQuotes ( Config . language ) ) ;
35+ }
3136
3237function getSearchService < T > (
3338 language : string ,
@@ -188,10 +193,61 @@ function buildQuoteSearchResult(
188193 ` ;
189194}
190195
196+ function exactSearch ( quotes : Quote [ ] , captured : RegExp [ ] ) : [ Quote [ ] , string [ ] ] {
197+ const matches : Quote [ ] = [ ] ;
198+ const exactSearchQueryTerms : Set < string > = new Set < string > ( ) ;
199+
200+ for ( const quote of quotes ) {
201+ const textAndSource = quote . text + quote . source ;
202+ const currentMatches = [ ] ;
203+ let noMatch = false ;
204+
205+ for ( const regex of captured ) {
206+ const match = textAndSource . match ( regex ) ;
207+
208+ if ( ! match ) {
209+ noMatch = true ;
210+ break ;
211+ }
212+
213+ currentMatches . push ( match [ 0 ] ) ;
214+ }
215+
216+ if ( ! noMatch ) {
217+ currentMatches . forEach ( ( match ) => exactSearchQueryTerms . add ( match ) ) ;
218+ matches . push ( quote ) ;
219+ }
220+ }
221+
222+ return [ matches , Array . from ( exactSearchQueryTerms ) ] ;
223+ }
224+
191225async function updateResults ( searchText : string ) : Promise < void > {
192226 if ( ! modal . isOpen ( ) ) return ;
193227
194- const { quotes } = await QuotesController . getQuotes ( Config . language ) ;
228+ if ( quotes === undefined ) {
229+ ( { quotes } = await QuotesController . getQuotes ( Config . language ) ) ;
230+ }
231+
232+ let matches : Quote [ ] = [ ] ;
233+ let matchedQueryTerms : string [ ] = [ ] ;
234+ let exactSearchMatches : Quote [ ] = [ ] ;
235+ let exactSearchMatchedQueryTerms : string [ ] = [ ] ;
236+
237+ const quotationsRegex = / " ( .* ?) " / g;
238+ const exactSearchQueries = Array . from ( searchText . matchAll ( quotationsRegex ) ) ;
239+ const removedSearchText = searchText . replaceAll ( quotationsRegex , "" ) ;
240+
241+ if ( exactSearchQueries [ 0 ] ) {
242+ const searchQueriesRaw = exactSearchQueries . map (
243+ ( query ) => new RegExp ( query [ 1 ] ?? "" , "i" ) ,
244+ ) ;
245+
246+ [ exactSearchMatches , exactSearchMatchedQueryTerms ] = exactSearch (
247+ quotes ,
248+ searchQueriesRaw ,
249+ ) ;
250+ }
195251
196252 const quoteSearchService = getSearchService < Quote > (
197253 Config . language ,
@@ -200,8 +256,21 @@ async function updateResults(searchText: string): Promise<void> {
200256 return `${ quote . text } ${ quote . id } ${ quote . source } ` ;
201257 } ,
202258 ) ;
203- const { results : matches , matchedQueryTerms } =
204- quoteSearchService . query ( searchText ) ;
259+
260+ if ( exactSearchMatches . length > 0 || removedSearchText === searchText ) {
261+ const ids = exactSearchMatches . map ( ( match ) => match . id ) ;
262+
263+ ( { results : matches , matchedQueryTerms } = quoteSearchService . query (
264+ removedSearchText ,
265+ ids ,
266+ ) ) ;
267+
268+ exactSearchMatches . forEach ( ( match ) => {
269+ if ( ! matches . includes ( match ) ) matches . push ( match ) ;
270+ } ) ;
271+
272+ matchedQueryTerms = [ ...exactSearchMatchedQueryTerms , ...matchedQueryTerms ] ;
273+ }
205274
206275 const quotesToShow = applyQuoteLengthFilter (
207276 applyQuoteFavFilter ( searchText === "" ? quotes : matches ) ,
@@ -340,12 +409,7 @@ export async function show(showOptions?: ShowOptions): Promise<void> {
340409 } ) ;
341410 } ,
342411 afterAnimation : async ( ) => {
343- const quoteSearchInputValue = $ (
344- "#quoteSearchModal input" ,
345- ) . val ( ) as string ;
346- currentPageNumber = 1 ;
347-
348- void updateResults ( quoteSearchInputValue ) ;
412+ void updateQuotes ( ) ;
349413 } ,
350414 } ) ;
351415}
0 commit comments