Tree.hs 3.63 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
{-|
Module      : Gargantext.Database.Tree
Description : Tree of Resource Nodes built from Database
Copyright   : (c) CNRS, 2017-Present
License     : AGPL + CECILL v3
Maintainer  : team@gargantext.org
Stability   : experimental
Portability : POSIX

Let a Root Node, return the Tree of the Node as a directed acyclic graph
(Tree).

-}

{-# LANGUAGE NoImplicitPrelude #-}
16 17
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE RankNTypes        #-}
18

19
module Gargantext.Database.Tree (treeDB, TreeError(..), HasTreeError(..), dbTree, toNodeTree, DbTreeNode) where
20

21 22
import Control.Lens (Prism', (#), (^..), at, each, _Just, to)
import Control.Monad.Error.Class (MonadError(throwError))
23
import Data.Map (Map, fromListWith, lookup)
24
import Data.Text (Text)
25 26 27 28 29
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.SqlQQ

import Gargantext.Prelude
import Gargantext.Core.Types.Main (NodeTree(..), Tree(..))
30
import Gargantext.Database.Types.Node (NodeId)
31
import Gargantext.Database.Config (fromNodeTypeId)
32
import Gargantext.Database.Utils (Cmd, runPGSQuery)
33
------------------------------------------------------------------------
34
-- import Gargantext.Database.Utils (runCmdDev)
35
-- treeTest :: IO (Tree NodeTree)
36
-- treeTest = runCmdDev $ treeDB 347474
37
------------------------------------------------------------------------
38 39 40 41 42 43 44 45 46 47

data TreeError = NoRoot | EmptyRoot | TooManyRoots
  deriving (Show)

class HasTreeError e where
  _TreeError :: Prism' e TreeError

treeError :: (MonadError e m, HasTreeError e) => TreeError -> m a
treeError te = throwError $ _TreeError # te

48
-- | Returns the Tree of Nodes in Database
49 50
treeDB :: HasTreeError err => RootId -> Cmd err (Tree NodeTree)
treeDB r = toTree =<< (toTreeParent <$> dbTree r)
51

52 53
type RootId = NodeId
type ParentId = NodeId
54
------------------------------------------------------------------------
55 56 57 58 59 60 61 62
toTree :: (MonadError e m, HasTreeError e)
       => Map (Maybe ParentId) [DbTreeNode] -> m (Tree NodeTree)
toTree m =
    case lookup Nothing m of
        Just [n] -> pure $ toTree' m n
        Nothing  -> treeError NoRoot
        Just []  -> treeError EmptyRoot
        Just _   -> treeError TooManyRoots
63 64

toTree' :: Map (Maybe ParentId) [DbTreeNode] -> DbTreeNode -> Tree NodeTree
65 66 67 68
toTree' m n =
  TreeN (toNodeTree n) $
    m ^.. at (Just $ dt_nodeId n) . _Just . each . to (toTree' m)

69 70 71 72
------------------------------------------------------------------------
toNodeTree :: DbTreeNode -> NodeTree
toNodeTree (DbTreeNode nId tId _ n) = NodeTree n nodeType nId
  where
73
    nodeType = fromNodeTypeId tId
74 75 76 77
------------------------------------------------------------------------
toTreeParent :: [DbTreeNode] -> Map (Maybe ParentId) [DbTreeNode]
toTreeParent = fromListWith (<>) . map (\n -> (dt_parentId n, [n]))
------------------------------------------------------------------------
78
data DbTreeNode = DbTreeNode { dt_nodeId :: NodeId
79
                             , dt_typeId :: Int
80
                             , dt_parentId :: Maybe NodeId
81 82 83
                             , dt_name     :: Text
                             } deriving (Show)

84 85
-- | Main DB Tree function
-- TODO add typenames as parameters
86 87
dbTree :: RootId -> Cmd err [DbTreeNode]
dbTree rootId = map (\(nId, tId, pId, n) -> DbTreeNode nId tId pId n) <$> runPGSQuery [sql|
88
  WITH RECURSIVE
89
      tree (id, typename, parent_id, name) AS
90
      (
91 92 93 94 95 96 97 98 99 100
        SELECT p.id, p.typename, p.parent_id, p.name
        FROM nodes AS p
        WHERE p.id = ?
        
        UNION
        
        SELECT c.id, c.typename, c.parent_id, c.name
        FROM nodes AS c
        
        INNER JOIN tree AS s ON c.parent_id = s.id
101
        WHERE c.typename IN (2,3,30,31,7,9)
102
      )
103
  SELECT * from tree;
104 105
  |] (Only rootId)

106 107 108 109