@@ -781,7 +781,29 @@ function initSearch(rawSearchIndex) {
781
781
return a - b ;
782
782
}
783
783
784
- // Sort by non levenshtein results and then levenshtein results by the distance
784
+ // sort by index of keyword in item name (no literal occurrence goes later)
785
+ a = ( aaa . index < 0 ) ;
786
+ b = ( bbb . index < 0 ) ;
787
+ if ( a !== b ) {
788
+ return a - b ;
789
+ }
790
+
791
+ // Sort by distance in the path part, if specified
792
+ // (less changes required to match means higher rankings)
793
+ a = aaa . path_lev ;
794
+ b = bbb . path_lev ;
795
+ if ( a !== b ) {
796
+ return a - b ;
797
+ }
798
+
799
+ // (later literal occurrence, if any, goes later)
800
+ a = aaa . index ;
801
+ b = bbb . index ;
802
+ if ( a !== b ) {
803
+ return a - b ;
804
+ }
805
+
806
+ // Sort by distance in the name part, the last part of the path
785
807
// (less changes required to match means higher rankings)
786
808
a = ( aaa . lev ) ;
787
809
b = ( bbb . lev ) ;
@@ -810,19 +832,6 @@ function initSearch(rawSearchIndex) {
810
832
return ( a > b ? + 1 : - 1 ) ;
811
833
}
812
834
813
- // sort by index of keyword in item name (no literal occurrence goes later)
814
- a = ( aaa . index < 0 ) ;
815
- b = ( bbb . index < 0 ) ;
816
- if ( a !== b ) {
817
- return a - b ;
818
- }
819
- // (later literal occurrence, if any, goes later)
820
- a = aaa . index ;
821
- b = bbb . index ;
822
- if ( a !== b ) {
823
- return a - b ;
824
- }
825
-
826
835
// special precedence for primitive and keyword pages
827
836
if ( ( aaa . item . ty === TY_PRIMITIVE && bbb . item . ty !== TY_KEYWORD ) ||
828
837
( aaa . item . ty === TY_KEYWORD && bbb . item . ty !== TY_PRIMITIVE ) ) {
@@ -1230,15 +1239,19 @@ function initSearch(rawSearchIndex) {
1230
1239
* * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
1231
1240
* * `index` is an `integer`` used to sort by the position of the word in the item's name.
1232
1241
* * `lev` is the main metric used to sort the search results.
1242
+ * * `path_lev` is zero if a single-component search query is used, otherwise it's the
1243
+ * distance computed for everything other than the last path component.
1233
1244
*
1234
1245
* @param {Results } results
1235
1246
* @param {string } fullId
1236
1247
* @param {integer } id
1237
1248
* @param {integer } index
1238
1249
* @param {integer } lev
1250
+ * @param {integer } path_lev
1239
1251
*/
1240
- function addIntoResults ( results , fullId , id , index , lev ) {
1241
- if ( lev === 0 || ( ! parsedQuery . literalSearch && lev <= MAX_LEV_DISTANCE ) ) {
1252
+ function addIntoResults ( results , fullId , id , index , lev , path_lev ) {
1253
+ const inBounds = lev <= MAX_LEV_DISTANCE || index !== - 1 ;
1254
+ if ( lev === 0 || ( ! parsedQuery . literalSearch && inBounds ) ) {
1242
1255
if ( results [ fullId ] !== undefined ) {
1243
1256
const result = results [ fullId ] ;
1244
1257
if ( result . dontValidate || result . lev <= lev ) {
@@ -1250,6 +1263,7 @@ function initSearch(rawSearchIndex) {
1250
1263
index : index ,
1251
1264
dontValidate : parsedQuery . literalSearch ,
1252
1265
lev : lev ,
1266
+ path_lev : path_lev ,
1253
1267
} ;
1254
1268
}
1255
1269
}
@@ -1280,68 +1294,68 @@ function initSearch(rawSearchIndex) {
1280
1294
if ( ! row || ( filterCrates !== null && row . crate !== filterCrates ) ) {
1281
1295
return ;
1282
1296
}
1283
- let lev , lev_add = 0 , index = - 1 ;
1297
+ let lev , index = - 1 , path_lev = 0 ;
1284
1298
const fullId = row . id ;
1299
+ const searchWord = searchWords [ pos ] ;
1285
1300
1286
1301
const in_args = findArg ( row , elem , parsedQuery . typeFilter ) ;
1287
1302
const returned = checkReturned ( row , elem , parsedQuery . typeFilter ) ;
1288
1303
1289
- addIntoResults ( results_in_args , fullId , pos , index , in_args ) ;
1290
- addIntoResults ( results_returned , fullId , pos , index , returned ) ;
1304
+ // path_lev is 0 because no parent path information is currently stored
1305
+ // in the search index
1306
+ addIntoResults ( results_in_args , fullId , pos , - 1 , in_args , 0 ) ;
1307
+ addIntoResults ( results_returned , fullId , pos , - 1 , returned , 0 ) ;
1291
1308
1292
1309
if ( ! typePassesFilter ( parsedQuery . typeFilter , row . ty ) ) {
1293
1310
return ;
1294
1311
}
1295
- const searchWord = searchWords [ pos ] ;
1296
1312
1297
- if ( parsedQuery . literalSearch ) {
1298
- if ( searchWord === elem . name ) {
1299
- addIntoResults ( results_others , fullId , pos , - 1 , 0 ) ;
1300
- }
1301
- return ;
1313
+ const row_index = row . normalizedName . indexOf ( elem . pathLast ) ;
1314
+ const word_index = searchWord . indexOf ( elem . pathLast ) ;
1315
+
1316
+ // lower indexes are "better" matches
1317
+ // rank based on the "best" match
1318
+ if ( row_index === - 1 ) {
1319
+ index = word_index ;
1320
+ } else if ( word_index === - 1 ) {
1321
+ index = row_index ;
1322
+ } else if ( word_index < row_index ) {
1323
+ index = word_index ;
1324
+ } else {
1325
+ index = row_index ;
1302
1326
}
1303
1327
1304
1328
// No need to check anything else if it's a "pure" generics search.
1305
1329
if ( elem . name . length === 0 ) {
1306
1330
if ( row . type !== null ) {
1307
1331
lev = checkGenerics ( row . type , elem , MAX_LEV_DISTANCE + 1 ) ;
1308
- addIntoResults ( results_others , fullId , pos , index , lev ) ;
1332
+ // path_lev is 0 because we know it's empty
1333
+ addIntoResults ( results_others , fullId , pos , index , lev , 0 ) ;
1309
1334
}
1310
1335
return ;
1311
1336
}
1312
1337
1313
1338
if ( elem . fullPath . length > 1 ) {
1314
- lev = checkPath ( elem . pathWithoutLast , row ) ;
1315
- if ( lev > MAX_LEV_DISTANCE || ( parsedQuery . literalSearch && lev !== 0 ) ) {
1339
+ path_lev = checkPath ( elem . pathWithoutLast , row ) ;
1340
+ if ( path_lev > MAX_LEV_DISTANCE ) {
1316
1341
return ;
1317
- } else if ( lev > 0 ) {
1318
- lev_add = lev / 10 ;
1319
1342
}
1320
1343
}
1321
1344
1322
- if ( searchWord . indexOf ( elem . pathLast ) > - 1 ||
1323
- row . normalizedName . indexOf ( elem . pathLast ) > - 1
1324
- ) {
1325
- index = row . normalizedName . indexOf ( elem . pathLast ) ;
1326
- }
1327
- lev = levenshtein ( searchWord , elem . pathLast ) ;
1328
- if ( lev > 0 && elem . pathLast . length > 2 && searchWord . indexOf ( elem . pathLast ) > - 1 ) {
1329
- if ( elem . pathLast . length < 6 ) {
1330
- lev = 1 ;
1331
- } else {
1332
- lev = 0 ;
1345
+ if ( parsedQuery . literalSearch ) {
1346
+ if ( searchWord === elem . name ) {
1347
+ addIntoResults ( results_others , fullId , pos , index , 0 , path_lev ) ;
1333
1348
}
1334
- }
1335
- lev += lev_add ;
1336
- if ( lev > MAX_LEV_DISTANCE ) {
1337
1349
return ;
1338
- } else if ( index !== - 1 && elem . fullPath . length < 2 ) {
1339
- lev -= 1 ;
1340
1350
}
1341
- if ( lev < 0 ) {
1342
- lev = 0 ;
1351
+
1352
+ lev = levenshtein ( searchWord , elem . pathLast ) ;
1353
+
1354
+ if ( index === - 1 && lev + path_lev > MAX_LEV_DISTANCE ) {
1355
+ return ;
1343
1356
}
1344
- addIntoResults ( results_others , fullId , pos , index , lev ) ;
1357
+
1358
+ addIntoResults ( results_others , fullId , pos , index , lev , path_lev ) ;
1345
1359
}
1346
1360
1347
1361
/**
@@ -1386,7 +1400,7 @@ function initSearch(rawSearchIndex) {
1386
1400
return ;
1387
1401
}
1388
1402
const lev = Math . round ( totalLev / nbLev ) ;
1389
- addIntoResults ( results , row . id , pos , 0 , lev ) ;
1403
+ addIntoResults ( results , row . id , pos , 0 , lev , 0 ) ;
1390
1404
}
1391
1405
1392
1406
function innerRunQuery ( ) {
0 commit comments