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 FlexibleContexts #-}
16 {-# LANGUAGE NoImplicitPrelude #-}
17 {-# LANGUAGE OverloadedStrings #-}
18 {-# LANGUAGE QuasiQuotes #-}
19 {-# LANGUAGE RankNTypes #-}
21 module Gargantext.Database.Query.Tree
22 ( module Gargantext.Database.Query.Tree.Error
29 import Control.Lens ((^..), at, each, _Just, to)
30 import Control.Monad.Error.Class (MonadError())
31 import Data.Map (Map, fromListWith, lookup)
32 import Data.Text (Text)
33 import Database.PostgreSQL.Simple
34 import Database.PostgreSQL.Simple.SqlQQ
35 import Gargantext.Core.Types.Main (NodeTree(..), Tree(..))
36 import Gargantext.Database.Admin.Types.Node -- (pgNodeId, NodeType(..))
37 import Gargantext.Database.Admin.Config (fromNodeTypeId, nodeTypeId)
38 import Gargantext.Database.Admin.Types.Node (NodeId, NodeType, DocId, allNodeTypes)
39 import Gargantext.Database.Prelude (Cmd, runPGSQuery)
40 import Gargantext.Database.Query.Tree.Error
41 import Gargantext.Prelude
43 ------------------------------------------------------------------------
44 -- TODO more generic find fun
45 _findCorpus :: RootId -> Cmd err (Maybe CorpusId)
47 _mapNodes <- toTreeParent <$> dbTree r []
50 -- | Returns the Tree of Nodes in Database
51 treeDB :: HasTreeError err
54 -> Cmd err (Tree NodeTree)
55 treeDB r nodeTypes = toTree =<< (toTreeParent <$> dbTree r nodeTypes)
57 ------------------------------------------------------------------------
58 toTree :: ( MonadError e m
60 => Map (Maybe ParentId) [DbTreeNode]
63 case lookup Nothing m of
64 Just [n] -> pure $ toTree' m n
65 Nothing -> treeError NoRoot
66 Just [] -> treeError EmptyRoot
67 Just _ -> treeError TooManyRoots
69 toTree' :: Map (Maybe ParentId) [DbTreeNode]
73 TreeN (toNodeTree n) $
74 m ^.. at (Just $ dt_nodeId n) . _Just . each . to (toTree' m)
76 ------------------------------------------------------------------------
77 toNodeTree :: DbTreeNode
79 toNodeTree (DbTreeNode nId tId _ n) = NodeTree n nodeType nId
81 nodeType = fromNodeTypeId tId
82 ------------------------------------------------------------------------
83 toTreeParent :: [DbTreeNode]
84 -> Map (Maybe ParentId) [DbTreeNode]
85 toTreeParent = fromListWith (<>) . map (\n -> (dt_parentId n, [n]))
86 ------------------------------------------------------------------------
87 data DbTreeNode = DbTreeNode { dt_nodeId :: NodeId
89 , dt_parentId :: Maybe NodeId
93 -- | Main DB Tree function
94 -- TODO add typenames as parameters
97 -> Cmd err [DbTreeNode]
98 dbTree rootId nodeTypes = map (\(nId, tId, pId, n) -> DbTreeNode nId tId pId n)
101 tree (id, typename, parent_id, name) AS
103 SELECT p.id, p.typename, p.parent_id, p.name
109 SELECT c.id, c.typename, c.parent_id, c.name
112 INNER JOIN tree AS s ON c.parent_id = s.id
113 WHERE c.typename IN ?
116 |] (rootId, In typename)
118 typename = map nodeTypeId ns
119 ns = case nodeTypes of
121 -- [2, 20, 21, 22, 3, 5, 30, 31, 40, 7, 9, 90, 71]
124 isDescendantOf :: NodeId -> RootId -> Cmd err Bool
125 isDescendantOf childId rootId = (== [Only True])
126 <$> runPGSQuery [sql|
128 SET TRANSACTION READ ONLY;
132 tree (id, parent_id) AS
134 SELECT c.id, c.parent_id
140 SELECT p.id, p.parent_id
142 INNER JOIN tree AS t ON t.parent_id = p.id
145 SELECT COUNT(*) = 1 from tree AS t
149 -- TODO should we check the category?
150 isIn :: NodeId -> DocId -> Cmd err Bool
151 isIn cId docId = ( == [Only True])
152 <$> runPGSQuery [sql| SELECT COUNT(*) = 1
154 WHERE nn.node1_id = ?