2 Module : Gargantext.Database.TextSearch
3 Description : Postgres text search experimentation
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
11 {-# LANGUAGE Arrows #-}
13 module Gargantext.Database.Action.Search where
15 import Control.Arrow (returnA)
16 import Control.Lens ((^.))
18 import Data.Text (Text, unpack, intercalate)
19 import Data.Time (UTCTime)
20 import Gargantext.Core
21 import Gargantext.Core.Types
22 import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..), HyperdataContact(..))
23 import Gargantext.Database.Prelude (Cmd, runOpaQuery, runCountOpaQuery)
24 import Gargantext.Database.Query.Facet
25 import Gargantext.Database.Query.Filter
26 import Gargantext.Database.Query.Join (leftJoin5)
27 import Gargantext.Database.Query.Table.Node
28 import Gargantext.Database.Query.Table.Context
29 import Gargantext.Database.Query.Table.NodeContext
30 import Gargantext.Database.Query.Table.NodeContext_NodeContext
31 import Gargantext.Database.Schema.Node
32 import Gargantext.Database.Schema.Context
33 import Gargantext.Prelude
34 import Gargantext.Core.Text.Terms.Mono.Stem.En (stemIt)
35 import Opaleye hiding (Order)
36 import Data.Profunctor.Product (p4)
37 import qualified Opaleye as O hiding (Order)
39 ------------------------------------------------------------------------
40 searchDocInDatabase :: HasDBid NodeType
43 -> Cmd err [(NodeId, HyperdataDocument)]
44 searchDocInDatabase p t = runOpaQuery (queryDocInDatabase p t)
46 -- | Global search query where ParentId is Master Node Corpus Id
47 queryDocInDatabase :: ParentId -> Text -> O.Select (Column SqlInt4, Column SqlJsonb)
48 queryDocInDatabase _p q = proc () -> do
49 row <- queryNodeSearchTable -< ()
50 restrict -< (_ns_search row) @@ (sqlTSQuery (unpack q))
51 restrict -< (_ns_typename row) .== (sqlInt4 $ toDBid NodeDocument)
52 returnA -< (_ns_id row, _ns_hyperdata row)
54 ------------------------------------------------------------------------
55 -- | todo add limit and offset and order
56 searchInCorpus :: HasDBid NodeType
64 searchInCorpus cId t q o l order = runOpaQuery
65 $ filterWith o l order
70 searchCountInCorpus :: HasDBid NodeType
75 searchCountInCorpus cId t q = runCountOpaQuery
80 queryInCorpus :: HasDBid NodeType
84 -> O.Select FacetDocRead
85 queryInCorpus cId t q = proc () -> do
86 (c, nc) <- joinInCorpus -< ()
87 restrict -< (nc^.nc_node_id) .== (toNullable $ pgNodeId cId)
89 then (nc^.nc_category) .== (toNullable $ sqlInt4 0)
90 else (nc^.nc_category) .>= (toNullable $ sqlInt4 1)
91 restrict -< (c ^. cs_search) @@ (sqlTSQuery (unpack q))
92 restrict -< (c ^. cs_typename ) .== (sqlInt4 $ toDBid NodeDocument)
93 returnA -< FacetDoc { facetDoc_id = c^.cs_id
94 , facetDoc_created = c^.cs_date
95 , facetDoc_title = c^.cs_name
96 , facetDoc_hyperdata = c^.cs_hyperdata
97 , facetDoc_category = nc^.nc_category
98 , facetDoc_ngramCount = nc^.nc_score
99 , facetDoc_score = nc^.nc_score
102 joinInCorpus :: O.Select (ContextSearchRead, NodeContextReadNull)
103 joinInCorpus = leftJoin queryContextSearchTable queryNodeContextTable cond
105 cond :: (ContextSearchRead, NodeContextRead) -> Column SqlBool
106 cond (c, nc) = nc^.nc_context_id .== _cs_id c
108 ------------------------------------------------------------------------
109 searchInCorpusWithContacts
117 -> Cmd err [FacetPaired Int UTCTime HyperdataContact Int]
118 searchInCorpusWithContacts cId aId q o l _order =
119 runOpaQuery $ limit' l
121 $ orderBy (desc _fp_score)
122 $ selectGroup cId aId
126 selectGroup :: HasDBid NodeType
130 -> Select FacetPairedReadNull
131 selectGroup cId aId q = proc () -> do
132 (a, b, c, d) <- aggregate (p4 (groupBy, groupBy, groupBy, O.sum))
133 (selectContactViaDoc cId aId q) -< ()
134 returnA -< FacetPaired a b c d
143 ( Column (Nullable SqlInt4)
144 , Column (Nullable SqlTimestamptz)
145 , Column (Nullable SqlJsonb)
146 , Column (Nullable SqlInt4)
148 selectContactViaDoc cId aId query = proc () -> do
149 (doc, (corpus, (_nodeContext_nodeContext, (annuaire, contact)))) <- queryContactViaDoc -< ()
150 restrict -< (doc^.cs_search) @@ (sqlTSQuery $ unpack query )
151 restrict -< (doc^.cs_typename) .== (sqlInt4 $ toDBid NodeDocument )
152 restrict -< (corpus^.nc_node_id) .== (toNullable $ pgNodeId cId )
153 restrict -< (annuaire^.nc_node_id) .== (toNullable $ pgNodeId aId )
154 restrict -< (contact^.context_typename) .== (toNullable $ sqlInt4 $ toDBid NodeContact)
155 returnA -< ( contact^.context_id
156 , contact^.context_date
157 , contact^.context_hyperdata
158 , toNullable $ sqlInt4 1
161 queryContactViaDoc :: O.Select ( ContextSearchRead
162 , ( NodeContextReadNull
163 , ( NodeContext_NodeContextReadNull
164 , ( NodeContextReadNull
173 queryNodeContextTable
174 queryNodeContext_NodeContextTable
175 queryNodeContextTable
176 queryContextSearchTable
182 cond12 :: (NodeContextRead, ContextRead) -> Column SqlBool
183 cond12 (annuaire, contact) = contact^.context_id .== annuaire^.nc_context_id
185 cond23 :: ( NodeContext_NodeContextRead
190 cond23 (nodeContext_nodeContext, (annuaire, _)) = nodeContext_nodeContext^.ncnc_nodecontext2 .== annuaire^.nc_id
192 cond34 :: ( NodeContextRead
193 , ( NodeContext_NodeContextRead
194 , ( NodeContextReadNull
199 cond34 (corpus, (nodeContext_nodeContext, (_,_))) = nodeContext_nodeContext^.ncnc_nodecontext1 .== corpus^.nc_id
202 cond45 :: ( ContextSearchRead
204 , ( NodeContext_NodeContextReadNull
205 , ( NodeContextReadNull
211 cond45 (doc, (corpus, (_,(_,_)))) = doc^.cs_id .== corpus^.nc_context_id