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 #-}
12 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
14 {-# LANGUAGE Arrows #-}
15 {-# LANGUAGE DeriveGeneric #-}
16 {-# LANGUAGE FlexibleContexts #-}
17 {-# LANGUAGE FlexibleInstances #-}
18 {-# LANGUAGE FunctionalDependencies #-}
19 {-# LANGUAGE MultiParamTypeClasses #-}
20 {-# LANGUAGE QuasiQuotes #-}
21 {-# LANGUAGE NoImplicitPrelude #-}
22 {-# LANGUAGE NoMonomorphismRestriction #-}
23 {-# LANGUAGE OverloadedStrings #-}
24 {-# LANGUAGE RankNTypes #-}
25 {-# LANGUAGE TemplateHaskell #-}
26 {-# LANGUAGE TypeFamilies #-}
27 ------------------------------------------------------------------------
28 module Gargantext.Database.Query.Facet
43 import Control.Arrow (returnA)
44 import Control.Lens ((^.))
45 import Data.Aeson (FromJSON, ToJSON)
46 import Data.Aeson.TH (deriveJSON)
47 import Data.Either(Either(Left))
48 import Data.Maybe (Maybe)
49 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
51 import Data.Text (Text)
52 import Data.Time (UTCTime)
53 import Data.Time.Segment (jour)
54 import GHC.Generics (Generic)
55 import Gargantext.Core.Types
56 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger)
57 import Gargantext.Database.Query.Filter
58 import Gargantext.Database.Query.Join (leftJoin5)
59 import Gargantext.Database.Query.Table.Ngrams
60 import Gargantext.Database.Admin.Config (nodeTypeId)
61 import Gargantext.Database.Admin.Utils
62 import Gargantext.Database.Schema.Ngrams
63 import Gargantext.Database.Schema.Node
64 import Gargantext.Database.Query.Table.NodeNode
65 import Gargantext.Database.Query.Table.NodeNodeNgrams
67 import Prelude hiding (null, id, map, sum, not, read)
69 import Test.QuickCheck (elements)
70 import Test.QuickCheck.Arbitrary
71 import qualified Opaleye.Internal.Unpackspec()
73 ------------------------------------------------------------------------
76 -- data Facet = FacetDoc | FacetSources | FacetAuthors | FacetTerms
77 -- deriving (Show, Generic)
78 --instance FromJSON Facet
79 --instance ToJSON Facet
85 type FacetDoc = Facet NodeId UTCTime Title HyperdataDocument (Maybe Favorite) (Maybe Double)
86 -- type FacetSources = FacetDoc
87 -- type FacetAuthors = FacetDoc
88 -- type FacetTerms = FacetDoc
91 data Facet id created title hyperdata favorite ngramCount =
92 FacetDoc { facetDoc_id :: id
93 , facetDoc_created :: created
94 , facetDoc_title :: title
95 , facetDoc_hyperdata :: hyperdata
96 , facetDoc_favorite :: favorite
97 , facetDoc_ngramCount :: ngramCount
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 {_p_id :: i
110 } deriving (Show, Generic)
111 $(deriveJSON (unPrefix "_p_") ''Pair)
112 $(makeAdaptorAndInstance "pPair" ''Pair)
114 instance (ToSchema i, ToSchema l) => ToSchema (Pair i l) where
115 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_p_")
116 instance (Arbitrary i, Arbitrary l) => Arbitrary (Pair i l) where
117 arbitrary = Pair <$> arbitrary <*> arbitrary
119 data FacetPaired id date hyperdata score pair =
120 FacetPaired {_fp_id :: id
122 ,_fp_hyperdata :: hyperdata
125 } deriving (Show, Generic)
126 $(deriveJSON (unPrefix "_fp_") ''FacetPaired)
127 $(makeAdaptorAndInstance "pFacetPaired" ''FacetPaired)
129 instance ( ToSchema id
134 ) => ToSchema (FacetPaired id date hyperdata score pair) where
135 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_fp_")
137 instance ( Arbitrary id
139 , Arbitrary hyperdata
142 ) => Arbitrary (FacetPaired id date hyperdata score pair) where
143 arbitrary = FacetPaired <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
145 type FacetPairedRead = FacetPaired (Column PGInt4 )
146 (Column PGTimestamptz)
149 ( Column (Nullable PGInt4)
150 , Column (Nullable PGText)
154 $(deriveJSON (unPrefix "facetDoc_") ''Facet)
156 -- | Documentation instance
157 instance ToSchema FacetDoc where
158 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "facetDoc_")
160 -- | Mock and Quickcheck instances
161 instance Arbitrary FacetDoc where
162 arbitrary = elements [ FacetDoc id' (jour year 01 01) t hp (Just cat) (Just ngramCount)
164 , year <- [1990..2000]
165 , t <- ["title", "another title"]
166 , hp <- arbitraryHyperdataDocuments
168 , ngramCount <- [3..100]
171 -- Facets / Views for the Front End
172 -- | Database instances
173 $(makeAdaptorAndInstance "pFacetDoc" ''Facet)
174 -- $(makeLensesWith abbreviatedFields ''Facet)
176 type FacetDocRead = Facet (Column PGInt4 )
177 (Column PGTimestamptz)
180 (Column (Nullable PGInt4)) -- Category
181 (Column (Nullable PGFloat8)) -- Score
183 -----------------------------------------------------------------------
184 -----------------------------------------------------------------------
185 data OrderBy = DateAsc | DateDesc
186 | TitleAsc | TitleDesc
187 | ScoreDesc | ScoreAsc
188 | SourceAsc | SourceDesc
189 deriving (Generic, Enum, Bounded, Read, Show)
191 instance FromHttpApiData OrderBy
193 parseUrlPiece "DateAsc" = pure DateAsc
194 parseUrlPiece "DateDesc" = pure DateDesc
195 parseUrlPiece "TitleAsc" = pure TitleAsc
196 parseUrlPiece "TitleDesc" = pure TitleDesc
197 parseUrlPiece "ScoreAsc" = pure ScoreAsc
198 parseUrlPiece "ScoreDesc" = pure ScoreDesc
199 parseUrlPiece "SourceAsc" = pure SourceAsc
200 parseUrlPiece "SourceDesc" = pure SourceDesc
201 parseUrlPiece _ = Left "Unexpected value of OrderBy"
203 instance ToParamSchema OrderBy
204 instance FromJSON OrderBy
205 instance ToJSON OrderBy
206 instance ToSchema OrderBy
207 instance Arbitrary OrderBy
209 arbitrary = elements [minBound..maxBound]
212 -- TODO-SECURITY check
215 runViewAuthorsDoc :: ContactId -> IsTrash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
216 runViewAuthorsDoc cId t o l order = runOpaQuery $ filterWith o l order $ viewAuthorsDoc cId t ntId
221 viewAuthorsDoc :: ContactId -> IsTrash -> NodeType -> Query FacetDocRead
222 viewAuthorsDoc cId _ nt = proc () -> do
223 (doc,(_,(_,(_,contact)))) <- queryAuthorsDoc -< ()
225 {-nn <- queryNodeNodeTable -< ()
226 restrict -< nn_node1_id nn .== _node_id doc
227 -- restrict -< nn_delete nn .== (pgBool t)
230 restrict -< _node_id contact .== (toNullable $ pgNodeId cId)
231 restrict -< _node_typename doc .== (pgInt4 $ nodeTypeId nt)
233 returnA -< FacetDoc (_node_id doc)
236 (_node_hyperdata doc)
237 (toNullable $ pgInt4 1)
238 (toNullable $ pgDouble 1)
240 queryAuthorsDoc :: Query (NodeRead, (NodeNodeNgramsReadNull, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull))))
241 queryAuthorsDoc = leftJoin5 queryNodeTable queryNodeNodeNgramsTable queryNgramsTable queryNodeNodeNgramsTable queryNodeTable cond12 cond23 cond34 cond45
243 cond12 :: (NodeNodeNgramsRead, NodeRead) -> Column PGBool
244 cond12 (nodeNgram, doc) = _node_id doc
245 .== _nnng_node1_id nodeNgram
247 cond23 :: (NgramsRead, (NodeNodeNgramsRead, NodeReadNull)) -> Column PGBool
248 cond23 (ngrams, (nodeNgram, _)) = ngrams^.ngrams_id
249 .== _nnng_ngrams_id nodeNgram
251 cond34 :: (NodeNodeNgramsRead, (NgramsRead, (NodeNodeNgramsReadNull, NodeReadNull))) -> Column PGBool
252 cond34 (nodeNgram2, (ngrams, (_,_)))= ngrams^.ngrams_id .== _nnng_ngrams_id nodeNgram2
254 cond45 :: (NodeRead, (NodeNodeNgramsRead, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull)))) -> Column PGBool
255 cond45 (contact, (nodeNgram2, (_, (_,_)))) = _node_id contact .== _nnng_node1_id nodeNgram2
258 ------------------------------------------------------------------------
260 -- TODO-SECURITY check
261 runViewDocuments :: CorpusId -> IsTrash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
262 runViewDocuments cId t o l order =
263 runOpaQuery $ filterWith o l order $ viewDocuments cId t ntId
265 ntId = nodeTypeId NodeDocument
267 viewDocuments :: CorpusId -> IsTrash -> NodeTypeId -> Query FacetDocRead
268 viewDocuments cId t ntId = proc () -> do
269 n <- queryNodeTable -< ()
270 nn <- queryNodeNodeTable -< ()
271 restrict -< n^.node_id .== nn^.nn_node2_id
272 restrict -< nn^.nn_node1_id .== (pgNodeId cId)
273 restrict -< n^.node_typename .== (pgInt4 ntId)
274 restrict -< if t then nn^.nn_category .== (pgInt4 0)
275 else nn^.nn_category .>= (pgInt4 1)
276 returnA -< FacetDoc (_node_id n)
280 (toNullable $ nn^.nn_category)
281 (toNullable $ nn^.nn_score)
283 ------------------------------------------------------------------------
284 filterWith :: (PGOrd date, PGOrd title, PGOrd score, hyperdata ~ Column SqlJsonb) =>
285 Maybe Gargantext.Core.Types.Offset
286 -> Maybe Gargantext.Core.Types.Limit
288 -> Select (Facet id (Column date) (Column title) hyperdata (Column score) ngramCount)
289 -> Select (Facet id (Column date) (Column title) hyperdata (Column score) ngramCount)
290 filterWith o l order q = limit' l $ offset' o $ orderBy (orderWith order) q
293 orderWith :: (PGOrd b1, PGOrd b2, PGOrd b3)
295 -> Order (Facet id (Column b1) (Column b2) (Column SqlJsonb) (Column b3) score)
296 orderWith (Just DateAsc) = asc facetDoc_created
297 orderWith (Just DateDesc) = desc facetDoc_created
299 orderWith (Just TitleAsc) = asc facetDoc_title
300 orderWith (Just TitleDesc) = desc facetDoc_title
302 orderWith (Just ScoreAsc) = asc facetDoc_favorite
303 orderWith (Just ScoreDesc) = desc facetDoc_favorite
305 orderWith (Just SourceAsc) = asc facetDoc_source
306 orderWith (Just SourceDesc) = desc facetDoc_source
308 orderWith _ = asc facetDoc_created
310 facetDoc_source :: PGIsJson a
311 => Facet id created title (Column a) favorite ngramCount
312 -> Column (Nullable PGText)
313 facetDoc_source x = toNullable (facetDoc_hyperdata x) .->> pgString "source"