2 Module : Gargantext.Database.Query.Table.NodeContext
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 FunctionalDependencies #-}
18 {-# LANGUAGE QuasiQuotes #-}
19 {-# LANGUAGE TemplateHaskell #-}
21 module Gargantext.Database.Query.Table.NodeContext
22 ( module Gargantext.Database.Schema.NodeContext
23 , queryNodeContextTable
27 , nodeContextsCategory
31 , updateNodeContextCategory
32 , getContextsForNgrams
33 , ContextForNgrams(..)
34 , getContextsForNgramsTerms
35 , ContextForNgramsTerms(..)
38 , selectPublicContexts
43 import Control.Arrow (returnA)
44 import Control.Lens (view, (^.))
45 import Data.Maybe (catMaybes)
46 import Data.Time (UTCTime)
47 import Data.Text (Text, splitOn)
48 import Database.PostgreSQL.Simple.SqlQQ (sql)
49 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
51 import qualified Database.PostgreSQL.Simple as PGS (In(..), Query, Only(..))
52 import qualified Opaleye as O
54 import Gargantext.Core
55 import Gargantext.Core.Types
56 -- import Gargantext.Core.Types.Search (HyperdataRow(..), toHyperdataRow)
57 import Gargantext.Database.Admin.Types.Hyperdata
58 import Gargantext.Database.Query.Table.Node.Error (HasNodeError, NodeError(DoesNotExist), nodeError)
59 import Gargantext.Database.Prelude
60 import Gargantext.Prelude.Crypto.Hash (Hash)
61 import Gargantext.Database.Schema.Context
62 import Gargantext.Database.Schema.Node
63 import Gargantext.Database.Schema.NodeContext
64 import Gargantext.Prelude
66 queryNodeContextTable :: Select NodeContextRead
67 queryNodeContextTable = selectTable nodeContextTable
69 -- | not optimized (get all ngrams without filters)
70 _nodesContexts :: Cmd err [NodeContext]
71 _nodesContexts = runOpaQuery queryNodeContextTable
73 ------------------------------------------------------------------------
74 -- | Basic NodeContext tools
75 getNodeContexts :: NodeId -> Cmd err [NodeContext]
76 getNodeContexts n = runOpaQuery (selectNodeContexts $ pgNodeId n)
78 selectNodeContexts :: Column SqlInt4 -> Select NodeContextRead
79 selectNodeContexts n' = proc () -> do
80 ns <- queryNodeContextTable -< ()
81 restrict -< _nc_node_id ns .== n'
85 getNodeContext :: HasNodeError err => ContextId -> NodeId -> Cmd err NodeContext
86 getNodeContext c n = do
87 maybeNodeContext <- headMay <$> runOpaQuery (selectNodeContext (pgNodeId c) (pgNodeId n))
88 case maybeNodeContext of
89 Nothing -> nodeError (DoesNotExist c)
92 selectNodeContext :: Column SqlInt4 -> Column SqlInt4 -> Select NodeContextRead
93 selectNodeContext c' n' = proc () -> do
94 ns <- queryNodeContextTable -< ()
95 restrict -< _nc_context_id ns .== c'
96 restrict -< _nc_node_id ns .== n'
99 updateNodeContextCategory :: ContextId -> NodeId -> Int -> Cmd err Int64
100 updateNodeContextCategory cId nId cat = do
101 execPGSQuery upScore (cat, cId, nId)
104 upScore = [sql| UPDATE nodes_contexts
109 data ContextForNgrams =
110 ContextForNgrams { _cfn_nodeId :: NodeId
111 , _cfn_hash :: Maybe Hash
112 , _cfn_userId :: UserId
113 , _cfn_parentId :: Maybe ParentId
114 , _cfn_c_title :: ContextTitle
115 , _cfn_date :: UTCTime
116 , _cfn_hyperdata :: HyperdataDocument }
117 getContextsForNgrams :: HasNodeError err
120 -> Cmd err [ContextForNgrams]
121 getContextsForNgrams cId ngramsIds = do
122 res <- runPGSQuery query (cId, PGS.In ngramsIds)
123 pure $ (\( _cfn_nodeId
129 , _cfn_hyperdata) -> ContextForNgrams { .. }) <$> res
132 query = [sql| SELECT contexts.id, hash_id, typename, user_id, parent_id, name, date, hyperdata
134 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
135 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
136 WHERE nodes_contexts.node_id = ?
137 AND context_node_ngrams.ngrams_id IN ? |]
139 data ContextForNgramsTerms =
140 ContextForNgramsTerms { _cfnt_nodeId :: NodeId
141 , _cfnt_hash :: Maybe Hash
142 , _cfnt_nodeTypeId :: NodeTypeId
143 , _cfnt_userId :: UserId
144 , _cfnt_parentId :: Maybe ParentId
145 , _cfnt_c_title :: ContextTitle
146 , _cfnt_date :: UTCTime
147 , _cfnt_hyperdata :: HyperdataDocument
148 , _cfnt_score :: Maybe Double
149 , _cfnt_category :: Maybe Int }
150 getContextsForNgramsTerms :: HasNodeError err
153 -> Cmd err [ContextForNgramsTerms]
154 getContextsForNgramsTerms cId ngramsTerms = do
155 res <- runPGSQuery query (cId, PGS.In ngramsTerms)
156 pure $ (\( _cfnt_nodeId
165 , _cfnt_category) -> ContextForNgramsTerms { .. }) <$> res
168 query = [sql| SELECT t.id, t.hash_id, t.typename, t.user_id, t.parent_id, t.name, t.date, t.hyperdata, t.score, t.category
170 SELECT DISTINCT ON (contexts.id) contexts.id AS id, hash_id, typename, user_id, parent_id, name, date, hyperdata, nodes_contexts.score AS score, nodes_contexts.category AS category,context_node_ngrams.doc_count AS doc_count
172 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
173 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
174 JOIN ngrams ON context_node_ngrams.ngrams_id = ngrams.id
175 WHERE nodes_contexts.node_id = ?
176 AND ngrams.terms IN ?) t
177 ORDER BY t.doc_count DESC |]
179 ------------------------------------------------------------------------
180 insertNodeContext :: [NodeContext] -> Cmd err Int
181 insertNodeContext ns = mkCmd $ \conn -> fromIntegral <$> (runInsert_ conn
182 $ Insert nodeContextTable ns' rCount (Just DoNothing))
184 ns' :: [NodeContextWrite]
185 ns' = map (\(NodeContext i n c x y)
186 -> NodeContext (sqlInt4 <$> i)
194 ------------------------------------------------------------------------
195 type Node_Id = NodeId
196 type Context_Id = NodeId
198 deleteNodeContext :: Node_Id -> Context_Id -> Cmd err Int
199 deleteNodeContext n c = mkCmd $ \conn ->
200 fromIntegral <$> runDelete_ conn
201 (Delete nodeContextTable
202 (\(NodeContext _ n_id c_id _ _) -> n_id .== pgNodeId n
203 .&& c_id .== pgNodeId c
208 ------------------------------------------------------------------------
209 -- | Favorite management
210 nodeContextsCategory :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
211 nodeContextsCategory inputData = map (\(PGS.Only a) -> a)
212 <$> runPGSQuery catSelect (PGS.Only $ Values fields inputData)
214 fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","int4"]
215 catSelect :: PGS.Query
216 catSelect = [sql| UPDATE nodes_contexts as nn0
217 SET category = nn1.category
218 FROM (?) as nn1(node_id,context_id,category)
219 WHERE nn0.node_id = nn1.node_id
220 AND nn0.context_id = nn1.context_id
221 RETURNING nn1.node_id
224 ------------------------------------------------------------------------
225 -- | Score management
226 nodeContextsScore :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
227 nodeContextsScore inputData = map (\(PGS.Only a) -> a)
228 <$> runPGSQuery catScore (PGS.Only $ Values fields inputData)
230 fields = map (\t-> QualifiedIdentifier Nothing t) ["int4","int4","int4"]
231 catScore :: PGS.Query
232 catScore = [sql| UPDATE nodes_contexts as nn0
233 SET score = nn1.score
234 FROM (?) as nn1(node_id, context_id, score)
235 WHERE nn0.node_id = nn1.node_id
236 AND nn0.context_id = nn1.context_id
237 RETURNING nn1.context_id
241 ------------------------------------------------------------------------
242 selectCountDocs :: HasDBid NodeType => CorpusId -> Cmd err Int
243 selectCountDocs cId = runCountOpaQuery (queryCountDocs cId)
245 queryCountDocs cId' = proc () -> do
246 (c, nc) <- joinInCorpus -< ()
247 restrict -< nc^.nc_node_id .== (toNullable $ pgNodeId cId')
248 restrict -< nc^.nc_category .>= (toNullable $ sqlInt4 1)
249 restrict -< c^.context_typename .== (sqlInt4 $ toDBid NodeDocument)
253 -- | TODO use UTCTime fast
254 selectDocsDates :: HasDBid NodeType => CorpusId -> Cmd err [Text]
255 selectDocsDates cId = map (head' "selectDocsDates" . splitOn "-")
257 <$> map (view hd_publication_date)
260 selectDocs :: HasDBid NodeType => CorpusId -> Cmd err [HyperdataDocument]
261 selectDocs cId = runOpaQuery (queryDocs cId)
263 queryDocs :: HasDBid NodeType => CorpusId -> O.Select (Column SqlJsonb)
264 queryDocs cId = proc () -> do
265 (c, nn) <- joinInCorpus -< ()
266 restrict -< nn^.nc_node_id .== (toNullable $ pgNodeId cId)
267 restrict -< nn^.nc_category .>= (toNullable $ sqlInt4 1)
268 restrict -< c^.context_typename .== (sqlInt4 $ toDBid NodeDocument)
269 returnA -< view (context_hyperdata) c
271 selectDocNodes :: HasDBid NodeType => CorpusId -> Cmd err [Context HyperdataDocument]
272 selectDocNodes cId = runOpaQuery (queryDocNodes cId)
274 queryDocNodes :: HasDBid NodeType => CorpusId -> O.Select ContextRead
275 queryDocNodes cId = proc () -> do
276 (c, nc) <- joinInCorpus -< ()
277 restrict -< nc^.nc_node_id .== (toNullable $ pgNodeId cId)
278 restrict -< nc^.nc_category .>= (toNullable $ sqlInt4 1)
279 restrict -< c^.context_typename .== (sqlInt4 $ toDBid NodeDocument)
282 joinInCorpus :: O.Select (ContextRead, NodeContextReadNull)
283 joinInCorpus = leftJoin queryContextTable queryNodeContextTable cond
285 cond :: (ContextRead, NodeContextRead) -> Column SqlBool
286 cond (c, nc) = c^.context_id .== nc^.nc_context_id
289 joinOn1 :: O.Select (NodeRead, NodeContextReadNull)
290 joinOn1 = leftJoin queryNodeTable queryNodeContextTable cond
292 cond :: (NodeRead, NodeContextRead) -> Column SqlBool
293 cond (n, nc) = nc^.nc_node_id .== n^.node_id
296 ------------------------------------------------------------------------
297 selectPublicContexts :: HasDBid NodeType => (Hyperdata a, DefaultFromField SqlJsonb a)
298 => Cmd err [(Node a, Maybe Int)]
299 selectPublicContexts = runOpaQuery (queryWithType NodeFolderPublic)
301 queryWithType :: HasDBid NodeType =>NodeType -> O.Select (NodeRead, Column (Nullable SqlInt4))
302 queryWithType nt = proc () -> do
303 (n, nc) <- joinOn1 -< ()
304 restrict -< n^.node_typename .== (sqlInt4 $ toDBid nt)
305 returnA -< (n, nc^.nc_context_id)