search: improve handling of multiterm search #407

This commit is contained in:
Sören Weber 2024-10-27 12:37:05 +01:00
parent 8debc83a77
commit 57b73a5f47
No known key found for this signature in database
GPG key ID: BEC6D55545451B6D
3 changed files with 66 additions and 21 deletions

View file

@ -1 +1 @@
7.1.1+fc960aa5eaf2240ba8a6de92943ac838423e1269
7.1.1+8debc83a770553885b78318e2651a127a82d2b0c

View file

@ -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 =
@ -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';
@ -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';

View file

@ -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();
@ -1339,6 +1341,8 @@ function scrollToPositions() {
window.scroll( 0, scrolledY - 125 );
}
}
sessionStorage.setItem( window.relearn.absBaseUri+'/search-value', value );
mark();
return;
}
@ -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' } );
@ -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( '|' ) + ')';
@ -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 ){
@ -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;
}
@ -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();
}