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)
180 nodes_contexts.score AS score,
181 nodes_contexts.category AS category,
182 context_node_ngrams.doc_count AS doc_count
184 JOIN context_node_ngrams ON contexts.id = context_node_ngrams.context_id
185 JOIN nodes_contexts ON contexts.id = nodes_contexts.context_id
186 JOIN ngrams ON context_node_ngrams.ngrams_id = ngrams.id
187 WHERE nodes_contexts.node_id = ?
188 AND ngrams.terms IN ?) t
189 ORDER BY t.doc_count DESC |]
191 ------------------------------------------------------------------------
192 insertNodeContext :: [NodeContext] -> Cmd err Int
193 insertNodeContext ns = mkCmd $ \conn -> fromIntegral <$> (runInsert_ conn
194 $ Insert nodeContextTable ns' rCount (Just DoNothing))
196 ns' :: [NodeContextWrite]
197 ns' = map (\(NodeContext i n c x y)
198 -> NodeContext (sqlInt4 <$> i)
206 ------------------------------------------------------------------------
207 type Node_Id = NodeId
208 type Context_Id = NodeId
210 deleteNodeContext :: Node_Id -> Context_Id -> Cmd err Int
211 deleteNodeContext n c = mkCmd $ \conn ->
212 fromIntegral <$> runDelete_ conn
213 (Delete nodeContextTable
214 (\(NodeContext _ n_id c_id _ _) -> n_id .== pgNodeId n
215 .&& c_id .== pgNodeId c
220 ------------------------------------------------------------------------
221 -- | Favorite management
222 nodeContextsCategory :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
223 nodeContextsCategory inputData = map (\(PGS.Only a) -> a)
224 <$> runPGSQuery catSelect (PGS.Only $ Values fields inputData)
226 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
227 catSelect :: PGS.Query
228 catSelect = [sql| UPDATE nodes_contexts as nn0
229 SET category = nn1.category
230 FROM (?) as nn1(node_id,context_id,category)
231 WHERE nn0.node_id = nn1.node_id
232 AND nn0.context_id = nn1.context_id
233 RETURNING nn1.node_id
236 ------------------------------------------------------------------------
237 -- | Score management
238 nodeContextsScore :: [(CorpusId, DocId, Int)] -> Cmd err [Int]
239 nodeContextsScore inputData = map (\(PGS.Only a) -> a)
240 <$> runPGSQuery catScore (PGS.Only $ Values fields inputData)
242 fields = map (QualifiedIdentifier Nothing) ["int4","int4","int4"]
243 catScore :: PGS.Query
244 catScore = [sql| UPDATE nodes_contexts as nn0
245 SET score = nn1.score
246 FROM (?) as nn1(node_id, context_id, score)
247 WHERE nn0.node_id = nn1.node_id
248 AND nn0.context_id = nn1.context_id
249 RETURNING nn1.context_id
253 ------------------------------------------------------------------------
254 selectCountDocs :: HasDBid NodeType => CorpusId -> Cmd err Int
255 selectCountDocs cId = runCountOpaQuery (queryCountDocs cId)
257 queryCountDocs cId' = proc () -> do
258 (c, nc) <- joinInCorpus -< ()
259 restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId' .&&
260 (nc' ^. nc_category) .>= sqlInt4 1
261 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
265 -- | TODO use UTCTime fast
266 selectDocsDates :: HasDBid NodeType => CorpusId -> Cmd err [Text]
267 selectDocsDates cId = map (head' "selectDocsDates" . splitOn "-")
269 <$> map (view hd_publication_date)
272 selectDocs :: HasDBid NodeType => CorpusId -> Cmd err [HyperdataDocument]
273 selectDocs cId = runOpaQuery (queryDocs cId)
275 queryDocs :: HasDBid NodeType => CorpusId -> O.Select (Field SqlJsonb)
276 queryDocs cId = proc () -> do
277 (c, nn) <- joinInCorpus -< ()
278 restrict -< restrictMaybe nn $ \nn' -> (nn' ^. nc_node_id) .== pgNodeId cId .&&
279 (nn' ^. nc_category) .>= sqlInt4 1
280 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
281 returnA -< view (context_hyperdata) c
283 selectDocNodes :: HasDBid NodeType => CorpusId -> Cmd err [Context HyperdataDocument]
284 selectDocNodes cId = runOpaQuery (queryDocNodes cId)
286 queryDocNodes :: HasDBid NodeType => CorpusId -> O.Select ContextRead
287 queryDocNodes cId = proc () -> do
288 (c, nc) <- joinInCorpus -< ()
289 -- restrict -< restrictMaybe nc $ \nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
290 -- (nc' ^. nc_category) .>= sqlInt4 1
291 restrict -< matchMaybe nc $ \case
292 Nothing -> toFields True
293 Just nc' -> (nc' ^. nc_node_id) .== pgNodeId cId .&&
294 (nc' ^. nc_category) .>= sqlInt4 1
295 restrict -< (c ^. context_typename) .== sqlInt4 (toDBid NodeDocument)
298 joinInCorpus :: O.Select (ContextRead, MaybeFields NodeContextRead)
299 joinInCorpus = proc () -> do
300 c <- queryContextTable -< ()
301 nc <- optionalRestrict queryNodeContextTable -<
302 (\nc' -> (c ^. context_id) .== (nc' ^. nc_context_id))
306 joinOn1 :: O.Select (NodeRead, MaybeFields NodeContextRead)
307 joinOn1 = proc () -> do
308 n <- queryNodeTable -< ()
309 nc <- optionalRestrict queryNodeContextTable -<
310 (\nc' -> (nc' ^. nc_node_id) .== (n ^. node_id))
314 ------------------------------------------------------------------------
315 selectPublicContexts :: HasDBid NodeType => (Hyperdata a, DefaultFromField SqlJsonb a)
316 => Cmd err [(Node a, Maybe Int)]
317 selectPublicContexts = runOpaQuery (queryWithType NodeFolderPublic)
319 queryWithType :: HasDBid NodeType => NodeType -> O.Select (NodeRead, MaybeFields (Field SqlInt4))
320 queryWithType nt = proc () -> do
321 (n, nc) <- joinOn1 -< ()
322 restrict -< (n ^. node_typename) .== sqlInt4 (toDBid nt)
323 returnA -< (n, view nc_context_id <$> nc)