Skip to content

Commit

Permalink
search: improve handling of multiterm search #407
Browse files Browse the repository at this point in the history
  • Loading branch information
McShelby committed Oct 27, 2024
1 parent 8debc83 commit 57b73a5
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 21 deletions.
2 changes: 1 addition & 1 deletion layouts/partials/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.1.1+fc960aa5eaf2240ba8a6de92943ac838423e1269
7.1.1+8debc83a770553885b78318e2651a127a82d2b0c
26 changes: 22 additions & 4 deletions static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { init, search } from './orama-adapter.js';

(function(){

function escapeRegex( string ){
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

window.relearn = window.relearn || {};

window.relearn.executeInitialSearch =
Expand Down Expand Up @@ -95,12 +99,19 @@ function executeSearch( value ) {
a.forEach( function(item){
var page = item.page;
var context = [];
if( item.matches ){
if( item.matches && item.matches.length ){
var numContextWords = 10;
var contextPattern = '(?:\\S+ +){0,' + numContextWords + '}\\S*\\b(?:' +
item.matches.map( function(match){return match.replace(/\W/g, '\\$&')} ).join('|') +
escapeRegex( item.matches[0] ) +
')\\b\\S*(?: +\\S+){0,' + numContextWords + '}';
context = page.content.match(new RegExp(contextPattern, 'i'));
if( !context ){
item.matches.shift();
var contextPattern = '(?:\\S+ +){0,' + numContextWords + '}\\S*\\b(?:' +
item.matches.map( escapeRegex ).join('|') +
')\\b\\S*(?: +\\S+){0,' + numContextWords + '}';
context = page.content.match(new RegExp(contextPattern, 'i'));
}
}
var divsuggestion = document.createElement('a');
divsuggestion.className = 'autocomplete-suggestion';
Expand Down Expand Up @@ -169,12 +180,19 @@ function initSearchAfterLoad(){
renderItem: function( item, term ) {
var page = item.page;
var context = [];
if( item.matches ){
if( item.matches && item.matches.length ){
var numContextWords = 2;
var contextPattern = '(?:\\S+ +){0,' + numContextWords + '}\\S*\\b(?:' +
item.matches.map( function(match){return match.replace(/\W/g, '\\$&')} ).join('|') +
escapeRegex( item.matches[0] ) +
')\\b\\S*(?: +\\S+){0,' + numContextWords + '}';
context = page.content.match(new RegExp(contextPattern, 'i'));
if( !context ){
item.matches.shift();
var contextPattern = '(?:\\S+ +){0,' + numContextWords + '}\\S*\\b(?:' +
item.matches.map( escapeRegex ).join('|') +
')\\b\\S*(?: +\\S+){0,' + numContextWords + '}';
context = page.content.match(new RegExp(contextPattern, 'i'));
}
}
var divsuggestion = document.createElement('div');
divsuggestion.className = 'autocomplete-suggestion';
Expand Down
59 changes: 43 additions & 16 deletions static/js/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -1327,10 +1327,12 @@ function scrollToPositions() {
return;
}

var search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
if( search && search.length ){
search = regexEscape( search );
var found = elementContains( search, elc );

var value = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
var words = (value ?? '') .split( ' ' ).filter( word => word.trim() != '' );
if( words && words.length ){
unmark();
var found = elementContains( words, elc );
var searchedElem = found.length && found[ 0 ];
if( searchedElem ){
searchedElem.scrollIntoView();
Expand All @@ -1339,6 +1341,8 @@ function scrollToPositions() {
window.scroll( 0, scrolledY - 125 );
}
}
sessionStorage.setItem( window.relearn.absBaseUri+'/search-value', value );
mark();
return;
}

Expand Down Expand Up @@ -1371,7 +1375,7 @@ function mark() {
bodyInnerLinks[i].classList.add( 'highlight' );
}

var value = sessionStorage.getItem( window.relearn.absBaseUri + '/search-value' );
var value = (sessionStorage.getItem( window.relearn.absBaseUri + '/search-value' ) ?? '').split( ' ' ).filter( word => word.trim() != '' );
var highlightableElements = document.querySelectorAll( '.highlightable' );
highlight( highlightableElements, value, { element: 'mark', className: 'search' } );

Expand Down Expand Up @@ -1411,17 +1415,10 @@ function highlight( es, words, options ){
};
Object.assign( settings, options );

if( !words ){ return; }
if( words.constructor === String ){
words = [ words ];
}
words = words.filter( function( word, i ){
return word != '';
});
if( !words.length ){ return; }
words = words.map( function( word, i ){
return regexEscape( word );
});
if( words.length == 0 ){ return this; }

var flag = settings.caseSensitive ? '' : 'i';
var pattern = "(" + words.join( '|' ) + ')';
Expand Down Expand Up @@ -1507,8 +1504,24 @@ function unhighlight( es, options ){
};

// replace jQuery.createPseudo with https://stackoverflow.com/a/66318392
function elementContains( txt, e ){
var regex = RegExp( txt, 'i' );
function elementContains( words, e ){
var settings = {
caseSensitive: false,
wordsOnly: false
};

if( !words.length ){ return; }
words = words.map( function( word, i ){
return regexEscape( word );
});

var flag = settings.caseSensitive ? '' : 'i';
var pattern = "(" + words.join( '\\s+' ) + ')';
if( settings.wordsOnly ){
pattern = '\\b' + pattern + '\\b';
}
var regex = new RegExp( pattern, flag );

var nodes = [];
if( e ){
var tree = document.createTreeWalker( e, 4 /* NodeFilter.SHOW_TEXT */, function( node ){
Expand All @@ -1518,6 +1531,20 @@ function elementContains( txt, e ){
while( node = tree.nextNode() ){
nodes.push( node.parentElement );
}

var pattern = "(" + words.join( '|' ) + ')';
if( settings.wordsOnly ){
pattern = '\\b' + pattern + '\\b';
}
regex = new RegExp( pattern, flag );

tree = document.createTreeWalker( e, 4 /* NodeFilter.SHOW_TEXT */, function( node ){
return regex.test( node.data );
}, false );
node = null;
while( node = tree.nextNode() ){
nodes.push( node.parentElement );
}
}
return nodes;
}
Expand All @@ -1537,7 +1564,7 @@ function initSearch() {
e.addEventListener( 'keydown', function( event ){
if( event.key == 'Escape' ){
var input = event.target;
var search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
var search = (sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' ) ?? '').split( ' ' ).filter( word => word.trim() != '' );
if( !search || !search.length ){
input.blur();
}
Expand Down

0 comments on commit 57b73a5

Please sign in to comment.