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 LambdaCase #-}
19 {-# LANGUAGE QuasiQuotes #-}
20 {-# LANGUAGE TemplateHaskell #-}
22 module Gargantext.Database.Query.Table.NodeContext
23 ( module Gargantext.Database.Schema.NodeContext
24 , queryNodeContextTable
28 , nodeContextsCategory
32 , updateNodeContextCategory
33 , getContextsForNgrams
34 , ContextForNgrams(..)
35 , getContextsForNgramsTerms
36 , ContextForNgramsTerms(..)
39 , selectPublicContexts
44 import Control.Arrow (returnA)
45 import Control.Lens (view, (^.))
46 import Data.Maybe (catMaybes)
47 import Data.Time (UTCTime)
48 import Data.Text (Text, splitOn)
49 import Database.PostgreSQL.Simple.SqlQQ (sql)
50 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
52 import qualified Database.PostgreSQL.Simple as PGS (In(..), Query, Only(..))
53 import qualified Opaleye as O
55 import Gargantext.Core
56 import Gargantext.Core.Types
57 -- import Gargantext.Core.Types.Search (HyperdataRow(..), toHyperdataRow)
58 import Gargantext.Database.Admin.Types.Hyperdata
59 import Gargantext.Database.Query.Table.Node.Error (HasNodeError, NodeError(DoesNotExist), nodeError)
60 import Gargantext.Database.Prelude
61 import Gargantext.Prelude.Crypto.Hash (Hash)
62 import Gargantext.Database.Schema.Context
63 import Gargantext.Database.Schema.Node
64 import Gargantext.Database.Schema.NodeContext
65 import Gargantext.Prelude
67 queryNodeContextTable :: Select NodeContextRead
68 queryNodeContextTable = selectTable nodeContextTable
70 -- | not optimized (get all ngrams without filters)
71 _nodesContexts :: Cmd err [NodeContext]
72 _nodesContexts = runOpaQuery queryNodeContextTable
74 ------------------------------------------------------------------------
75 -- | Basic NodeContext tools
76 getNodeContexts :: NodeId -> Cmd err [NodeContext]
77 getNodeContexts n = runOpaQuery (selectNodeContexts $ pgNodeId n)
79 selectNodeContexts :: Field SqlInt4 -> Select NodeContextRead
80 selectNodeContexts n' = proc () -> do
81 ns <- queryNodeContextTable -< ()
82 restrict -< _nc_node_id ns .== n'
86 getNodeContext :: HasNodeError err => ContextId -> NodeId -> Cmd err NodeContext
87 getNodeContext c n = do
88 maybeNodeContext <- headMay <$> runOpaQuery (selectNodeContext (pgNodeId c) (pgNodeId n))
89 case maybeNodeContext of
90 Nothing -> nodeError (DoesNotExist c)
93 selectNodeContext :: Field SqlInt4 -> Field SqlInt4 -> Select NodeContextRead
94 selectNodeContext c' n' = proc () -> do
95 ns <- queryNodeContextTable -< ()
96 restrict -< _nc_context_id ns .== c'
97 restrict -< _nc_node_id ns .== n'
100 updateNodeContextCategory :: ContextId -> NodeId -> Int -> Cmd err Int64
101 updateNodeContextCategory cId nId cat = do
102 execPGSQuery upScore (cat, cId, nId)
105 upScore = [sql| UPDATE nodes_contexts
110 data ContextForNgrams =
111 ContextForNgrams { _cfn_nodeId :: NodeId
112 , _cfn_hash :: Maybe Hash
113 , _cfn_userId :: UserId
114 , _cfn_parentId :: Maybe ParentId
115 , _cfn_c_title :: ContextTitle
116 , _cfn_date :: UTCTime
117 , _cfn_hyperdata :: HyperdataDocument }
118 getContextsForNgrams :: HasNodeError err
121 -> Cmd err [ContextForNgrams]
122 getContextsForNgrams cId ngramsIds = do
123 res <- runPGSQuery query (cId, PGS.In ngramsIds)
124 pure $ (\( _cfn_nodeId
130 , _cfn_hyperdata) -> ContextForNgrams { .. }) <$> res
133 query = [sql| SELECT contexts.id, hash_id, typename, user_id, parent_id, name, date, hyperdata
135 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
136 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
137 WHERE nodes_contexts.node_id = ?
138 AND context_node_ngrams.ngrams_id IN ? |]
140 data ContextForNgramsTerms =
141 ContextForNgramsTerms { _cfnt_nodeId :: NodeId
142 , _cfnt_hash :: Maybe Hash
143 , _cfnt_nodeTypeId :: NodeTypeId
144 , _cfnt_userId :: UserId
145 , _cfnt_parentId :: Maybe ParentId
146 , _cfnt_c_title :: ContextTitle
147 , _cfnt_date :: UTCTime
148 , _cfnt_hyperdata :: HyperdataDocument
149 , _cfnt_score :: Maybe Double
150 , _cfnt_category :: Maybe Int }
151 getContextsForNgramsTerms :: HasNodeError err
154 -> Cmd err [ContextForNgramsTerms]
155 getContextsForNgramsTerms cId ngramsTerms = do
156 res <- runPGSQuery query (cId, PGS.In ngramsTerms)
157 pure $ (\( _cfnt_nodeId
166 , _cfnt_category) -> ContextForNgramsTerms { .. }) <$> res
169 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
171 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
173 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
174 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
175 JOIN ngrams ON context_node_ngrams.ngrams_id = ngrams.id
176 WHERE nodes_contexts.node_id = ?
177 AND ngrams.terms IN ?) t
178 ORDER BY t.doc_count DESC |]
180 ------------------------------------------------------------------------
181 insertNodeContext :: [NodeContext] -> Cmd err Int
182 insertNodeContext ns = mkCmd $ \conn -> fromIntegral <$> (runInsert_ conn
183 $ Insert nodeContextTable ns' rCount (Just DoNothing))
185 ns' :: [NodeContextWrite]
186 ns' = map (\(NodeContext i n c x y)
187 -> NodeContext (sqlInt4 <$> i)
195 ------------------------------------------------------------------------
196 type Node_Id = NodeId
197 type Context_Id = NodeId
199 deleteNodeContext :: Node_Id -> Context_Id -> Cmd err Int
200 deleteNodeContext n c = mkCmd $ \conn ->
201 fromIntegral <$> runDelete_ conn
202 (Delete nodeContextTable
203 (\(NodeContext _ n_id c_id _ _) -> n_id .== pgNodeId n
204 .&& c_id .== pgNodeId c
209 ------------------------------------------------------------------------
210 -- | Favorite management
211 nodeContextsCategory :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
212 nodeContextsCategory inputData = map (\(PGS.Only a) -> a)
213 <$> runPGSQuery catSelect (PGS.Only $ Values fields inputData)
215 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
216 catSelect :: PGS.Query
217 catSelect = [sql| UPDATE nodes_contexts as nn0
218 SET category = nn1.category
219 FROM (?) as nn1(node_id,context_id,category)
220 WHERE nn0.node_id = nn1.node_id
221 AND nn0.context_id = nn1.context_id
222 RETURNING nn1.node_id
225 ------------------------------------------------------------------------
226 -- | Score management
227 nodeContextsScore :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
228 nodeContextsScore inputData = map (\(PGS.Only a) -> a)
229 <$> runPGSQuery catScore (PGS.Only $ Values fields inputData)
231 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
232 catScore :: PGS.Query
233 catScore = [sql| UPDATE nodes_contexts as nn0
234 SET score = nn1.score
235 FROM (?) as nn1(node_id, context_id, score)
236 WHERE nn0.node_id = nn1.node_id
237 AND nn0.context_id = nn1.context_id
238 RETURNING nn1.context_id
242 ------------------------------------------------------------------------
243 selectCountDocs :: HasDBid NodeType => CorpusId -> Cmd err Int
244 selectCountDocs cId = runCountOpaQuery (queryCountDocs cId)
246 queryCountDocs cId' = proc () -> do
247 (c, nc) <- joinInCorpus -< ()
248 restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId' .&&
249 (nc' ^. nc_category) .>= sqlInt4 1
250 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
254 -- | TODO use UTCTime fast
255 selectDocsDates :: HasDBid NodeType => CorpusId -> Cmd err [Text]
256 selectDocsDates cId = map (head' "selectDocsDates" . splitOn "-")
258 <$> map (view hd_publication_date)
261 selectDocs :: HasDBid NodeType => CorpusId -> Cmd err [HyperdataDocument]
262 selectDocs cId = runOpaQuery (queryDocs cId)
264 queryDocs :: HasDBid NodeType => CorpusId -> O.Select (Field SqlJsonb)
265 queryDocs cId = proc () -> do
266 (c, nn) <- joinInCorpus -< ()
267 restrict -< restrictMaybe nn $ \nn' -> (nn' ^. nc_node_id) .== pgNodeId cId .&&
268 (nn' ^. nc_category) .>= sqlInt4 1
269 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
270 returnA -< view (context_hyperdata) c
272 selectDocNodes :: HasDBid NodeType => CorpusId -> Cmd err [Context HyperdataDocument]
273 selectDocNodes cId = runOpaQuery (queryDocNodes cId)
275 queryDocNodes :: HasDBid NodeType => CorpusId -> O.Select ContextRead
276 queryDocNodes cId = proc () -> do
277 (c, nc) <- joinInCorpus -< ()
278 -- restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
279 -- (nc' ^. nc_category) .>= sqlInt4 1
280 restrict -< matchMaybe nc $ \case
281 Nothing -> toFields True
282 Just nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
283 (nc' ^. nc_category) .>= sqlInt4 1
284 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
287 joinInCorpus :: O.Select (ContextRead, MaybeFields NodeContextRead)
288 joinInCorpus = proc () -> do
289 c <- queryContextTable -< ()
290 nc <- optionalRestrict queryNodeContextTable -<
291 (\nc' -> (c ^. context_id) .== (nc' ^. nc_context_id))
295 joinOn1 :: O.Select (NodeRead, MaybeFields NodeContextRead)
296 joinOn1 = proc () -> do
297 n <- queryNodeTable -< ()
298 nc <- optionalRestrict queryNodeContextTable -<
299 (\nc' -> (nc' ^. nc_node_id) .== (n ^. node_id))
303 ------------------------------------------------------------------------
304 selectPublicContexts :: HasDBid NodeType => (Hyperdata a, DefaultFromField SqlJsonb a)
305 => Cmd err [(Node a, Maybe Int)]
306 selectPublicContexts = runOpaQuery (queryWithType NodeFolderPublic)
308 queryWithType :: HasDBid NodeType => NodeType -> O.Select (NodeRead, MaybeFields (Field SqlInt4))
309 queryWithType nt = proc () -> do
310 (n, nc) <- joinOn1 -< ()
311 restrict -< (n ^. node_typename) .== sqlInt4 (toDBid nt)
312 returnA -< (n, view nc_context_id <$> nc)