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 FunctionalDependencies #-}
16 {-# LANGUAGE QuasiQuotes #-}
17 {-# LANGUAGE NoMonomorphismRestriction #-}
18 {-# LANGUAGE TemplateHaskell #-}
19 {-# LANGUAGE TypeFamilies #-}
20 ------------------------------------------------------------------------
21 module Gargantext.Database.Query.Facet
36 import Control.Arrow (returnA)
37 import Control.Lens ((^.))
38 import Data.Aeson (FromJSON, ToJSON)
39 import Data.Aeson.TH (deriveJSON)
40 import Data.Either(Either(Left))
41 import Data.Maybe (Maybe)
42 import Data.Profunctor.Product.TH (makeAdaptorAndInstance)
44 import Data.Text (Text)
45 import Data.Time (UTCTime)
46 import Data.Time.Segment (jour)
47 import GHC.Generics (Generic)
49 import Prelude hiding (null, id, map, sum, not, read)
51 import Test.QuickCheck (elements)
52 import Test.QuickCheck.Arbitrary
53 import qualified Opaleye.Internal.Unpackspec()
55 import Gargantext.Core.Types
56 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger)
57 import Gargantext.Database.Admin.Config (nodeTypeId)
58 import Gargantext.Database.Admin.Types.Hyperdata
59 import Gargantext.Database.Query.Filter
60 import Gargantext.Database.Query.Join (leftJoin5)
61 import Gargantext.Database.Query.Table.Ngrams
62 import Gargantext.Database.Query.Table.NodeNode
63 import Gargantext.Database.Query.Table.NodeNodeNgrams
64 import Gargantext.Database.Prelude
65 import Gargantext.Database.Schema.Node
67 ------------------------------------------------------------------------
70 -- data Facet = FacetDoc | FacetSources | FacetAuthors | FacetTerms
71 -- deriving (Show, Generic)
72 --instance FromJSON Facet
73 --instance ToJSON Facet
79 type FacetDoc = Facet NodeId UTCTime Title HyperdataDocument (Maybe Favorite) (Maybe Double)
80 -- type FacetSources = FacetDoc
81 -- type FacetAuthors = FacetDoc
82 -- type FacetTerms = FacetDoc
85 data Facet id created title hyperdata favorite ngramCount =
86 FacetDoc { facetDoc_id :: id
87 , facetDoc_created :: created
88 , facetDoc_title :: title
89 , facetDoc_hyperdata :: hyperdata
90 , facetDoc_favorite :: favorite
91 , facetDoc_ngramCount :: ngramCount
92 } deriving (Show, Generic)
94 data Facet id date hyperdata score =
95 FacetDoc { facetDoc_id :: id
96 , facetDoc_date :: date
97 , facetDoc_hyperdata :: hyperdata
98 , facetDoc_score :: score
99 } deriving (Show, Generic)
102 data Pair i l = Pair {_p_id :: i
104 } deriving (Show, Generic)
105 $(deriveJSON (unPrefix "_p_") ''Pair)
106 $(makeAdaptorAndInstance "pPair" ''Pair)
108 instance (ToSchema i, ToSchema l) => ToSchema (Pair i l) where
109 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_p_")
110 instance (Arbitrary i, Arbitrary l) => Arbitrary (Pair i l) where
111 arbitrary = Pair <$> arbitrary <*> arbitrary
113 data FacetPaired id date hyperdata score pair =
114 FacetPaired {_fp_id :: id
116 ,_fp_hyperdata :: hyperdata
119 } deriving (Show, Generic)
120 $(deriveJSON (unPrefix "_fp_") ''FacetPaired)
121 $(makeAdaptorAndInstance "pFacetPaired" ''FacetPaired)
123 instance ( ToSchema id
128 ) => ToSchema (FacetPaired id date hyperdata score pair) where
129 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_fp_")
131 instance ( Arbitrary id
133 , Arbitrary hyperdata
136 ) => Arbitrary (FacetPaired id date hyperdata score pair) where
137 arbitrary = FacetPaired <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
139 type FacetPairedRead = FacetPaired (Column PGInt4 )
140 (Column PGTimestamptz)
143 ( Column (Nullable PGInt4)
144 , Column (Nullable PGText)
148 $(deriveJSON (unPrefix "facetDoc_") ''Facet)
150 -- | Documentation instance
151 instance ToSchema FacetDoc where
152 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "facetDoc_")
154 -- | Mock and Quickcheck instances
155 instance Arbitrary FacetDoc where
156 arbitrary = elements [ FacetDoc id' (jour year 01 01) t hp (Just cat) (Just ngramCount)
158 , year <- [1990..2000]
159 , t <- ["title", "another title"]
160 , hp <- arbitraryHyperdataDocuments
162 , ngramCount <- [3..100]
165 -- Facets / Views for the Front End
166 -- | Database instances
167 $(makeAdaptorAndInstance "pFacetDoc" ''Facet)
168 -- $(makeLensesWith abbreviatedFields ''Facet)
170 type FacetDocRead = Facet (Column PGInt4 )
171 (Column PGTimestamptz)
174 (Column (Nullable PGInt4)) -- Category
175 (Column (Nullable PGFloat8)) -- Score
177 -----------------------------------------------------------------------
178 -----------------------------------------------------------------------
179 data OrderBy = DateAsc | DateDesc
180 | TitleAsc | TitleDesc
181 | ScoreDesc | ScoreAsc
182 | SourceAsc | SourceDesc
183 deriving (Generic, Enum, Bounded, Read, Show)
185 instance FromHttpApiData OrderBy
187 parseUrlPiece "DateAsc" = pure DateAsc
188 parseUrlPiece "DateDesc" = pure DateDesc
189 parseUrlPiece "TitleAsc" = pure TitleAsc
190 parseUrlPiece "TitleDesc" = pure TitleDesc
191 parseUrlPiece "ScoreAsc" = pure ScoreAsc
192 parseUrlPiece "ScoreDesc" = pure ScoreDesc
193 parseUrlPiece "SourceAsc" = pure SourceAsc
194 parseUrlPiece "SourceDesc" = pure SourceDesc
195 parseUrlPiece _ = Left "Unexpected value of OrderBy"
197 instance ToParamSchema OrderBy
198 instance FromJSON OrderBy
199 instance ToJSON OrderBy
200 instance ToSchema OrderBy
201 instance Arbitrary OrderBy
203 arbitrary = elements [minBound..maxBound]
206 -- TODO-SECURITY check
209 runViewAuthorsDoc :: ContactId -> IsTrash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
210 runViewAuthorsDoc cId t o l order = runOpaQuery $ filterWith o l order $ viewAuthorsDoc cId t ntId
215 viewAuthorsDoc :: ContactId -> IsTrash -> NodeType -> Query FacetDocRead
216 viewAuthorsDoc cId _ nt = proc () -> do
217 (doc,(_,(_,(_,contact)))) <- queryAuthorsDoc -< ()
219 {-nn <- queryNodeNodeTable -< ()
220 restrict -< nn_node1_id nn .== _node_id doc
221 -- restrict -< nn_delete nn .== (pgBool t)
224 restrict -< _node_id contact .== (toNullable $ pgNodeId cId)
225 restrict -< _node_typename doc .== (pgInt4 $ nodeTypeId nt)
227 returnA -< FacetDoc (_node_id doc)
230 (_node_hyperdata doc)
231 (toNullable $ pgInt4 1)
232 (toNullable $ pgDouble 1)
234 queryAuthorsDoc :: Query (NodeRead, (NodeNodeNgramsReadNull, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull))))
235 queryAuthorsDoc = leftJoin5 queryNodeTable queryNodeNodeNgramsTable queryNgramsTable queryNodeNodeNgramsTable queryNodeTable cond12 cond23 cond34 cond45
237 cond12 :: (NodeNodeNgramsRead, NodeRead) -> Column PGBool
238 cond12 (nodeNgram, doc) = _node_id doc
239 .== _nnng_node1_id nodeNgram
241 cond23 :: (NgramsRead, (NodeNodeNgramsRead, NodeReadNull)) -> Column PGBool
242 cond23 (ngrams, (nodeNgram, _)) = ngrams^.ngrams_id
243 .== _nnng_ngrams_id nodeNgram
245 cond34 :: (NodeNodeNgramsRead, (NgramsRead, (NodeNodeNgramsReadNull, NodeReadNull))) -> Column PGBool
246 cond34 (nodeNgram2, (ngrams, (_,_)))= ngrams^.ngrams_id .== _nnng_ngrams_id nodeNgram2
248 cond45 :: (NodeRead, (NodeNodeNgramsRead, (NgramsReadNull, (NodeNodeNgramsReadNull, NodeReadNull)))) -> Column PGBool
249 cond45 (contact, (nodeNgram2, (_, (_,_)))) = _node_id contact .== _nnng_node1_id nodeNgram2
252 ------------------------------------------------------------------------
254 -- TODO-SECURITY check
255 runViewDocuments :: CorpusId -> IsTrash -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Cmd err [FacetDoc]
256 runViewDocuments cId t o l order =
257 runOpaQuery $ filterWith o l order $ viewDocuments cId t ntId
259 ntId = nodeTypeId NodeDocument
261 viewDocuments :: CorpusId -> IsTrash -> NodeTypeId -> Query FacetDocRead
262 viewDocuments cId t ntId = proc () -> do
263 n <- queryNodeTable -< ()
264 nn <- queryNodeNodeTable -< ()
265 restrict -< n^.node_id .== nn^.nn_node2_id
266 restrict -< nn^.nn_node1_id .== (pgNodeId cId)
267 restrict -< n^.node_typename .== (pgInt4 ntId)
268 restrict -< if t then nn^.nn_category .== (pgInt4 0)
269 else nn^.nn_category .>= (pgInt4 1)
270 returnA -< FacetDoc (_node_id n)
274 (toNullable $ nn^.nn_category)
275 (toNullable $ nn^.nn_score)
277 ------------------------------------------------------------------------
278 filterWith :: (PGOrd date, PGOrd title, PGOrd score, hyperdata ~ Column SqlJsonb) =>
279 Maybe Gargantext.Core.Types.Offset
280 -> Maybe Gargantext.Core.Types.Limit
282 -> Select (Facet id (Column date) (Column title) hyperdata (Column score) ngramCount)
283 -> Select (Facet id (Column date) (Column title) hyperdata (Column score) ngramCount)
284 filterWith o l order q = limit' l $ offset' o $ orderBy (orderWith order) q
287 orderWith :: (PGOrd b1, PGOrd b2, PGOrd b3)
289 -> Order (Facet id (Column b1) (Column b2) (Column SqlJsonb) (Column b3) score)
290 orderWith (Just DateAsc) = asc facetDoc_created
291 orderWith (Just DateDesc) = desc facetDoc_created
293 orderWith (Just TitleAsc) = asc facetDoc_title
294 orderWith (Just TitleDesc) = desc facetDoc_title
296 orderWith (Just ScoreAsc) = asc facetDoc_favorite
297 orderWith (Just ScoreDesc) = desc facetDoc_favorite
299 orderWith (Just SourceAsc) = asc facetDoc_source
300 orderWith (Just SourceDesc) = desc facetDoc_source
302 orderWith _ = asc facetDoc_created
304 facetDoc_source :: PGIsJson a
305 => Facet id created title (Column a) favorite ngramCount
306 -> Column (Nullable PGText)
307 facetDoc_source x = toNullable (facetDoc_hyperdata x) .->> pgString "source"