@@ -44,73 +44,71 @@ export function init(blocks: Block[]) {
4444 inited = true ;
4545}
4646
47+ const CURRENT_SECTION_BOOST = 2 ;
48+ const EXACT_MATCH_BOOST = 10 ;
49+ const WORD_MATCH_BOOST = 4 ;
50+ const NEAR_MATCH_BOOST = 2 ;
51+ const BREADCRUMB_LENGTH_BOOST = 0.2 ;
52+
4753/**
4854 * Search for a given query in the existing index
4955 */
5056export function search ( query : string , path : string ) : BlockGroup [ ] {
5157 const escaped = query . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, '\\$&' ) ;
52- const regex = new RegExp ( `(^|\\b)${ escaped } ` , 'i' ) ;
58+ const exact_match = new RegExp ( `^${ escaped } $` , 'i' ) ;
59+ const word_match = new RegExp ( `(^|\\b)${ escaped } ($|\\b)` , 'i' ) ;
60+ const near_match = new RegExp ( `(^|\\b)${ escaped } ` , 'i' ) ;
5361
5462 const parts = path . split ( '/' ) ;
5563
5664 const blocks = indexes
5765 . flatMap ( ( index ) => index . search ( query ) )
5866 // @ts -expect-error flexsearch types are wrong i think?
5967 . map ( lookup )
60- . map ( ( block , rank ) => ( { block : block as Block , rank } ) )
61- . sort ( ( a , b ) => {
68+ . map ( ( block ) => {
69+ const block_parts = block . href . split ( '/' ) ;
70+
6271 // prioritise current section
63- const a_parts = a . block . href . split ( '/' ) ;
64- const b_parts = b . block . href . split ( '/' ) ;
65-
66- for ( let i = 0 ; i < parts . length ; i += 1 ) {
67- const a_part_matches = a_parts [ i ] === parts [ i ] ;
68- const b_part_matches = b_parts [ i ] === parts [ i ] ;
69-
70- if ( ! a_part_matches || ! b_part_matches ) {
71- if ( a_part_matches !== b_part_matches ) {
72- if ( i > 1 ) {
73- console . log ( 'here' , a , b ) ;
74- }
75- return a_part_matches ? - 1 : 1 ;
76- }
77-
78- break ;
79- }
72+ let score = block_parts . findIndex ( ( part , i ) => part !== parts [ i ] ) ;
73+ if ( score === - 1 ) score = block_parts . length ;
74+ score *= CURRENT_SECTION_BOOST ;
75+
76+ if ( block . breadcrumbs . some ( ( text ) => exact_match . test ( text ) ) ) {
77+ console . log ( 'EXACT MATCH' , block . breadcrumbs ) ;
78+ score += EXACT_MATCH_BOOST ;
79+ } else if ( block . breadcrumbs . some ( ( text ) => word_match . test ( text ) ) ) {
80+ score += WORD_MATCH_BOOST ;
81+ } else if ( block . breadcrumbs . some ( ( text ) => near_match . test ( text ) ) ) {
82+ score += NEAR_MATCH_BOOST ;
8083 }
8184
82- const a_title_matches = regex . test ( a . block . breadcrumbs . at ( - 1 ) ! ) ;
83- const b_title_matches = regex . test ( b . block . breadcrumbs . at ( - 1 ) ! ) ;
84-
85- // massage the order a bit, so that title matches
86- // are given higher priority
87- if ( a_title_matches !== b_title_matches ) {
88- return a_title_matches ? - 1 : 1 ;
89- }
85+ // prioritise branches over leaves
86+ score -= block . breadcrumbs . length * BREADCRUMB_LENGTH_BOOST ;
9087
91- return a . block . breadcrumbs . length - b . block . breadcrumbs . length || a . rank - b . rank ;
92- } )
93- . map ( ( { block } ) => block ) ;
88+ return { block, score } ;
89+ } ) ;
9490
9591 const groups : Record < string , BlockGroup > = { } ;
9692
97- for ( const block of blocks ) {
93+ for ( const { score , block } of blocks ) {
9894 const breadcrumbs = block . breadcrumbs . slice ( 0 , 2 ) ;
9995
10096 const group = ( groups [ breadcrumbs . join ( '::' ) ] ??= {
10197 breadcrumbs,
102- blocks : [ ]
98+ blocks : [ ] ,
99+ score : 0
103100 } ) ;
104101
102+ group . score = Math . max ( score , group . score ) ;
105103 group . blocks . push ( block ) ;
106104 }
107105
108- return Object . values ( groups ) ;
106+ return Object . values ( groups ) . sort ( ( a , b ) => b . score - a . score ) ;
109107}
110108
111109/**
112110 * Get a block with details by its href
113111 */
114112export function lookup ( href : string ) {
115- return map . get ( href ) ;
113+ return map . get ( href ) ! ;
116114}
0 commit comments