2 Module : Gargantext.Database.Tree
3 Description : Tree of Resource Nodes built from Database
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 Let a Root Node, return the Tree of the Node as a directed acyclic graph
15 {-# LANGUAGE QuasiQuotes #-}
17 module Gargantext.Database.Query.Tree
18 ( module Gargantext.Database.Query.Tree.Error
25 import Control.Lens ((^..), at, each, _Just, to)
26 import Control.Monad.Error.Class (MonadError())
27 import Data.Map (Map, fromListWith, lookup)
28 import Data.Text (Text)
29 import Database.PostgreSQL.Simple
30 import Database.PostgreSQL.Simple.SqlQQ
31 import Gargantext.Core.Types.Main (NodeTree(..), Tree(..))
32 import Gargantext.Database.Admin.Types.Node -- (pgNodeId, NodeType(..))
33 import Gargantext.Database.Admin.Config (fromNodeTypeId, nodeTypeId)
34 import Gargantext.Database.Admin.Types.Node (NodeId, NodeType, DocId, allNodeTypes)
35 import Gargantext.Database.Prelude (Cmd, runPGSQuery)
36 import Gargantext.Database.Query.Tree.Error
37 import Gargantext.Prelude
39 ------------------------------------------------------------------------
40 -- TODO more generic find fun
41 _findCorpus :: RootId -> Cmd err (Maybe CorpusId)
43 _mapNodes <- toTreeParent <$> dbTree r []
46 -- | Returns the Tree of Nodes in Database
47 treeDB :: HasTreeError err
50 -> Cmd err (Tree NodeTree)
51 treeDB r nodeTypes = toTree =<< (toTreeParent <$> dbTree r nodeTypes)
53 ------------------------------------------------------------------------
54 toTree :: ( MonadError e m
56 => Map (Maybe ParentId) [DbTreeNode]
59 case lookup Nothing m of
60 Just [n] -> pure $ toTree' m n
61 Nothing -> treeError NoRoot
62 Just [] -> treeError EmptyRoot
63 Just _ -> treeError TooManyRoots
65 toTree' :: Map (Maybe ParentId) [DbTreeNode]
69 TreeN (toNodeTree n) $
70 m ^.. at (Just $ dt_nodeId n) . _Just . each . to (toTree' m)
72 ------------------------------------------------------------------------
73 toNodeTree :: DbTreeNode
75 toNodeTree (DbTreeNode nId tId _ n) = NodeTree n nodeType nId
77 nodeType = fromNodeTypeId tId
78 ------------------------------------------------------------------------
79 toTreeParent :: [DbTreeNode]
80 -> Map (Maybe ParentId) [DbTreeNode]
81 toTreeParent = fromListWith (<>) . map (\n -> (dt_parentId n, [n]))
82 ------------------------------------------------------------------------
83 data DbTreeNode = DbTreeNode { dt_nodeId :: NodeId
85 , dt_parentId :: Maybe NodeId
89 -- | Main DB Tree function
90 -- TODO add typenames as parameters
93 -> Cmd err [DbTreeNode]
94 dbTree rootId nodeTypes = map (\(nId, tId, pId, n) -> DbTreeNode nId tId pId n)
97 tree (id, typename, parent_id, name) AS
99 SELECT p.id, p.typename, p.parent_id, p.name
105 SELECT c.id, c.typename, c.parent_id, c.name
108 INNER JOIN tree AS s ON c.parent_id = s.id
109 WHERE c.typename IN ?
112 |] (rootId, In typename)
114 typename = map nodeTypeId ns
115 ns = case nodeTypes of
117 -- [2, 20, 21, 22, 3, 5, 30, 31, 40, 7, 9, 90, 71]
120 isDescendantOf :: NodeId -> RootId -> Cmd err Bool
121 isDescendantOf childId rootId = (== [Only True])
122 <$> runPGSQuery [sql|
124 SET TRANSACTION READ ONLY;
128 tree (id, parent_id) AS
130 SELECT c.id, c.parent_id
136 SELECT p.id, p.parent_id
138 INNER JOIN tree AS t ON t.parent_id = p.id
141 SELECT COUNT(*) = 1 from tree AS t
145 -- TODO should we check the category?
146 isIn :: NodeId -> DocId -> Cmd err Bool
147 isIn cId docId = ( == [Only True])
148 <$> runPGSQuery [sql| SELECT COUNT(*) = 1
150 WHERE nn.node1_id = ?