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
37 , ContextForNgramsTerms(..)
40 , selectPublicContexts
45 import Control.Arrow (returnA)
46 import Control.Lens (view, (^.))
47 import Data.Maybe (catMaybes)
48 import Data.Time (UTCTime)
49 import Data.Text (Text, splitOn)
50 import Database.PostgreSQL.Simple.SqlQQ (sql)
51 import Database.PostgreSQL.Simple.Types (Values(..), QualifiedIdentifier(..))
53 import qualified Database.PostgreSQL.Simple as PGS (In(..), Query, Only(..))
54 import qualified Opaleye as O
56 import Gargantext.Core
57 import Gargantext.Core.Types
58 -- import Gargantext.Core.Types.Search (HyperdataRow(..), toHyperdataRow)
59 import Gargantext.Database.Admin.Types.Hyperdata
60 import Gargantext.Database.Query.Table.Node.Error (HasNodeError, NodeError(DoesNotExist), nodeError)
61 import Gargantext.Database.Prelude
62 import Gargantext.Prelude.Crypto.Hash (Hash)
63 import Gargantext.Database.Schema.Context
64 import Gargantext.Database.Schema.Node
65 import Gargantext.Database.Schema.NodeContext
66 import Gargantext.Prelude
68 queryNodeContextTable :: Select NodeContextRead
69 queryNodeContextTable = selectTable nodeContextTable
71 -- | not optimized (get all ngrams without filters)
72 _nodesContexts :: Cmd err [NodeContext]
73 _nodesContexts = runOpaQuery queryNodeContextTable
75 ------------------------------------------------------------------------
76 -- | Basic NodeContext tools
77 getNodeContexts :: NodeId -> Cmd err [NodeContext]
78 getNodeContexts n = runOpaQuery (selectNodeContexts $ pgNodeId n)
80 selectNodeContexts :: Field SqlInt4 -> Select NodeContextRead
81 selectNodeContexts n' = proc () -> do
82 ns <- queryNodeContextTable -< ()
83 restrict -< _nc_node_id ns .== n'
87 getNodeContext :: HasNodeError err => ContextId -> NodeId -> Cmd err NodeContext
88 getNodeContext c n = do
89 maybeNodeContext <- headMay <$> runOpaQuery (selectNodeContext (pgNodeId c) (pgNodeId n))
90 case maybeNodeContext of
91 Nothing -> nodeError (DoesNotExist c)
94 selectNodeContext :: Field SqlInt4 -> Field SqlInt4 -> Select NodeContextRead
95 selectNodeContext c' n' = proc () -> do
96 ns <- queryNodeContextTable -< ()
97 restrict -< _nc_context_id ns .== c'
98 restrict -< _nc_node_id ns .== n'
101 updateNodeContextCategory :: ContextId -> NodeId -> Int -> Cmd err Int64
102 updateNodeContextCategory cId nId cat = do
103 execPGSQuery upScore (cat, cId, nId)
106 upScore = [sql| UPDATE nodes_contexts
111 data ContextForNgrams =
112 ContextForNgrams { _cfn_nodeId :: NodeId
113 , _cfn_hash :: Maybe Hash
114 , _cfn_userId :: UserId
115 , _cfn_parentId :: Maybe ParentId
116 , _cfn_c_title :: ContextTitle
117 , _cfn_date :: UTCTime
118 , _cfn_hyperdata :: HyperdataDocument }
119 getContextsForNgrams :: HasNodeError err
122 -> Cmd err [ContextForNgrams]
123 getContextsForNgrams cId ngramsIds = do
124 res <- runPGSQuery query (cId, PGS.In ngramsIds)
125 pure $ (\( _cfn_nodeId
131 , _cfn_hyperdata) -> ContextForNgrams { .. }) <$> res
134 query = [sql| SELECT contexts.id, hash_id, typename, user_id, parent_id, name, date, hyperdata
136 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
137 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
138 WHERE nodes_contexts.node_id = ?
139 AND context_node_ngrams.ngrams_id IN ? |]
141 data ContextForNgramsTerms =
142 ContextForNgramsTerms { _cfnt_nodeId :: NodeId
143 , _cfnt_hash :: Maybe Hash
144 , _cfnt_nodeTypeId :: NodeTypeId
145 , _cfnt_userId :: UserId
146 , _cfnt_parentId :: Maybe ParentId
147 , _cfnt_c_title :: ContextTitle
148 , _cfnt_date :: UTCTime
149 , _cfnt_hyperdata :: HyperdataDocument
150 , _cfnt_score :: Maybe Double
151 , _cfnt_category :: Maybe Int }
152 getContextsForNgramsTerms :: HasNodeError err
155 -> Cmd err [ContextForNgramsTerms]
156 getContextsForNgramsTerms cId ngramsTerms = do
157 res <- runPGSQuery query (cId, PGS.In ngramsTerms)
158 pure $ (\( _cfnt_nodeId
167 , _cfnt_category) -> ContextForNgramsTerms { .. }) <$> res
170 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
172 SELECT DISTINCT ON (contexts.id)
181 nodes_contexts.score AS score,
182 nodes_contexts.category AS category,
183 context_node_ngrams.doc_count AS doc_count
185 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
186 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
187 JOIN ngrams ON context_node_ngrams.ngrams_id = ngrams.id
188 WHERE nodes_contexts.node_id = ?
189 AND ngrams.terms IN ?) t
190 ORDER BY t.doc_count DESC |]
194 getContextNgrams :: HasNodeError err
198 getContextNgrams contextId listId = do
199 res <- runPGSQuery query (contextId, listId)
200 pure $ (\(PGS.Only term) -> term) <$> res
204 query = [sql| SELECT ngrams.terms
205 FROM context_node_ngrams
206 JOIN ngrams ON ngrams.id = ngrams_id
209 ------------------------------------------------------------------------
210 insertNodeContext :: [NodeContext] -> Cmd err Int
211 insertNodeContext ns = mkCmd $ \conn -> fromIntegral <$> (runInsert_ conn
212 $ Insert nodeContextTable ns' rCount (Just DoNothing))
214 ns' :: [NodeContextWrite]
215 ns' = map (\(NodeContext i n c x y)
216 -> NodeContext (sqlInt4 <$> i)
224 ------------------------------------------------------------------------
225 type Node_Id = NodeId
226 type Context_Id = NodeId
228 deleteNodeContext :: Node_Id -> Context_Id -> Cmd err Int
229 deleteNodeContext n c = mkCmd $ \conn ->
230 fromIntegral <$> runDelete_ conn
231 (Delete nodeContextTable
232 (\(NodeContext _ n_id c_id _ _) -> n_id .== pgNodeId n
233 .&& c_id .== pgNodeId c
238 ------------------------------------------------------------------------
239 -- | Favorite management
240 nodeContextsCategory :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
241 nodeContextsCategory inputData = map (\(PGS.Only a) -> a)
242 <$> runPGSQuery catSelect (PGS.Only $ Values fields inputData)
244 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
245 catSelect :: PGS.Query
246 catSelect = [sql| UPDATE nodes_contexts as nn0
247 SET category = nn1.category
248 FROM (?) as nn1(node_id,context_id,category)
249 WHERE nn0.node_id = nn1.node_id
250 AND nn0.context_id = nn1.context_id
251 RETURNING nn1.node_id
254 ------------------------------------------------------------------------
255 -- | Score management
256 nodeContextsScore :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
257 nodeContextsScore inputData = map (\(PGS.Only a) -> a)
258 <$> runPGSQuery catScore (PGS.Only $ Values fields inputData)
260 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
261 catScore :: PGS.Query
262 catScore = [sql| UPDATE nodes_contexts as nn0
263 SET score = nn1.score
264 FROM (?) as nn1(node_id, context_id, score)
265 WHERE nn0.node_id = nn1.node_id
266 AND nn0.context_id = nn1.context_id
267 RETURNING nn1.context_id
271 ------------------------------------------------------------------------
272 selectCountDocs :: HasDBid NodeType => CorpusId -> Cmd err Int
273 selectCountDocs cId = runCountOpaQuery (queryCountDocs cId)
275 queryCountDocs cId' = proc () -> do
276 (c, nc) <- joinInCorpus -< ()
277 restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId' .&&
278 (nc' ^. nc_category) .>= sqlInt4 1
279 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
283 -- | TODO use UTCTime fast
284 selectDocsDates :: HasDBid NodeType => CorpusId -> Cmd err [Text]
285 selectDocsDates cId = map (head' "selectDocsDates" . splitOn "-")
287 <$> map (view hd_publication_date)
290 selectDocs :: HasDBid NodeType => CorpusId -> Cmd err [HyperdataDocument]
291 selectDocs cId = runOpaQuery (queryDocs cId)
293 queryDocs :: HasDBid NodeType => CorpusId -> O.Select (Field SqlJsonb)
294 queryDocs cId = proc () -> do
295 (c, nn) <- joinInCorpus -< ()
296 restrict -< restrictMaybe nn $ \nn' -> (nn' ^. nc_node_id) .== pgNodeId cId .&&
297 (nn' ^. nc_category) .>= sqlInt4 1
298 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
299 returnA -< view (context_hyperdata) c
301 selectDocNodes :: HasDBid NodeType => CorpusId -> Cmd err [Context HyperdataDocument]
302 selectDocNodes cId = runOpaQuery (queryDocNodes cId)
304 queryDocNodes :: HasDBid NodeType => CorpusId -> O.Select ContextRead
305 queryDocNodes cId = proc () -> do
306 (c, nc) <- joinInCorpus -< ()
307 -- restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
308 -- (nc' ^. nc_category) .>= sqlInt4 1
309 restrict -< matchMaybe nc $ \case
310 Nothing -> toFields True
311 Just nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
312 (nc' ^. nc_category) .>= sqlInt4 1
313 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
316 joinInCorpus :: O.Select (ContextRead, MaybeFields NodeContextRead)
317 joinInCorpus = proc () -> do
318 c <- queryContextTable -< ()
319 nc <- optionalRestrict queryNodeContextTable -<
320 (\nc' -> (c ^. context_id) .== (nc' ^. nc_context_id))
324 joinOn1 :: O.Select (NodeRead, MaybeFields NodeContextRead)
325 joinOn1 = proc () -> do
326 n <- queryNodeTable -< ()
327 nc <- optionalRestrict queryNodeContextTable -<
328 (\nc' -> (nc' ^. nc_node_id) .== (n ^. node_id))
332 ------------------------------------------------------------------------
333 selectPublicContexts :: HasDBid NodeType => (Hyperdata a, DefaultFromField SqlJsonb a)
334 => Cmd err [(Node a, Maybe Int)]
335 selectPublicContexts = runOpaQuery (queryWithType NodeFolderPublic)
337 queryWithType :: HasDBid NodeType => NodeType -> O.Select (NodeRead, MaybeFields (Field SqlInt4))
338 queryWithType nt = proc () -> do
339 (n, nc) <- joinOn1 -< ()
340 restrict -< (n ^. node_typename) .== sqlInt4 (toDBid nt)
341 returnA -< (n, view nc_context_id <$> nc)