2 Module : Gargantext.Database.Query.Facet
3 Description : Main requests of Node to the database
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
11 {-# OPTIONS_GHC -fno-warn-orphans #-}
14 {-# LANGUAGE Arrows #-}
15 {-# LANGUAGE FunctionalDependencies #-}
16 {-# LANGUAGE QuasiQuotes #-}
17 {-# LANGUAGE NoMonomorphismRestriction #-}
18 {-# LANGUAGE TemplateHaskell #-}
19 {-# LANGUAGE TypeFamilies #-}
20 ------------------------------------------------------------------------
21 module Gargantext.Database.Query.Facet
38 , FacetPairedReadNullAgg
43 import Control.Arrow (returnA)
44 import Control.Lens ((^.))
45 import Data.Aeson (FromJSON, ToJSON)
46 import Data.Aeson.TH (deriveJSON)
47 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
49 import qualified Data.Text as T
50 import Data.Time (UTCTime)
51 import Data.Time.Segment (jour)
53 import Protolude hiding (null, map, sum, not)
55 import Test.QuickCheck (elements)
56 import Test.QuickCheck.Arbitrary
57 import qualified Opaleye.Internal.Unpackspec()
59 import Gargantext.Core
60 import Gargantext.Core.Types
61 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger, wellNamedSchema)
62 import Gargantext.Database.Admin.Types.Hyperdata
63 import Gargantext.Database.Query.Filter
64 import Gargantext.Database.Query.Join (leftJoin5)
65 import Gargantext.Database.Query.Table.Ngrams
66 import Gargantext.Database.Query.Table.NodeNode
67 import Gargantext.Database.Query.Table.NodeNodeNgrams
68 import Gargantext.Database.Prelude
69 import Gargantext.Database.Schema.Node
71 ------------------------------------------------------------------------
74 -- data Facet = FacetDoc | FacetSources | FacetAuthors | FacetTerms
75 -- deriving (Show, Generic)
76 --instance FromJSON Facet
77 --instance ToJSON Facet
84 type FacetDoc = Facet NodeId UTCTime Title HyperdataDocument (Maybe Category) (Maybe Double) (Maybe Score)
85 -- type FacetSources = FacetDoc
86 -- type FacetAuthors = FacetDoc
87 -- type FacetTerms = FacetDoc
90 data Facet id created title hyperdata category ngramCount score =
91 FacetDoc { facetDoc_id :: id
92 , facetDoc_created :: created
93 , facetDoc_title :: title
94 , facetDoc_hyperdata :: hyperdata
95 , facetDoc_category :: category
96 , facetDoc_ngramCount :: ngramCount
97 , facetDoc_score :: score
98 } deriving (Show, Generic)
100 data Facet id date hyperdata score =
101 FacetDoc { facetDoc_id :: id
102 , facetDoc_date :: date
103 , facetDoc_hyperdata :: hyperdata
104 , facetDoc_score :: score
105 } deriving (Show, Generic)
108 data Pair i l = Pair {
111 } deriving (Show, Generic)
112 $(deriveJSON (unPrefix "_p_") ''Pair)
113 $(makeAdaptorAndInstance "pPair" ''Pair)
115 instance (Typeable i, Typeable l, ToSchema i, ToSchema l) => ToSchema (Pair i l) where
116 declareNamedSchema = wellNamedSchema "_p_"
117 instance (Arbitrary i, Arbitrary l) => Arbitrary (Pair i l) where
118 arbitrary = Pair <$> arbitrary <*> arbitrary
120 data FacetPaired id date hyperdata score =
121 FacetPaired {_fp_id :: id
123 ,_fp_hyperdata :: hyperdata
125 } deriving (Show, Generic)
126 $(deriveJSON (unPrefix "_fp_") ''FacetPaired)
127 $(makeAdaptorAndInstance "pFacetPaired" ''FacetPaired)
131 instance ( ToSchema id
139 ) => ToSchema (FacetPaired id date hyperdata score) where
140 declareNamedSchema = wellNamedSchema "_fp_"
142 instance ( Arbitrary id
144 , Arbitrary hyperdata
146 ) => Arbitrary (FacetPaired id date hyperdata score) where
147 arbitrary = FacetPaired <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
149 type FacetPairedRead = FacetPaired (Column PGInt4 )
150 (Column PGTimestamptz)
154 type FacetPairedReadNull = FacetPaired (Column (Nullable PGInt4) )
155 (Column (Nullable PGTimestamptz))
156 (Column (Nullable PGJsonb) )
157 (Column (Nullable PGInt4) )
159 type FacetPairedReadNullAgg = FacetPaired (Aggregator (Column (Nullable PGInt4) )
160 (Column (Nullable PGInt4) )
162 (Aggregator (Column (Nullable PGTimestamptz))
163 (Column (Nullable PGTimestamptz))
166 (Aggregator (Column (Nullable PGJsonb) )
167 (Column (Nullable PGJsonb) )
169 (Aggregator (Column (Nullable PGInt4) )
170 (Column (Nullable PGInt4) )
177 $(deriveJSON (unPrefix "facetDoc_") ''Facet)
179 -- | Documentation instance
180 instance ToSchema FacetDoc where
181 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "facetDoc_")
183 -- | Mock and Quickcheck instances
184 instance Arbitrary FacetDoc where
185 arbitrary = elements [ FacetDoc id' (jour year 01 01) t hp (Just cat) (Just ngramCount) (Just score)
187 , year <- [1990..2000]
188 , t <- ["title", "another title"]
189 , hp <- arbitraryHyperdataDocuments
191 , ngramCount <- [3..100]
195 -- Facets / Views for the Front End
196 -- | Database instances
197 $(makeAdaptorAndInstance "pFacetDoc" ''Facet)
198 -- $(makeLensesWith abbreviatedFields ''Facet)
200 type FacetDocRead = Facet (Column PGInt4 )
201 (Column PGTimestamptz)
204 (Column (Nullable PGInt4)) -- Category
205 (Column (Nullable PGFloat8)) -- Ngrams Count
206 (Column (Nullable PGFloat8)) -- Score
208 -----------------------------------------------------------------------
209 -----------------------------------------------------------------------
210 data OrderBy = DateAsc | DateDesc
211 | TitleAsc | TitleDesc
212 | ScoreDesc | ScoreAsc
213 | SourceAsc | SourceDesc
214 deriving (Generic, Enum, Bounded, Read, Show)
216 instance FromHttpApiData OrderBy
218 parseUrlPiece "DateAsc" = pure DateAsc
219 parseUrlPiece "DateDesc" = pure DateDesc
220 parseUrlPiece "TitleAsc" = pure TitleAsc
221 parseUrlPiece "TitleDesc" = pure TitleDesc
222 parseUrlPiece "ScoreAsc" = pure ScoreAsc
223 parseUrlPiece "ScoreDesc" = pure ScoreDesc
224 parseUrlPiece "SourceAsc" = pure SourceAsc
225 parseUrlPiece "SourceDesc" = pure SourceDesc
226 parseUrlPiece _ = Left "Unexpected value of OrderBy"
228 instance ToParamSchema OrderBy
229 instance FromJSON OrderBy
230 instance ToJSON OrderBy
231 instance ToSchema OrderBy
232 instance Arbitrary OrderBy
234 arbitrary = elements [minBound..maxBound]
237 -- TODO-SECURITY check
240 runViewAuthorsDoc :: HasDBid NodeType
246 -> Cmd err [FacetDoc]
247 runViewAuthorsDoc cId t o l order = runOpaQuery $ filterWith o l order $ viewAuthorsDoc cId t ntId
252 viewAuthorsDoc :: HasDBid NodeType
256 -> Query FacetDocRead
257 viewAuthorsDoc cId _ nt = proc () -> do
258 (doc,(_,(_,(_,contact')))) <- queryAuthorsDoc -< ()
260 {-nn <- queryNodeNodeTable -< ()
261 restrict -< nn_node1_id nn .== _node_id doc
262 -- restrict -< nn_delete nn .== (pgBool t)
265 restrict -< _node_id contact' .== (toNullable $ pgNodeId cId)
266 restrict -< _node_typename doc .== (pgInt4 $ toDBid nt)
268 returnA -< FacetDoc (_node_id doc)
271 (_node_hyperdata doc)
272 (toNullable $ pgInt4 1)
273 (toNullable $ pgDouble 1)
274 (toNullable $ pgDouble 1)
276 queryAuthorsDoc :: Query (NodeRead, (NodeNodeNgramsReadNull, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull))))
277 queryAuthorsDoc = leftJoin5 queryNodeTable queryNodeNodeNgramsTable queryNgramsTable queryNodeNodeNgramsTable queryNodeTable cond12 cond23 cond34 cond45
279 cond12 :: (NodeNodeNgramsRead, NodeRead) -> Column PGBool
280 cond12 (nodeNgram, doc) = _node_id doc
281 .== _nnng_node1_id nodeNgram
283 cond23 :: (NgramsRead, (NodeNodeNgramsRead, NodeReadNull)) -> Column PGBool
284 cond23 (ngrams', (nodeNgram, _)) = ngrams'^.ngrams_id
285 .== _nnng_ngrams_id nodeNgram
287 cond34 :: (NodeNodeNgramsRead, (NgramsRead, (NodeNodeNgramsReadNull, NodeReadNull))) -> Column PGBool
288 cond34 (nodeNgram2, (ngrams', (_,_)))= ngrams'^.ngrams_id .== _nnng_ngrams_id nodeNgram2
290 cond45 :: (NodeRead, (NodeNodeNgramsRead, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull)))) -> Column PGBool
291 cond45 (contact', (nodeNgram2', (_, (_,_)))) = _node_id contact' .== _nnng_node1_id nodeNgram2'
294 ------------------------------------------------------------------------
296 -- TODO-SECURITY check
297 runViewDocuments :: HasDBid NodeType
304 -> Cmd err [FacetDoc]
305 runViewDocuments cId t o l order query = do
306 runOpaQuery $ filterWith o l order sqlQuery
308 ntId = toDBid NodeDocument
309 sqlQuery = viewDocuments cId t ntId query
311 runCountDocuments :: HasDBid NodeType => CorpusId -> IsTrash -> Maybe Text -> Cmd err Int
312 runCountDocuments cId t mQuery = do
313 runCountOpaQuery sqlQuery
315 sqlQuery = viewDocuments cId t (toDBid NodeDocument) mQuery
318 viewDocuments :: CorpusId
322 -> Query FacetDocRead
323 viewDocuments cId t ntId mQuery = proc () -> do
324 n <- queryNodeTable -< ()
325 nn <- queryNodeNodeTable -< ()
326 restrict -< n^.node_id .== nn^.nn_node2_id
327 restrict -< nn^.nn_node1_id .== (pgNodeId cId)
328 restrict -< n^.node_typename .== (pgInt4 ntId)
329 restrict -< if t then nn^.nn_category .== (pgInt4 0)
330 else nn^.nn_category .>= (pgInt4 1)
332 let query = (fromMaybe "" mQuery)
333 iLikeQuery = T.intercalate "" ["%", query, "%"]
334 restrict -< (n^.node_name) `ilike` (pgStrictText iLikeQuery)
336 returnA -< FacetDoc (_node_id n)
340 (toNullable $ nn^.nn_category)
341 (toNullable $ nn^.nn_score)
342 (toNullable $ nn^.nn_score)
344 ------------------------------------------------------------------------
345 filterWith :: (PGOrd date, PGOrd title, PGOrd category, PGOrd score, hyperdata ~ Column SqlJsonb) =>
346 Maybe Gargantext.Core.Types.Offset
347 -> Maybe Gargantext.Core.Types.Limit
349 -> Select (Facet id (Column date) (Column title) hyperdata (Column category) ngramCount (Column score))
350 -> Select (Facet id (Column date) (Column title) hyperdata (Column category) ngramCount (Column score))
351 filterWith o l order q = limit' l $ offset' o $ orderBy (orderWith order) q
354 orderWith :: (PGOrd b1, PGOrd b2, PGOrd b3, PGOrd b4)
356 -> Order (Facet id (Column b1) (Column b2) (Column SqlJsonb) (Column b3) ngramCount (Column b4))
357 orderWith (Just DateAsc) = asc facetDoc_created
358 orderWith (Just DateDesc) = desc facetDoc_created
360 orderWith (Just TitleAsc) = asc facetDoc_title
361 orderWith (Just TitleDesc) = desc facetDoc_title
363 orderWith (Just ScoreAsc) = asc facetDoc_score
364 orderWith (Just ScoreDesc) = descNullsLast facetDoc_score
366 orderWith (Just SourceAsc) = asc facetDoc_source
367 orderWith (Just SourceDesc) = desc facetDoc_source
369 orderWith _ = asc facetDoc_created
371 facetDoc_source :: PGIsJson a
372 => Facet id created title (Column a) favorite ngramCount score
373 -> Column (Nullable PGText)
374 facetDoc_source x = toNullable (facetDoc_hyperdata x) .->> pgString "source"