2 Module : Gargantext.Database.Schema.NodeNode
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 Here is a longer description of this module, containing some
11 commentary with @some markup@.
14 {-# OPTIONS_GHC -fno-warn-orphans #-}
16 {-# LANGUAGE Arrows #-}
17 {-# LANGUAGE FlexibleInstances #-}
18 {-# LANGUAGE FunctionalDependencies #-}
19 {-# LANGUAGE QuasiQuotes #-}
20 {-# LANGUAGE MultiParamTypeClasses #-}
21 {-# LANGUAGE NoImplicitPrelude #-}
22 {-# LANGUAGE OverloadedStrings #-}
23 {-# LANGUAGE RankNTypes #-}
24 {-# LANGUAGE TemplateHaskell #-}
26 module Gargantext.Database.Schema.NodeNode where
28 import Control.Lens (view, (^.))
29 import qualified Database.PostgreSQL.Simple as PGS (Query, Only(..))
30 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
31 import Database.PostgreSQL.Simple.SqlQQ (sql)
32 import Control.Lens.TH (makeLenses)
33 import Data.Maybe (Maybe, catMaybes)
34 import Data.Text (Text, splitOn)
35 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
36 import Gargantext.Database.Schema.Node
37 import Gargantext.Core.Types
38 import Gargantext.Database.Utils
39 import Gargantext.Database.Config (nodeTypeId)
40 import Gargantext.Database.Types.Node (CorpusId, DocId)
41 import Gargantext.Prelude
43 import Control.Arrow (returnA)
44 import qualified Opaleye as O
46 data NodeNodePoly node1_id node2_id score cat
47 = NodeNode { _nn_node1_id :: node1_id
48 , _nn_node2_id :: node2_id
53 type NodeNodeWrite = NodeNodePoly (Column (PGInt4))
55 (Maybe (Column (PGFloat8)))
56 (Maybe (Column (PGInt4)))
58 type NodeNodeRead = NodeNodePoly (Column (PGInt4))
63 type NodeNodeReadNull = NodeNodePoly (Column (Nullable PGInt4))
64 (Column (Nullable PGInt4))
65 (Column (Nullable PGFloat8))
66 (Column (Nullable PGInt4))
68 type NodeNode = NodeNodePoly NodeId NodeId (Maybe Double) (Maybe Int)
70 $(makeAdaptorAndInstance "pNodeNode" ''NodeNodePoly)
71 makeLenses ''NodeNodePoly
73 nodeNodeTable :: Table NodeNodeWrite NodeNodeRead
74 nodeNodeTable = Table "nodes_nodes" (pNodeNode
75 NodeNode { _nn_node1_id = required "node1_id"
76 , _nn_node2_id = required "node2_id"
77 , _nn_score = optional "score"
78 , _nn_category = optional "category"
82 queryNodeNodeTable :: Query NodeNodeRead
83 queryNodeNodeTable = queryTable nodeNodeTable
86 -- | not optimized (get all ngrams without filters)
87 nodesNodes :: Cmd err [NodeNode]
88 nodesNodes = runOpaQuery queryNodeNodeTable
90 instance QueryRunnerColumnDefault (Nullable PGInt4) Int where
91 queryRunnerColumnDefault = fieldQueryRunnerColumn
93 instance QueryRunnerColumnDefault (Nullable PGFloat8) Int where
94 queryRunnerColumnDefault = fieldQueryRunnerColumn
96 instance QueryRunnerColumnDefault (Nullable PGFloat8) Double where
97 queryRunnerColumnDefault = fieldQueryRunnerColumn
99 instance QueryRunnerColumnDefault PGFloat8 (Maybe Double) where
100 queryRunnerColumnDefault = fieldQueryRunnerColumn
102 instance QueryRunnerColumnDefault PGInt4 (Maybe Int) where
103 queryRunnerColumnDefault = fieldQueryRunnerColumn
105 ------------------------------------------------------------------------
106 -- | Basic NodeNode tools
107 getNodeNode :: NodeId -> Cmd err [NodeNode]
108 getNodeNode n = runOpaQuery (selectNodeNode $ pgNodeId n)
110 selectNodeNode :: Column PGInt4 -> Query NodeNodeRead
111 selectNodeNode n' = proc () -> do
112 ns <- queryNodeNodeTable -< ()
113 restrict -< _nn_node1_id ns .== n'
116 -------------------------
117 insertNodeNode :: [NodeNode] -> Cmd err Int64
118 insertNodeNode ns = mkCmd $ \conn -> runInsert_ conn $ Insert nodeNodeTable ns' rCount Nothing
120 ns' :: [NodeNodeWrite]
121 ns' = map (\(NodeNode n1 n2 x y)
122 -> NodeNode (pgNodeId n1)
129 -- | Favorite management
130 nodeNodeCategory :: CorpusId -> DocId -> Int -> Cmd err [Int]
131 nodeNodeCategory cId dId c = map (\(PGS.Only a) -> a) <$> runPGSQuery favQuery (c,cId,dId)
133 favQuery :: PGS.Query
134 favQuery = [sql|UPDATE nodes_nodes SET category = ?
135 WHERE node1_id = ? AND node2_id = ?
139 nodeNodesCategory :: [(CorpusId,DocId,Int)] -> Cmd err [Int]
140 nodeNodesCategory inputData = map (\(PGS.Only a) -> a)
141 <$> runPGSQuery catQuery (PGS.Only $ Values fields inputData)
143 fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","int4"]
144 catQuery :: PGS.Query
145 catQuery = [sql| UPDATE nodes_nodes as nn0
146 SET category = nn1.category
147 FROM (?) as nn1(node1_id,node2_id,category)
148 WHERE nn0.node1_id = nn1.node1_id
149 AND nn0.node2_id = nn1.node2_id
150 RETURNING nn1.node2_id
153 ------------------------------------------------------------------------
154 -- | TODO use UTCTime fast
155 selectDocsDates :: CorpusId -> Cmd err [Text]
156 selectDocsDates cId = map (head' "selectDocsDates" . splitOn "-")
158 <$> map (view hyperdataDocument_publication_date)
161 selectDocs :: CorpusId -> Cmd err [HyperdataDocument]
162 selectDocs cId = runOpaQuery (queryDocs cId)
164 queryDocs :: CorpusId -> O.Query (Column PGJsonb)
165 queryDocs cId = proc () -> do
166 (n, nn) <- joinInCorpus -< ()
167 restrict -< nn^.nn_node1_id .== (toNullable $ pgNodeId cId)
168 restrict -< nn^.nn_category .>= (toNullable $ pgInt4 1)
169 restrict -< n^.node_typename .== (pgInt4 $ nodeTypeId NodeDocument)
170 returnA -< view (node_hyperdata) n
172 selectDocNodes :: CorpusId -> Cmd err [Node HyperdataDocument]
173 selectDocNodes cId = runOpaQuery (queryDocNodes cId)
175 queryDocNodes :: CorpusId -> O.Query NodeRead
176 queryDocNodes cId = proc () -> do
177 (n, nn) <- joinInCorpus -< ()
178 restrict -< nn^.nn_node1_id .== (toNullable $ pgNodeId cId)
179 restrict -< nn^.nn_category .>= (toNullable $ pgInt4 1)
180 restrict -< n^.node_typename .== (pgInt4 $ nodeTypeId NodeDocument)
183 joinInCorpus :: O.Query (NodeRead, NodeNodeReadNull)
184 joinInCorpus = leftJoin queryNodeTable queryNodeNodeTable cond
186 cond :: (NodeRead, NodeNodeRead) -> Column PGBool
187 cond (n, nn) = nn^.nn_node2_id .== (view node_id n)
189 ------------------------------------------------------------------------
190 -- | Trash management
191 nodeToTrash :: CorpusId -> DocId -> Bool -> Cmd err [PGS.Only Int]
192 nodeToTrash cId dId b = runPGSQuery trashQuery (b,cId,dId)
194 trashQuery :: PGS.Query
195 trashQuery = [sql|UPDATE nodes_nodes SET delete = ?
196 WHERE node1_id = ? AND node2_id = ?
201 nodesToTrash :: [(CorpusId,DocId,Bool)] -> Cmd err [Int]
202 nodesToTrash input = map (\(PGS.Only a) -> a)
203 <$> runPGSQuery trashQuery (PGS.Only $ Values fields input)
205 fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","bool"]
206 trashQuery :: PGS.Query
207 trashQuery = [sql| UPDATE nodes_nodes as nn0 SET
209 from (?) as nn1(node1_id,node2_id,delete)
210 WHERE nn0.node1_id = nn1.node1_id
211 AND nn0.node2_id = nn1.node2_id
212 RETURNING nn1.node2_id
215 -- | /!\ Really remove nodes in the Corpus or Annuaire
216 emptyTrash :: CorpusId -> Cmd err [PGS.Only Int]
217 emptyTrash cId = runPGSQuery delQuery (PGS.Only cId)
219 delQuery :: PGS.Query
220 delQuery = [sql|DELETE from nodes_nodes n
225 ------------------------------------------------------------------------