packages feed

hoogle-4.0: src/Hoogle/DataBase/NameSearch.hs

module Hoogle.DataBase.NameSearch
    (NameSearch, createNameSearch
    ,TextScore, searchNameSearch
    ) where

import Data.Binary.Defer
import Data.Binary.Defer.Trie
import Data.Binary.Defer.Chunk
import Data.Binary.Defer.Index
import Data.Char
import Data.List
import Data.Range
import General.Code
import Hoogle.Item.All
import Hoogle.TextBase.All


---------------------------------------------------------------------
-- DATA TYPES

data NameSearch = NameSearch (Trie NameItem) (Chunk (Int,Link Entry))
                  deriving Show

data NameItem = NameItem {nameStart :: Int
                         ,nameCountAll :: Int -- number that match exactly
                         ,nameCountAny :: Int -- number that match a prefix
                         }

instance Show NameItem where
    show (NameItem a b c) = unwords $ map show [a,b,c]


instance BinaryDefer NameSearch where
    put (NameSearch a b) = put2 a b
    get = get2 NameSearch

instance BinaryDefer NameItem where
    put (NameItem a b c) = put3 a b c
    get = get3 NameItem

{-
TRIE data structure

Given the functions "map" and "pm" we would generate:

Trie:
"ap"   (0,1,1)
"m"    (1,1,2)
"ma"   (2,0,1)  
"map"  (2,1,1)
"p"    (3,1,2)
"pm"   (4,1,1)

Chunk:
0 "ap"   (1,[map])
1 "m"    (1,[pm])
2 "map"  (0,[map])
3 "p"    (2,[map])
4 "pm"   (0,[pm])

There will be one trie entry per unique substring.
There will be one chunk entry per suffix.
Both are sorted by the string they represent.

[item] is the id of the item.
-}

---------------------------------------------------------------------
-- CREATION

createNameSearch :: [(a, Maybe (Link Entry))] -> NameSearch
createNameSearch xs = NameSearch
        (newTrie $ f sub (zip [0..] pre))
        (newChunk $ map snd pre)
    where
        ys = extractText xs
        sub = map head $ group $ sort $ concatMap (substrs . fst) ys
        pre = sortBy (compare `on` fst)
                  [(p,(i,e)) | (s,e) <- ys, (i,p) <- zip [0..] $ prefixes s]

        f :: [String] -> [(Int,(String,a))] -> [(String,NameItem)]
        f [] _ = []
        f (x:xs) ys = (x,NameItem s neq (neq+npr)) : f xs ys2
            where
                s = fst $ head ys
                (neq,npr) = (length eq, length pr)
                (eq,ys2) = span ((==) x . fst . snd) ys
                pr = takeWhile (isPrefixOf x . fst . snd) ys2


extractText :: [(a, Maybe (Link Entry))] -> [(String, Link Entry)]
extractText xs = [(map toLower s, e) |(_, Just e) <- xs, Focus s <- entryText $ fromLink e]


substrs, prefixes :: [a] -> [[a]]
substrs = concatMap (tail . inits) . prefixes
prefixes = init . tails


---------------------------------------------------------------------
-- SEARCHING

-- lower is better
data TextScore = TSExact | TSStart | TSNone
                 deriving (Eq,Ord)

instance Show TextScore where
    show TSExact = "exact"
    show TSStart = "start"
    show TSNone = "_"


searchNameSearch :: NameSearch -> String -> [(Link Entry,EntryView,TextScore)]
searchNameSearch (NameSearch trie chunk) str =
    case lookupTrie (map toLower str) trie of
        Nothing -> []
        Just i -> nubIntOn (linkKey . fst3) $ order exact0E ++ order (exact0S ++ start) ++ order none
            where
                (exact0,exactN) = partition ((==) 0 . fst) exact
                (partial0,partialN) = partition ((==) 0 . fst) partial
                (exact,partial) = splitAt (nameCountAll i) $
                    lookupChunk (rangeStartCount (nameStart i) (nameCountAny i)) chunk

                none = map (f $ const TSNone) $ exactN ++ partialN
                (exact0E,exact0S) = partition ((==) TSExact . thd3) $ map (f test) exact0
                start = map (f $ const TSStart) partial0
                test e = if entryName e == str then TSExact else TSStart
    where
        nstr = length str
        order = sortOn (linkKey . fst3)

        f :: (Entry -> TextScore) -> (Int, Link Entry) -> (Link Entry,EntryView,TextScore)
        f score (p,e) = (e, FocusOn (rangeStartCount p nstr), score $ fromLink e)