25
25
26
26
import Control.Lens hiding (from )
27
27
import Data.Foldable qualified as Foldable
28
- import Data.List.NonEmpty qualified as NEL
29
28
import Data.Text qualified as Text
30
29
import Share.Postgres
31
30
import Share.Postgres qualified as PG
@@ -307,13 +306,35 @@ listNameLookupMounts !_nameLookupReceipt rootBranchHashId =
307
306
in (mountPath, mountedRootBranchHashId)
308
307
309
308
-- | Larger is better.
310
- type FuzzySearchScore = (Bool , Bool , Int64 , Int64 )
309
+ data FuzzySearchScore
310
+ = FuzzySearchScore
311
+ { exactLastSegmentMatch :: Bool ,
312
+ lastSegmentInfixMatch :: Bool ,
313
+ lastSegmentMatchPos :: Int64 ,
314
+ inverseNameLength :: Int64
315
+ }
316
+ deriving (Show , Eq )
317
+
318
+ instance Ord FuzzySearchScore where
319
+ compare (FuzzySearchScore exact1 infix1 pos1 len1) (FuzzySearchScore exact2 infix2 pos2 len2) =
320
+ exact1 `compare` exact2
321
+ <> infix1 `compare` infix2
322
+ <> pos1 `compare` pos2
323
+ <> len1 `compare` len2
324
+
325
+ instance DecodeRow FuzzySearchScore where
326
+ decodeRow =
327
+ FuzzySearchScore
328
+ <$> PG. decodeField
329
+ <*> PG. decodeField
330
+ <*> PG. decodeField
331
+ <*> PG. decodeField
311
332
312
333
-- | Searches for all names within the given name lookup which contain the provided list of segments
313
334
-- in order.
314
335
-- Search is case insensitive.
315
- fuzzySearchTerms :: (PG. QueryM m ) => NameLookupReceipt -> Bool -> BranchHashId -> Int64 -> PathSegments -> NonEmpty Text -> m [(FuzzySearchScore , NamedRef (PGReferent , Maybe ConstructorType ))]
316
- fuzzySearchTerms ! _nameLookupReceipt includeDependencies bhId limit namespace querySegments = do
336
+ fuzzySearchTerms :: (PG. QueryM m ) => NameLookupReceipt -> Bool -> BranchHashId -> Int64 -> PathSegments -> NonEmpty Text -> Text -> m [(FuzzySearchScore , NamedRef (PGReferent , Maybe ConstructorType ))]
337
+ fuzzySearchTerms ! _nameLookupReceipt includeDependencies bhId limit namespace querySegments lastSearchTerm = do
317
338
fmap unRow
318
339
<$> PG. queryListRows
319
340
[PG. sql |
@@ -322,7 +343,7 @@ fuzzySearchTerms !_nameLookupReceipt includeDependencies bhId limit namespace qu
322
343
FROM (
323
344
SELECT reversed_name, referent_builtin, referent_component_hash_id, referent_component_index, referent_constructor_index, referent_constructor_type, last_name_segment,
324
345
(last_name_segment = #{lastSearchTerm}) AS exact_last_segment_match,
325
- (#{lastSearchTerm} ILIKE like_escape ('%' || last_name_segment ) || '%') AS last_segment_infix_match,
346
+ (last_name_segment ILIKE ('%' || like_escape(#{lastSearchTerm} ) || '%') ) AS last_segment_infix_match,
326
347
(-POSITION(#{lastSearchTerm} IN last_name_segment)) AS last_segment_match_pos,
327
348
(-length(reversed_name)) AS inverse_name_length
328
349
FROM scoped_term_name_lookup
@@ -335,22 +356,21 @@ fuzzySearchTerms !_nameLookupReceipt includeDependencies bhId limit namespace qu
335
356
-- Exact last-segment matches first, then last-segment infix matches sorting prefix
336
357
-- matches first, then prefer shorter names.
337
358
ORDER BY (last_name_segment = #{lastSearchTerm},
338
- (#{lastSearchTerm} ILIKE like_escape ('%' || last_name_segment) || '%'),
359
+ (#{lastSearchTerm} ILIKE ('%' || like_escape( last_name_segment) || '%') ),
339
360
(-POSITION(#{lastSearchTerm} IN last_name_segment)),
340
361
(-length(reversed_name))
341
362
) DESC
342
363
LIMIT #{limit}
343
364
|]
344
365
where
345
- lastSearchTerm = NEL. last querySegments
346
366
namespacePrefix = toNamespacePrefix namespace
347
367
-- Union in the dependencies if required.
348
368
dependenciesSql =
349
369
[PG. sql |
350
370
UNION ALL
351
371
SELECT (names.reversed_name || mount.reversed_mount_path) AS reversed_name, referent_builtin, referent_component_hash_id, referent_component_index, referent_constructor_index, referent_constructor_type, last_name_segment,
352
372
(last_name_segment = #{lastSearchTerm}) AS exact_last_segment_match,
353
- (#{lastSearchTerm} ILIKE like_escape ('%' || last_name_segment ) || '%') AS last_segment_infix_match,
373
+ (last_name_segment ILIKE ('%' || like_escape(#{lastSearchTerm} ) || '%') ) AS last_segment_infix_match,
354
374
(-POSITION(#{lastSearchTerm} IN last_name_segment)) AS last_segment_match_pos,
355
375
(-length(reversed_name)) AS inverse_name_length
356
376
FROM name_lookup_mounts mount
@@ -372,8 +392,8 @@ fuzzySearchTerms !_nameLookupReceipt includeDependencies bhId limit namespace qu
372
392
-- in order.
373
393
--
374
394
-- Search is case insensitive.
375
- fuzzySearchTypes :: (PG. QueryM m ) => NameLookupReceipt -> Bool -> BranchHashId -> Int64 -> PathSegments -> NonEmpty Text -> m [(FuzzySearchScore , NamedRef PGReference )]
376
- fuzzySearchTypes ! _nameLookupReceipt includeDependencies bhId limit namespace querySegments = do
395
+ fuzzySearchTypes :: (PG. QueryM m ) => NameLookupReceipt -> Bool -> BranchHashId -> Int64 -> PathSegments -> NonEmpty Text -> Text -> m [(FuzzySearchScore , NamedRef PGReference )]
396
+ fuzzySearchTypes ! _nameLookupReceipt includeDependencies bhId limit namespace querySegments lastSearchTerm = do
377
397
fmap unRow
378
398
<$> PG. queryListRows
379
399
[PG. sql |
@@ -383,7 +403,7 @@ fuzzySearchTypes !_nameLookupReceipt includeDependencies bhId limit namespace qu
383
403
FROM (
384
404
SELECT reversed_name, reference_builtin, reference_component_hash_id, reference_component_index, last_name_segment,
385
405
(last_name_segment = #{lastSearchTerm}) AS exact_last_segment_match,
386
- (#{lastSearchTerm} ILIKE like_escape ('%' || last_name_segment ) || '%') AS last_segment_infix_match,
406
+ (last_name_segment ILIKE ('%' || like_escape(#{lastSearchTerm} ) || '%') ) AS last_segment_infix_match,
387
407
(-POSITION(#{lastSearchTerm} IN last_name_segment)) AS last_segment_match_pos,
388
408
(-length(reversed_name)) AS inverse_name_length
389
409
FROM scoped_type_name_lookup
@@ -396,14 +416,13 @@ fuzzySearchTypes !_nameLookupReceipt includeDependencies bhId limit namespace qu
396
416
-- Exact last-segment matches first, then last-segment prefix matches, then prefer
397
417
-- shorter names.
398
418
ORDER BY (last_name_segment = #{lastSearchTerm},
399
- (#{lastSearchTerm} ILIKE like_escape ('%' || last_name_segment) || '%'),
419
+ (#{lastSearchTerm} ILIKE ('%' || like_escape( last_name_segment) || '%') ),
400
420
(-POSITION(#{lastSearchTerm} IN last_name_segment)),
401
421
(-length(reversed_name))
402
422
) DESC
403
423
LIMIT #{limit}
404
424
|]
405
425
where
406
- lastSearchTerm = NEL. last querySegments
407
426
unRow :: (NamedRef PGReference PG. :. FuzzySearchScore ) -> (FuzzySearchScore , NamedRef PGReference )
408
427
unRow (namedRef PG. :. score) = (score, namedRef)
409
428
namespacePrefix = toNamespacePrefix namespace
@@ -413,7 +432,7 @@ fuzzySearchTypes !_nameLookupReceipt includeDependencies bhId limit namespace qu
413
432
UNION ALL
414
433
SELECT (names.reversed_name || mount.reversed_mount_path) AS reversed_name, reference_builtin, reference_component_hash_id, reference_component_index, last_name_segment,
415
434
(last_name_segment = #{lastSearchTerm}) AS exact_last_segment_match,
416
- (#{lastSearchTerm} ILIKE like_escape('%' || last_name_segment ) || '%') AS last_segment_infix_match,
435
+ (last_name_segment ILIKE ('%' || like_escape('%' || #{lastSearchTerm} ) || '%') ) AS last_segment_infix_match,
417
436
(-POSITION(#{lastSearchTerm} IN last_name_segment)) AS last_segment_match_pos,
418
437
(-length(reversed_name)) AS inverse_name_length
419
438
FROM name_lookup_mounts mount
0 commit comments