1 {-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
3 Module : Gargantext.API.Ngrams
4 Description : Server API
5 Copyright : (c) CNRS, 2017-Present
6 License : AGPL + CECILL v3
7 Maintainer : team@gargantext.org
8 Stability : experimental
14 get ngrams filtered by NgramsType
19 {-# LANGUAGE ConstraintKinds #-}
20 {-# LANGUAGE ScopedTypeVariables #-}
21 {-# LANGUAGE TemplateHaskell #-}
22 {-# LANGUAGE TypeOperators #-}
23 {-# LANGUAGE TypeFamilies #-}
24 {-# OPTIONS -fno-warn-orphans #-}
26 module Gargantext.API.Ngrams
33 --, rmListNgrams TODO fix before exporting
36 , apiNgramsTableCorpus
60 , NgramsRepoElement(..)
69 , ngramsTypeFromTabType
87 , listNgramsChangedSince
91 import Codec.Serialise (Serialise())
92 import Control.Category ((>>>))
93 import Control.Concurrent
94 import Control.Lens (makeLenses, makePrisms, Getter, Iso', iso, from, (.~), (?=), (#), to, folded, {-withIndex, ifolded,-} view, use, (^.), (^..), (^?), (+~), (%~), (.~), (%=), sumOf, at, _Just, Each(..), itraverse_, both, forOf_, (%%~), (?~), mapped)
95 import Control.Monad.Base (MonadBase, liftBase)
96 import Control.Monad.Error.Class (MonadError)
97 import Control.Monad.Reader
98 import Control.Monad.State
99 import Control.Monad.Trans.Control (MonadBaseControl)
100 import Data.Aeson hiding ((.=))
101 import Data.Aeson.TH (deriveJSON)
102 import qualified Data.Aeson.Text as DAT
103 import Data.Either (Either(..))
105 import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
106 import qualified Data.List as List
107 import Data.Map.Strict (Map)
108 import qualified Data.Map.Strict as Map
109 import qualified Data.Map.Strict.Patch as PM
110 import Data.Maybe (fromMaybe)
112 import Data.Ord (Down(..))
113 import Data.Patch.Class (Replace, replace, Action(act), Group, Applicable(..), Composable(..), Transformable(..),
114 PairPatch(..), Patched, ConflictResolution, ConflictResolutionReplace, ours,
115 MaybePatch(Mod), unMod, old, new)
116 import Data.Set (Set)
117 import qualified Data.Set as S
118 import qualified Data.Set as Set
119 import Data.String (IsString, fromString)
120 import Data.Swagger hiding (version, patch)
121 import Data.Text (Text, count, isInfixOf, pack, strip, unpack)
122 import Data.Text.Lazy.IO as DTL
124 import Database.PostgreSQL.Simple.FromField (FromField, fromField, ResultError(ConversionFailed), returnError)
125 import Formatting (hprint, int, (%))
126 import Formatting.Clock (timeSpecs)
127 import GHC.Generics (Generic)
128 import Servant hiding (Patch)
129 import System.Clock (getTime, TimeSpec, Clock(..))
130 import System.FileLock (FileLock)
131 import System.IO (stderr)
132 import Test.QuickCheck (elements, frequency)
133 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
135 import Prelude (error)
136 import Protolude (maybeToEither)
137 import Gargantext.Prelude
139 import Gargantext.Core.Types (ListType(..), NodeId, ListId, DocId, Limit, Offset, HasInvalidError, assertValid)
140 import Gargantext.Core.Types (TODO)
141 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixUntagged, unPrefixSwagger, wellNamedSchema)
142 import Gargantext.Database.Action.Metrics.NgramsByNode (getOccByNgramsOnlyFast')
143 import Gargantext.Database.Query.Table.Node.Select
144 import Gargantext.Database.Query.Table.Ngrams hiding (NgramsType(..), ngrams, ngramsType, ngrams_terms)
145 import Gargantext.Database.Admin.Config (userMaster)
146 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
147 import Gargantext.Database.Admin.Types.Node (NodeType(..))
148 import Gargantext.Database.Prelude (fromField', HasConnectionPool, HasConfig)
149 import qualified Gargantext.Database.Query.Table.Ngrams as TableNgrams
151 ------------------------------------------------------------------------
152 --data FacetFormat = Table | Chart
153 data TabType = Docs | Trash | MoreFav | MoreTrash
154 | Terms | Sources | Authors | Institutes
156 deriving (Generic, Enum, Bounded, Show)
158 instance FromHttpApiData TabType
160 parseUrlPiece "Docs" = pure Docs
161 parseUrlPiece "Trash" = pure Trash
162 parseUrlPiece "MoreFav" = pure MoreFav
163 parseUrlPiece "MoreTrash" = pure MoreTrash
165 parseUrlPiece "Terms" = pure Terms
166 parseUrlPiece "Sources" = pure Sources
167 parseUrlPiece "Institutes" = pure Institutes
168 parseUrlPiece "Authors" = pure Authors
170 parseUrlPiece "Contacts" = pure Contacts
172 parseUrlPiece _ = Left "Unexpected value of TabType"
174 instance ToParamSchema TabType
175 instance ToJSON TabType
176 instance FromJSON TabType
177 instance ToSchema TabType
178 instance Arbitrary TabType
180 arbitrary = elements [minBound .. maxBound]
182 newtype MSet a = MSet (Map a ())
183 deriving (Eq, Ord, Show, Generic, Arbitrary, Semigroup, Monoid)
185 instance ToJSON a => ToJSON (MSet a) where
186 toJSON (MSet m) = toJSON (Map.keys m)
187 toEncoding (MSet m) = toEncoding (Map.keys m)
189 mSetFromSet :: Set a -> MSet a
190 mSetFromSet = MSet . Map.fromSet (const ())
192 mSetFromList :: Ord a => [a] -> MSet a
193 mSetFromList = MSet . Map.fromList . map (\x -> (x, ()))
195 -- mSetToSet :: Ord a => MSet a -> Set a
196 -- mSetToSet (MSet a) = Set.fromList ( Map.keys a)
197 mSetToSet :: Ord a => MSet a -> Set a
198 mSetToSet = Set.fromList . mSetToList
200 mSetToList :: MSet a -> [a]
201 mSetToList (MSet a) = Map.keys a
203 instance Foldable MSet where
204 foldMap f (MSet m) = Map.foldMapWithKey (\k _ -> f k) m
206 instance (Ord a, FromJSON a) => FromJSON (MSet a) where
207 parseJSON = fmap mSetFromList . parseJSON
209 instance (ToJSONKey a, ToSchema a) => ToSchema (MSet a) where
211 declareNamedSchema _ = wellNamedSchema "" (Proxy :: Proxy TODO)
213 ------------------------------------------------------------------------
214 newtype NgramsTerm = NgramsTerm { unNgramsTerm :: Text }
215 deriving (Ord, Eq, Show, Generic, ToJSONKey, ToJSON, FromJSON, Semigroup, Arbitrary, Serialise, ToSchema)
217 instance FromJSONKey NgramsTerm where
218 fromJSONKey = FromJSONKeyTextParser $ \t -> pure $ NgramsTerm $ strip t
220 instance IsString NgramsTerm where
221 fromString s = NgramsTerm $ pack s
223 instance FromField NgramsTerm
225 fromField field mb = do
226 v <- fromField field mb
228 Success a -> pure $ NgramsTerm $ strip a
229 Error _err -> returnError ConversionFailed field
230 $ List.intercalate " " [ "cannot parse hyperdata for JSON: "
234 data RootParent = RootParent
235 { _rp_root :: NgramsTerm
236 , _rp_parent :: NgramsTerm
238 deriving (Ord, Eq, Show, Generic)
240 deriveJSON (unPrefix "_rp_") ''RootParent
241 makeLenses ''RootParent
243 data NgramsRepoElement = NgramsRepoElement
245 , _nre_list :: ListType
246 --, _nre_root_parent :: Maybe RootParent
247 , _nre_root :: Maybe NgramsTerm
248 , _nre_parent :: Maybe NgramsTerm
249 , _nre_children :: MSet NgramsTerm
251 deriving (Ord, Eq, Show, Generic)
253 deriveJSON (unPrefix "_nre_") ''NgramsRepoElement
255 -- if ngrams & not size => size
258 makeLenses ''NgramsRepoElement
260 instance ToSchema NgramsRepoElement where
261 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_nre_")
263 instance Serialise (MSet NgramsTerm)
264 instance Serialise NgramsRepoElement
267 NgramsElement { _ne_ngrams :: NgramsTerm
269 , _ne_list :: ListType
270 , _ne_occurrences :: Int
271 , _ne_root :: Maybe NgramsTerm
272 , _ne_parent :: Maybe NgramsTerm
273 , _ne_children :: MSet NgramsTerm
275 deriving (Ord, Eq, Show, Generic)
277 deriveJSON (unPrefix "_ne_") ''NgramsElement
278 makeLenses ''NgramsElement
280 mkNgramsElement :: NgramsTerm
285 mkNgramsElement ngrams list rp children =
286 NgramsElement ngrams size list 1 (_rp_root <$> rp) (_rp_parent <$> rp) children
289 size = 1 + (count " " $ unNgramsTerm ngrams)
291 newNgramsElement :: Maybe ListType -> NgramsTerm -> NgramsElement
292 newNgramsElement mayList ngrams =
293 mkNgramsElement ngrams (fromMaybe MapTerm mayList) Nothing mempty
295 instance ToSchema NgramsElement where
296 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_ne_")
297 instance Arbitrary NgramsElement where
298 arbitrary = elements [newNgramsElement Nothing "sport"]
300 ngramsElementToRepo :: NgramsElement -> NgramsRepoElement
302 (NgramsElement { _ne_size = s
316 ngramsElementFromRepo :: NgramsTerm -> NgramsRepoElement -> NgramsElement
317 ngramsElementFromRepo
326 NgramsElement { _ne_size = s
331 , _ne_ngrams = ngrams
332 , _ne_occurrences = panic $ "API.Ngrams._ne_occurrences"
334 -- Here we could use 0 if we want to avoid any `panic`.
335 -- It will not happen using getTableNgrams if
336 -- getOccByNgramsOnly provides a count of occurrences for
337 -- all the ngrams given.
341 ------------------------------------------------------------------------
342 newtype NgramsTable = NgramsTable [NgramsElement]
343 deriving (Ord, Eq, Generic, ToJSON, FromJSON, Show)
345 type NgramsList = NgramsTable
347 makePrisms ''NgramsTable
349 -- | Question: why these repetition of Type in this instance
350 -- may you document it please ?
351 instance Each NgramsTable NgramsTable NgramsElement NgramsElement where
352 each = _NgramsTable . each
355 -- | TODO Check N and Weight
357 toNgramsElement :: [NgramsTableData] -> [NgramsElement]
358 toNgramsElement ns = map toNgramsElement' ns
360 toNgramsElement' (NgramsTableData _ p t _ lt w) = NgramsElement t lt' (round w) p' c'
364 Just x -> lookup x mapParent
365 c' = maybe mempty identity $ lookup t mapChildren
366 lt' = maybe (panic "API.Ngrams: listypeId") identity lt
368 mapParent :: Map Int Text
369 mapParent = Map.fromListWith (<>) $ map (\(NgramsTableData i _ t _ _ _) -> (i,t)) ns
371 mapChildren :: Map Text (Set Text)
372 mapChildren = Map.mapKeys (\i -> (maybe (panic "API.Ngrams.mapChildren: ParentId with no Terms: Impossible") identity $ lookup i mapParent))
373 $ Map.fromListWith (<>)
374 $ map (first fromJust)
375 $ filter (isJust . fst)
376 $ map (\(NgramsTableData _ p t _ _ _) -> (p, Set.singleton t)) ns
379 mockTable :: NgramsTable
380 mockTable = NgramsTable
381 [ mkNgramsElement "animal" MapTerm Nothing (mSetFromList ["dog", "cat"])
382 , mkNgramsElement "cat" MapTerm (rp "animal") mempty
383 , mkNgramsElement "cats" StopTerm Nothing mempty
384 , mkNgramsElement "dog" MapTerm (rp "animal") (mSetFromList ["dogs"])
385 , mkNgramsElement "dogs" StopTerm (rp "dog") mempty
386 , mkNgramsElement "fox" MapTerm Nothing mempty
387 , mkNgramsElement "object" CandidateTerm Nothing mempty
388 , mkNgramsElement "nothing" StopTerm Nothing mempty
389 , mkNgramsElement "organic" MapTerm Nothing (mSetFromList ["flower"])
390 , mkNgramsElement "flower" MapTerm (rp "organic") mempty
391 , mkNgramsElement "moon" CandidateTerm Nothing mempty
392 , mkNgramsElement "sky" StopTerm Nothing mempty
395 rp n = Just $ RootParent n n
397 instance Arbitrary NgramsTable where
398 arbitrary = pure mockTable
400 instance ToSchema NgramsTable
402 ------------------------------------------------------------------------
403 type NgramsTableMap = Map NgramsTerm NgramsRepoElement
404 ------------------------------------------------------------------------
405 -- On the Client side:
406 --data Action = InGroup NgramsId NgramsId
407 -- | OutGroup NgramsId NgramsId
408 -- | SetListType NgramsId ListType
410 data PatchSet a = PatchSet
414 deriving (Eq, Ord, Show, Generic)
416 makeLenses ''PatchSet
417 makePrisms ''PatchSet
419 instance ToJSON a => ToJSON (PatchSet a) where
420 toJSON = genericToJSON $ unPrefix "_"
421 toEncoding = genericToEncoding $ unPrefix "_"
423 instance (Ord a, FromJSON a) => FromJSON (PatchSet a) where
424 parseJSON = genericParseJSON $ unPrefix "_"
427 instance (Ord a, Arbitrary a) => Arbitrary (PatchSet a) where
428 arbitrary = PatchSet <$> arbitrary <*> arbitrary
430 type instance Patched (PatchSet a) = Set a
432 type ConflictResolutionPatchSet a = SimpleConflictResolution' (Set a)
433 type instance ConflictResolution (PatchSet a) = ConflictResolutionPatchSet a
435 instance Ord a => Semigroup (PatchSet a) where
436 p <> q = PatchSet { _rem = (q ^. rem) `Set.difference` (p ^. add) <> p ^. rem
437 , _add = (q ^. add) `Set.difference` (p ^. rem) <> p ^. add
440 instance Ord a => Monoid (PatchSet a) where
441 mempty = PatchSet mempty mempty
443 instance Ord a => Group (PatchSet a) where
444 invert (PatchSet r a) = PatchSet a r
446 instance Ord a => Composable (PatchSet a) where
447 composable _ _ = undefined
449 instance Ord a => Action (PatchSet a) (Set a) where
450 act p source = (source `Set.difference` (p ^. rem)) <> p ^. add
452 instance Applicable (PatchSet a) (Set a) where
453 applicable _ _ = mempty
455 instance Ord a => Validity (PatchSet a) where
456 validate p = check (Set.disjoint (p ^. rem) (p ^. add)) "_rem and _add should be dijoint"
458 instance Ord a => Transformable (PatchSet a) where
459 transformable = undefined
461 conflicts _p _q = undefined
463 transformWith conflict p q = undefined conflict p q
465 instance ToSchema a => ToSchema (PatchSet a)
468 type AddRem = Replace (Maybe ())
470 instance Serialise AddRem
472 remPatch, addPatch :: AddRem
473 remPatch = replace (Just ()) Nothing
474 addPatch = replace Nothing (Just ())
476 isRem :: Replace (Maybe ()) -> Bool
477 isRem = (== remPatch)
479 type PatchMap = PM.PatchMap
482 newtype PatchMSet a = PatchMSet (PatchMap a AddRem)
483 deriving (Eq, Show, Generic, Validity, Semigroup, Monoid, Group,
484 Transformable, Composable)
486 type ConflictResolutionPatchMSet a = a -> ConflictResolutionReplace (Maybe ())
487 type instance ConflictResolution (PatchMSet a) = ConflictResolutionPatchMSet a
489 instance (Serialise a, Ord a) => Serialise (PatchMap a AddRem)
490 instance (Serialise a, Ord a) => Serialise (PatchMSet a)
492 -- TODO this breaks module abstraction
493 makePrisms ''PM.PatchMap
495 makePrisms ''PatchMSet
497 _PatchMSetIso :: Ord a => Iso' (PatchMSet a) (PatchSet a)
498 _PatchMSetIso = _PatchMSet . _PatchMap . iso f g . from _PatchSet
500 f :: Ord a => Map a (Replace (Maybe ())) -> (Set a, Set a)
501 f = Map.partition isRem >>> both %~ Map.keysSet
503 g :: Ord a => (Set a, Set a) -> Map a (Replace (Maybe ()))
504 g (rems, adds) = Map.fromSet (const remPatch) rems
505 <> Map.fromSet (const addPatch) adds
507 instance Ord a => Action (PatchMSet a) (MSet a) where
508 act (PatchMSet p) (MSet m) = MSet $ act p m
510 instance Ord a => Applicable (PatchMSet a) (MSet a) where
511 applicable (PatchMSet p) (MSet m) = applicable p m
513 instance (Ord a, ToJSON a) => ToJSON (PatchMSet a) where
514 toJSON = toJSON . view _PatchMSetIso
515 toEncoding = toEncoding . view _PatchMSetIso
517 instance (Ord a, FromJSON a) => FromJSON (PatchMSet a) where
518 parseJSON = fmap (_PatchMSetIso #) . parseJSON
520 instance (Ord a, Arbitrary a) => Arbitrary (PatchMSet a) where
521 arbitrary = (PatchMSet . PM.fromMap) <$> arbitrary
523 instance ToSchema a => ToSchema (PatchMSet a) where
525 declareNamedSchema _ = wellNamedSchema "" (Proxy :: Proxy TODO)
527 type instance Patched (PatchMSet a) = MSet a
529 instance (Eq a, Arbitrary a) => Arbitrary (Replace a) where
530 arbitrary = uncurry replace <$> arbitrary
531 -- If they happen to be equal then the patch is Keep.
533 instance ToSchema a => ToSchema (Replace a) where
534 declareNamedSchema (_ :: Proxy (Replace a)) = do
535 -- TODO Keep constructor is not supported here.
536 aSchema <- declareSchemaRef (Proxy :: Proxy a)
537 return $ NamedSchema (Just "Replace") $ mempty
538 & type_ ?~ SwaggerObject
540 InsOrdHashMap.fromList
544 & required .~ [ "old", "new" ]
547 = NgramsPatch { _patch_children :: PatchMSet NgramsTerm
548 , _patch_list :: Replace ListType -- TODO Map UserId ListType
550 | NgramsReplace { _patch_old :: Maybe NgramsRepoElement
551 , _patch_new :: Maybe NgramsRepoElement
553 deriving (Eq, Show, Generic)
555 -- The JSON encoding is untagged, this is OK since the field names are disjoints and thus the encoding is unambiguous.
556 -- TODO: the empty object should be accepted and treated as mempty.
557 deriveJSON (unPrefixUntagged "_") ''NgramsPatch
558 makeLenses ''NgramsPatch
560 -- TODO: This instance is simplified since we should either have the fields children and/or list
561 -- or the fields old and/or new.
562 instance ToSchema NgramsPatch where
563 declareNamedSchema _ = do
564 childrenSch <- declareSchemaRef (Proxy :: Proxy (PatchMSet NgramsTerm))
565 listSch <- declareSchemaRef (Proxy :: Proxy (Replace ListType))
566 nreSch <- declareSchemaRef (Proxy :: Proxy NgramsRepoElement)
567 return $ NamedSchema (Just "NgramsPatch") $ mempty
568 & type_ ?~ SwaggerObject
570 InsOrdHashMap.fromList
571 [ ("children", childrenSch)
577 instance Arbitrary NgramsPatch where
578 arbitrary = frequency [ (9, NgramsPatch <$> arbitrary <*> (replace <$> arbitrary <*> arbitrary))
579 , (1, NgramsReplace <$> arbitrary <*> arbitrary)
582 instance Serialise NgramsPatch
583 instance Serialise (Replace ListType)
585 instance Serialise ListType
587 type NgramsPatchIso =
588 MaybePatch NgramsRepoElement (PairPatch (PatchMSet NgramsTerm) (Replace ListType))
590 _NgramsPatch :: Iso' NgramsPatch NgramsPatchIso
591 _NgramsPatch = iso unwrap wrap
593 unwrap (NgramsPatch c l) = Mod $ PairPatch (c, l)
594 unwrap (NgramsReplace o n) = replace o n
597 Just (PairPatch (c, l)) -> NgramsPatch c l
598 Nothing -> NgramsReplace (x ^? old . _Just) (x ^? new . _Just)
600 instance Semigroup NgramsPatch where
601 p <> q = _NgramsPatch # (p ^. _NgramsPatch <> q ^. _NgramsPatch)
603 instance Monoid NgramsPatch where
604 mempty = _NgramsPatch # mempty
606 instance Validity NgramsPatch where
607 validate p = p ^. _NgramsPatch . to validate
609 instance Transformable NgramsPatch where
610 transformable p q = transformable (p ^. _NgramsPatch) (q ^. _NgramsPatch)
612 conflicts p q = conflicts (p ^. _NgramsPatch) (q ^. _NgramsPatch)
614 transformWith conflict p q = (_NgramsPatch # p', _NgramsPatch # q')
616 (p', q') = transformWith conflict (p ^. _NgramsPatch) (q ^. _NgramsPatch)
618 type ConflictResolutionNgramsPatch =
619 ( ConflictResolutionReplace (Maybe NgramsRepoElement)
620 , ( ConflictResolutionPatchMSet NgramsTerm
621 , ConflictResolutionReplace ListType
625 type instance ConflictResolution NgramsPatch =
626 ConflictResolutionNgramsPatch
628 type PatchedNgramsPatch = Maybe NgramsRepoElement
629 type instance Patched NgramsPatch = PatchedNgramsPatch
631 instance Applicable (PairPatch (PatchMSet NgramsTerm) (Replace ListType)) NgramsRepoElement where
632 applicable (PairPatch (c, l)) n = applicable c (n ^. nre_children) <> applicable l (n ^. nre_list)
634 instance Action (PairPatch (PatchMSet NgramsTerm) (Replace ListType)) NgramsRepoElement where
635 act (PairPatch (c, l)) = (nre_children %~ act c)
636 . (nre_list %~ act l)
638 instance Applicable NgramsPatch (Maybe NgramsRepoElement) where
639 applicable p = applicable (p ^. _NgramsPatch)
641 instance Action NgramsPatch (Maybe NgramsRepoElement) where
642 act p = act (p ^. _NgramsPatch)
644 newtype NgramsTablePatch = NgramsTablePatch (PatchMap NgramsTerm NgramsPatch)
645 deriving (Eq, Show, Generic, ToJSON, FromJSON, Semigroup, Monoid, Validity, Transformable)
647 instance Serialise NgramsTablePatch
648 instance Serialise (PatchMap NgramsTerm NgramsPatch)
650 instance FromField NgramsTablePatch
652 fromField = fromField'
654 instance FromField (PatchMap TableNgrams.NgramsType (PatchMap NodeId NgramsTablePatch))
656 fromField = fromField'
658 type instance ConflictResolution NgramsTablePatch =
659 NgramsTerm -> ConflictResolutionNgramsPatch
661 type PatchedNgramsTablePatch = Map NgramsTerm PatchedNgramsPatch
662 -- ~ Patched (PatchMap NgramsTerm NgramsPatch)
663 type instance Patched NgramsTablePatch = PatchedNgramsTablePatch
665 makePrisms ''NgramsTablePatch
666 instance ToSchema (PatchMap NgramsTerm NgramsPatch)
667 instance ToSchema NgramsTablePatch
669 instance Applicable NgramsTablePatch (Maybe NgramsTableMap) where
670 applicable p = applicable (p ^. _NgramsTablePatch)
672 instance Action NgramsTablePatch (Maybe NgramsTableMap) where
674 fmap (execState (reParentNgramsTablePatch p)) .
675 act (p ^. _NgramsTablePatch)
677 instance Arbitrary NgramsTablePatch where
678 arbitrary = NgramsTablePatch <$> PM.fromMap <$> arbitrary
680 -- Should it be less than an Lens' to preserve PatchMap's abstraction.
681 -- ntp_ngrams_patches :: Lens' NgramsTablePatch (Map NgramsTerm NgramsPatch)
682 -- ntp_ngrams_patches = _NgramsTablePatch . undefined
684 type ReParent a = forall m. MonadState NgramsTableMap m => a -> m ()
686 reRootChildren :: NgramsTerm -> ReParent NgramsTerm
687 reRootChildren root ngram = do
688 nre <- use $ at ngram
689 forOf_ (_Just . nre_children . folded) nre $ \child -> do
690 at child . _Just . nre_root ?= root
691 reRootChildren root child
693 reParent :: Maybe RootParent -> ReParent NgramsTerm
694 reParent rp child = do
695 at child . _Just %= ( (nre_parent .~ (_rp_parent <$> rp))
696 . (nre_root .~ (_rp_root <$> rp))
698 reRootChildren (fromMaybe child (rp ^? _Just . rp_root)) child
700 reParentAddRem :: RootParent -> NgramsTerm -> ReParent AddRem
701 reParentAddRem rp child p =
702 reParent (if isRem p then Nothing else Just rp) child
704 reParentNgramsPatch :: NgramsTerm -> ReParent NgramsPatch
705 reParentNgramsPatch parent ngramsPatch = do
706 root_of_parent <- use (at parent . _Just . nre_root)
708 root = fromMaybe parent root_of_parent
709 rp = RootParent { _rp_root = root, _rp_parent = parent }
710 itraverse_ (reParentAddRem rp) (ngramsPatch ^. patch_children . _PatchMSet . _PatchMap)
711 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
713 reParentNgramsTablePatch :: ReParent NgramsTablePatch
714 reParentNgramsTablePatch p = itraverse_ reParentNgramsPatch (p ^. _NgramsTablePatch. _PatchMap)
715 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
717 ------------------------------------------------------------------------
718 ------------------------------------------------------------------------
721 data Versioned a = Versioned
722 { _v_version :: Version
725 deriving (Generic, Show, Eq)
726 deriveJSON (unPrefix "_v_") ''Versioned
727 makeLenses ''Versioned
728 instance (Typeable a, ToSchema a) => ToSchema (Versioned a) where
729 declareNamedSchema = wellNamedSchema "_v_"
730 instance Arbitrary a => Arbitrary (Versioned a) where
731 arbitrary = Versioned 1 <$> arbitrary -- TODO 1 is constant so far
735 -- TODO sequences of modifications (Patchs)
736 type NgramsIdPatch = Patch NgramsId NgramsPatch
738 ngramsPatch :: Int -> NgramsPatch
739 ngramsPatch n = NgramsPatch (DM.fromList [(1, StopTerm)]) (Set.fromList [n]) Set.empty
741 toEdit :: NgramsId -> NgramsPatch -> Edit NgramsId NgramsPatch
742 toEdit n p = Edit n p
743 ngramsIdPatch :: Patch NgramsId NgramsPatch
744 ngramsIdPatch = fromList $ catMaybes $ reverse [ replace (1::NgramsId) (Just $ ngramsPatch 1) Nothing
745 , replace (1::NgramsId) Nothing (Just $ ngramsPatch 2)
746 , replace (2::NgramsId) Nothing (Just $ ngramsPatch 2)
749 -- applyPatchBack :: Patch -> IO Patch
750 -- isEmptyPatch = Map.all (\x -> Set.isEmpty (add_children x) && Set.isEmpty ... )
752 ------------------------------------------------------------------------
753 ------------------------------------------------------------------------
754 ------------------------------------------------------------------------
757 -- TODO: Replace.old is ignored which means that if the current list
758 -- `MapTerm` and that the patch is `Replace CandidateTerm StopTerm` then
759 -- the list is going to be `StopTerm` while it should keep `MapTerm`.
760 -- However this should not happen in non conflicting situations.
761 mkListsUpdate :: NgramsType -> NgramsTablePatch -> [(NgramsTypeId, NgramsTerm, ListTypeId)]
762 mkListsUpdate nt patches =
763 [ (ngramsTypeId nt, ng, listTypeId lt)
764 | (ng, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
765 , lt <- patch ^.. patch_list . new
768 mkChildrenGroups :: (PatchSet NgramsTerm -> Set NgramsTerm)
771 -> [(NgramsTypeId, NgramsParent, NgramsChild)]
772 mkChildrenGroups addOrRem nt patches =
773 [ (ngramsTypeId nt, parent, child)
774 | (parent, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
775 , child <- patch ^.. patch_children . to addOrRem . folded
779 ngramsTypeFromTabType :: TabType -> TableNgrams.NgramsType
780 ngramsTypeFromTabType tabType =
781 let lieu = "Garg.API.Ngrams: " :: Text in
783 Sources -> TableNgrams.Sources
784 Authors -> TableNgrams.Authors
785 Institutes -> TableNgrams.Institutes
786 Terms -> TableNgrams.NgramsTerms
787 _ -> panic $ lieu <> "No Ngrams for this tab"
788 -- TODO: This `panic` would disapear with custom NgramsType.
790 ------------------------------------------------------------------------
792 { _r_version :: Version
795 -- first patch in the list is the most recent
799 instance (FromJSON s, FromJSON p) => FromJSON (Repo s p) where
800 parseJSON = genericParseJSON $ unPrefix "_r_"
802 instance (ToJSON s, ToJSON p) => ToJSON (Repo s p) where
803 toJSON = genericToJSON $ unPrefix "_r_"
804 toEncoding = genericToEncoding $ unPrefix "_r_"
806 instance (Serialise s, Serialise p) => Serialise (Repo s p)
810 initRepo :: Monoid s => Repo s p
811 initRepo = Repo 1 mempty []
813 type NgramsRepo = Repo NgramsState NgramsStatePatch
814 type NgramsState = Map TableNgrams.NgramsType (Map NodeId NgramsTableMap)
815 type NgramsStatePatch = PatchMap TableNgrams.NgramsType (PatchMap NodeId NgramsTablePatch)
817 instance Serialise (PM.PatchMap NodeId NgramsTablePatch)
818 instance Serialise NgramsStatePatch
820 initMockRepo :: NgramsRepo
821 initMockRepo = Repo 1 s []
823 s = Map.singleton TableNgrams.NgramsTerms
824 $ Map.singleton 47254
826 [ (n ^. ne_ngrams, ngramsElementToRepo n) | n <- mockTable ^. _NgramsTable ]
828 data RepoEnv = RepoEnv
829 { _renv_var :: !(MVar NgramsRepo)
830 , _renv_saver :: !(IO ())
831 , _renv_lock :: !FileLock
837 class HasRepoVar env where
838 repoVar :: Getter env (MVar NgramsRepo)
840 instance HasRepoVar (MVar NgramsRepo) where
843 class HasRepoSaver env where
844 repoSaver :: Getter env (IO ())
846 class (HasRepoVar env, HasRepoSaver env) => HasRepo env where
847 repoEnv :: Getter env RepoEnv
849 instance HasRepo RepoEnv where
852 instance HasRepoVar RepoEnv where
855 instance HasRepoSaver RepoEnv where
856 repoSaver = renv_saver
858 type RepoCmdM env err m =
861 , MonadBaseControl IO m
864 ------------------------------------------------------------------------
866 saveRepo :: ( MonadReader env m, MonadBase IO m, HasRepoSaver env )
868 saveRepo = liftBase =<< view repoSaver
870 listTypeConflictResolution :: ListType -> ListType -> ListType
871 listTypeConflictResolution _ _ = undefined -- TODO Use Map User ListType
873 ngramsStatePatchConflictResolution
874 :: TableNgrams.NgramsType
877 -> ConflictResolutionNgramsPatch
878 ngramsStatePatchConflictResolution _ngramsType _nodeId _ngramsTerm
879 = (ours, (const ours, ours), (False, False))
880 -- ^------^------- they mean that Mod has always priority.
881 --(True, False) <- would mean priority to the left (same as ours).
883 -- undefined {- TODO think this through -}, listTypeConflictResolution)
886 -- Insertions are not considered as patches,
887 -- they do not extend history,
888 -- they do not bump version.
889 insertNewOnly :: a -> Maybe b -> a
890 insertNewOnly m = maybe m (const $ error "insertNewOnly: impossible")
891 -- TODO error handling
893 something :: Monoid a => Maybe a -> a
894 something Nothing = mempty
895 something (Just a) = a
898 -- TODO refactor with putListNgrams
899 copyListNgrams :: RepoCmdM env err m
900 => NodeId -> NodeId -> NgramsType
902 copyListNgrams srcListId dstListId ngramsType = do
904 liftBase $ modifyMVar_ var $
905 pure . (r_state . at ngramsType %~ (Just . f . something))
908 f :: Map NodeId NgramsTableMap -> Map NodeId NgramsTableMap
909 f m = m & at dstListId %~ insertNewOnly (m ^. at srcListId)
911 -- TODO refactor with putListNgrams
912 -- The list must be non-empty!
913 -- The added ngrams must be non-existent!
914 addListNgrams :: RepoCmdM env err m
915 => NodeId -> NgramsType
916 -> [NgramsElement] -> m ()
917 addListNgrams listId ngramsType nes = do
919 liftBase $ modifyMVar_ var $
920 pure . (r_state . at ngramsType . _Just . at listId . _Just <>~ m)
923 m = Map.fromList $ (\n -> (n ^. ne_ngrams, n)) <$> nes
927 rmListNgrams :: RepoCmdM env err m
929 -> TableNgrams.NgramsType
931 rmListNgrams l nt = setListNgrams l nt mempty
933 -- | TODO: incr the Version number
934 -- && should use patch
936 setListNgrams :: RepoCmdM env err m
938 -> TableNgrams.NgramsType
939 -> Map NgramsTerm NgramsRepoElement
941 setListNgrams listId ngramsType ns = do
943 liftBase $ modifyMVar_ var $
947 (at listId .~ ( Just ns))
954 -- This is no longer part of the API.
955 -- This function is maintained for its usage in Database.Action.Flow.List.
956 -- If the given list of ngrams elements contains ngrams already in
957 -- the repo, they will be ignored.
958 putListNgrams :: (HasInvalidError err, RepoCmdM env err m)
960 -> TableNgrams.NgramsType
963 putListNgrams _ _ [] = pure ()
964 putListNgrams nodeId ngramsType nes = putListNgrams' nodeId ngramsType m
966 m = Map.fromList $ map (\n -> (n ^. ne_ngrams, ngramsElementToRepo n)) nes
968 putListNgrams' :: (HasInvalidError err, RepoCmdM env err m)
970 -> TableNgrams.NgramsType
971 -> Map NgramsTerm NgramsRepoElement
973 putListNgrams' nodeId ngramsType ns = do
974 printDebug "[putListNgrams'] nodeId" nodeId
975 printDebug "[putListNgrams'] ngramsType" ngramsType
976 printDebug "[putListNgrams'] ns" ns
978 let p1 = NgramsTablePatch . PM.fromMap $ NgramsReplace Nothing . Just <$> ns
979 (p0, p0_validity) = PM.singleton nodeId p1
980 (p, p_validity) = PM.singleton ngramsType p0
981 assertValid p0_validity
982 assertValid p_validity
986 q <- commitStatePatch (Versioned v p)
988 -- What if another commit comes in between?
989 -- Shall we have a blindCommitStatePatch? It would not ask for a version but just a patch.
990 -- The modifyMVar_ would test the patch with applicable first.
991 -- If valid the rest would be atomic and no merge is required.
994 liftBase $ modifyMVar_ var $ \r -> do
995 pure $ r & r_version +~ 1
997 & r_state . at ngramsType %~
1010 currentVersion :: RepoCmdM env err m
1014 r <- liftBase $ readMVar var
1015 pure $ r ^. r_version
1018 -- tableNgramsPut :: (HasInvalidError err, RepoCmdM env err m)
1019 commitStatePatch :: RepoCmdM env err m => Versioned NgramsStatePatch -> m (Versioned NgramsStatePatch)
1020 commitStatePatch (Versioned p_version p) = do
1022 vq' <- liftBase $ modifyMVar var $ \r -> do
1024 q = mconcat $ take (r ^. r_version - p_version) (r ^. r_history)
1025 (p', q') = transformWith ngramsStatePatchConflictResolution p q
1026 r' = r & r_version +~ 1
1028 & r_history %~ (p' :)
1030 -- Ideally we would like to check these properties. However:
1031 -- * They should be checked only to debug the code. The client data
1032 -- should be able to trigger these.
1033 -- * What kind of error should they throw (we are in IO here)?
1034 -- * Should we keep modifyMVar?
1035 -- * Should we throw the validation in an Exception, catch it around
1036 -- modifyMVar and throw it back as an Error?
1037 assertValid $ transformable p q
1038 assertValid $ applicable p' (r ^. r_state)
1040 pure (r', Versioned (r' ^. r_version) q')
1045 -- This is a special case of tableNgramsPut where the input patch is empty.
1046 tableNgramsPull :: RepoCmdM env err m
1048 -> TableNgrams.NgramsType
1050 -> m (Versioned NgramsTablePatch)
1051 tableNgramsPull listId ngramsType p_version = do
1053 r <- liftBase $ readMVar var
1056 q = mconcat $ take (r ^. r_version - p_version) (r ^. r_history)
1057 q_table = q ^. _PatchMap . at ngramsType . _Just . _PatchMap . at listId . _Just
1059 pure (Versioned (r ^. r_version) q_table)
1061 -- Apply the given patch to the DB and returns the patch to be applied on the
1063 -- TODO-ACCESS check
1064 tableNgramsPut :: (HasInvalidError err, RepoCmdM env err m)
1067 -> Versioned NgramsTablePatch
1068 -> m (Versioned NgramsTablePatch)
1069 tableNgramsPut tabType listId (Versioned p_version p_table)
1070 | p_table == mempty = do
1071 let ngramsType = ngramsTypeFromTabType tabType
1072 tableNgramsPull listId ngramsType p_version
1075 let ngramsType = ngramsTypeFromTabType tabType
1076 (p0, p0_validity) = PM.singleton listId p_table
1077 (p, p_validity) = PM.singleton ngramsType p0
1079 assertValid p0_validity
1080 assertValid p_validity
1082 commitStatePatch (Versioned p_version p)
1083 <&> v_data %~ (view (_PatchMap . at ngramsType . _Just . _PatchMap . at listId . _Just))
1085 mergeNgramsElement :: NgramsRepoElement -> NgramsRepoElement -> NgramsRepoElement
1086 mergeNgramsElement _neOld neNew = neNew
1088 { _ne_list :: ListType
1089 If we merge the parents/children we can potentially create cycles!
1090 , _ne_parent :: Maybe NgramsTerm
1091 , _ne_children :: MSet NgramsTerm
1095 getNgramsTableMap :: RepoCmdM env err m
1097 -> TableNgrams.NgramsType
1098 -> m (Versioned NgramsTableMap)
1099 getNgramsTableMap nodeId ngramsType = do
1101 repo <- liftBase $ readMVar v
1102 pure $ Versioned (repo ^. r_version)
1103 (repo ^. r_state . at ngramsType . _Just . at nodeId . _Just)
1105 dumpJsonTableMap :: RepoCmdM env err m
1108 -> TableNgrams.NgramsType
1110 dumpJsonTableMap fpath nodeId ngramsType = do
1111 m <- getNgramsTableMap nodeId ngramsType
1112 liftBase $ DTL.writeFile (unpack fpath) (DAT.encodeToLazyText m)
1118 -- | TODO Errors management
1119 -- TODO: polymorphic for Annuaire or Corpus or ...
1120 -- | Table of Ngrams is a ListNgrams formatted (sorted and/or cut).
1121 -- TODO: should take only one ListId
1123 getTime' :: MonadBase IO m => m TimeSpec
1124 getTime' = liftBase $ getTime ProcessCPUTime
1127 getTableNgrams :: forall env err m.
1128 (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1129 => NodeType -> NodeId -> TabType
1130 -> ListId -> Limit -> Maybe Offset
1132 -> Maybe MinSize -> Maybe MaxSize
1134 -> (NgramsTerm -> Bool)
1135 -> m (Versioned NgramsTable)
1136 getTableNgrams _nType nId tabType listId limit_ offset
1137 listType minSize maxSize orderBy searchQuery = do
1140 -- lIds <- selectNodesWithUsername NodeList userMaster
1142 ngramsType = ngramsTypeFromTabType tabType
1143 offset' = maybe 0 identity offset
1144 listType' = maybe (const True) (==) listType
1145 minSize' = maybe (const True) (<=) minSize
1146 maxSize' = maybe (const True) (>=) maxSize
1148 selected_node n = minSize' s
1150 && searchQuery (n ^. ne_ngrams)
1151 && listType' (n ^. ne_list)
1155 selected_inner roots n = maybe False (`Set.member` roots) (n ^. ne_root)
1157 ---------------------------------------
1158 sortOnOrder Nothing = identity
1159 sortOnOrder (Just TermAsc) = List.sortOn $ view ne_ngrams
1160 sortOnOrder (Just TermDesc) = List.sortOn $ Down . view ne_ngrams
1161 sortOnOrder (Just ScoreAsc) = List.sortOn $ view ne_occurrences
1162 sortOnOrder (Just ScoreDesc) = List.sortOn $ Down . view ne_occurrences
1164 ---------------------------------------
1165 selectAndPaginate :: Map NgramsTerm NgramsElement -> [NgramsElement]
1166 selectAndPaginate tableMap = roots <> inners
1168 list = tableMap ^.. each
1169 rootOf ne = maybe ne (\r -> fromMaybe (panic "getTableNgrams: invalid root") (tableMap ^. at r))
1171 selected_nodes = list & take limit_
1173 . filter selected_node
1174 . sortOnOrder orderBy
1175 roots = rootOf <$> selected_nodes
1176 rootsSet = Set.fromList (_ne_ngrams <$> roots)
1177 inners = list & filter (selected_inner rootsSet)
1179 ---------------------------------------
1180 setScores :: forall t. Each t t NgramsElement NgramsElement => Bool -> t -> m t
1181 setScores False table = pure table
1182 setScores True table = do
1183 let ngrams_terms = unNgramsTerm <$> (table ^.. each . ne_ngrams)
1185 occurrences <- getOccByNgramsOnlyFast' nId
1190 liftBase $ hprint stderr
1191 ("getTableNgrams/setScores #ngrams=" % int % " time=" % timeSpecs % "\n")
1192 (length ngrams_terms) t1 t2
1194 occurrences <- getOccByNgramsOnlySlow nType nId
1200 setOcc ne = ne & ne_occurrences .~ sumOf (at (unNgramsTerm (ne ^. ne_ngrams)) . _Just) occurrences
1202 pure $ table & each %~ setOcc
1203 ---------------------------------------
1205 -- lists <- catMaybes <$> listsWith userMaster
1206 -- trace (show lists) $
1207 -- getNgramsTableMap ({-lists <>-} listIds) ngramsType
1209 let scoresNeeded = needsScores orderBy
1210 tableMap1 <- getNgramsTableMap listId ngramsType
1212 tableMap2 <- tableMap1 & v_data %%~ setScores scoresNeeded
1213 . Map.mapWithKey ngramsElementFromRepo
1215 tableMap3 <- tableMap2 & v_data %%~ fmap NgramsTable
1216 . setScores (not scoresNeeded)
1219 liftBase $ hprint stderr
1220 ("getTableNgrams total=" % timeSpecs
1221 % " map1=" % timeSpecs
1222 % " map2=" % timeSpecs
1223 % " map3=" % timeSpecs
1224 % " sql=" % (if scoresNeeded then "map2" else "map3")
1226 ) t0 t3 t0 t1 t1 t2 t2 t3
1230 scoresRecomputeTableNgrams :: forall env err m. (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env) => NodeId -> TabType -> ListId -> m Int
1231 scoresRecomputeTableNgrams nId tabType listId = do
1232 tableMap <- getNgramsTableMap listId ngramsType
1233 _ <- tableMap & v_data %%~ setScores
1234 . Map.mapWithKey ngramsElementFromRepo
1238 ngramsType = ngramsTypeFromTabType tabType
1240 setScores :: forall t. Each t t NgramsElement NgramsElement => t -> m t
1241 setScores table = do
1242 let ngrams_terms = unNgramsTerm <$> (table ^.. each . ne_ngrams)
1243 occurrences <- getOccByNgramsOnlyFast' nId
1248 setOcc ne = ne & ne_occurrences .~ sumOf (at (unNgramsTerm (ne ^. ne_ngrams)) . _Just) occurrences
1250 pure $ table & each %~ setOcc
1256 -- TODO: find a better place for the code above, All APIs stay here
1257 type QueryParamR = QueryParam' '[Required, Strict]
1259 data OrderBy = TermAsc | TermDesc | ScoreAsc | ScoreDesc
1260 deriving (Generic, Enum, Bounded, Read, Show)
1262 instance FromHttpApiData OrderBy
1264 parseUrlPiece "TermAsc" = pure TermAsc
1265 parseUrlPiece "TermDesc" = pure TermDesc
1266 parseUrlPiece "ScoreAsc" = pure ScoreAsc
1267 parseUrlPiece "ScoreDesc" = pure ScoreDesc
1268 parseUrlPiece _ = Left "Unexpected value of OrderBy"
1271 instance ToParamSchema OrderBy
1272 instance FromJSON OrderBy
1273 instance ToJSON OrderBy
1274 instance ToSchema OrderBy
1275 instance Arbitrary OrderBy
1277 arbitrary = elements [minBound..maxBound]
1279 needsScores :: Maybe OrderBy -> Bool
1280 needsScores (Just ScoreAsc) = True
1281 needsScores (Just ScoreDesc) = True
1282 needsScores _ = False
1284 type TableNgramsApiGet = Summary " Table Ngrams API Get"
1285 :> QueryParamR "ngramsType" TabType
1286 :> QueryParamR "list" ListId
1287 :> QueryParamR "limit" Limit
1288 :> QueryParam "offset" Offset
1289 :> QueryParam "listType" ListType
1290 :> QueryParam "minTermSize" MinSize
1291 :> QueryParam "maxTermSize" MaxSize
1292 :> QueryParam "orderBy" OrderBy
1293 :> QueryParam "search" Text
1294 :> Get '[JSON] (Versioned NgramsTable)
1296 type TableNgramsApiPut = Summary " Table Ngrams API Change"
1297 :> QueryParamR "ngramsType" TabType
1298 :> QueryParamR "list" ListId
1299 :> ReqBody '[JSON] (Versioned NgramsTablePatch)
1300 :> Put '[JSON] (Versioned NgramsTablePatch)
1302 type RecomputeScoresNgramsApiGet = Summary " Recompute scores for ngrams table"
1303 :> QueryParamR "ngramsType" TabType
1304 :> QueryParamR "list" ListId
1305 :> "recompute" :> Post '[JSON] Int
1307 type TableNgramsApiGetVersion = Summary " Table Ngrams API Get Version"
1308 :> QueryParamR "ngramsType" TabType
1309 :> QueryParamR "list" ListId
1310 :> Get '[JSON] Version
1312 type TableNgramsApi = TableNgramsApiGet
1313 :<|> TableNgramsApiPut
1314 :<|> RecomputeScoresNgramsApiGet
1315 :<|> "version" :> TableNgramsApiGetVersion
1317 getTableNgramsCorpus :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1324 -> Maybe MinSize -> Maybe MaxSize
1326 -> Maybe Text -- full text search
1327 -> m (Versioned NgramsTable)
1328 getTableNgramsCorpus nId tabType listId limit_ offset listType minSize maxSize orderBy mt =
1329 getTableNgrams NodeCorpus nId tabType listId limit_ offset listType minSize maxSize orderBy searchQuery
1331 searchQuery (NgramsTerm nt) = maybe (const True) isInfixOf mt nt
1333 getTableNgramsVersion :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1338 getTableNgramsVersion _nId _tabType _listId = currentVersion
1340 -- Versioned { _v_version = v } <- getTableNgramsCorpus nId tabType listId 100000 Nothing Nothing Nothing Nothing Nothing Nothing
1341 -- This line above looks like a waste of computation to finally get only the version.
1342 -- See the comment about listNgramsChangedSince.
1345 -- | Text search is deactivated for now for ngrams by doc only
1346 getTableNgramsDoc :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1348 -> ListId -> Limit -> Maybe Offset
1350 -> Maybe MinSize -> Maybe MaxSize
1352 -> Maybe Text -- full text search
1353 -> m (Versioned NgramsTable)
1354 getTableNgramsDoc dId tabType listId limit_ offset listType minSize maxSize orderBy _mt = do
1355 ns <- selectNodesWithUsername NodeList userMaster
1356 let ngramsType = ngramsTypeFromTabType tabType
1357 ngs <- selectNgramsByDoc (ns <> [listId]) dId ngramsType
1358 let searchQuery (NgramsTerm nt) = flip S.member (S.fromList ngs) nt
1359 getTableNgrams NodeDocument dId tabType listId limit_ offset listType minSize maxSize orderBy searchQuery
1363 apiNgramsTableCorpus :: ( RepoCmdM env err m
1365 , HasInvalidError err
1366 , HasConnectionPool env
1369 => NodeId -> ServerT TableNgramsApi m
1370 apiNgramsTableCorpus cId = getTableNgramsCorpus cId
1372 :<|> scoresRecomputeTableNgrams cId
1373 :<|> getTableNgramsVersion cId
1375 apiNgramsTableDoc :: ( RepoCmdM env err m
1377 , HasInvalidError err
1378 , HasConnectionPool env
1381 => DocId -> ServerT TableNgramsApi m
1382 apiNgramsTableDoc dId = getTableNgramsDoc dId
1384 :<|> scoresRecomputeTableNgrams dId
1385 :<|> getTableNgramsVersion dId
1386 -- > index all the corpus accordingly (TODO AD)
1388 -- Did the given list of ngrams changed since the given version?
1389 -- The returned value is versioned boolean value, meaning that one always retrieve the
1391 -- If the given version is negative then one simply receive the latest version and True.
1392 -- Using this function is more precise than simply comparing the latest version number
1393 -- with the local version number. Indeed there might be no change to this particular list
1394 -- and still the version number has changed because of other lists.
1396 -- Here the added value is to make a compromise between precision, computation, and bandwidth:
1397 -- * currentVersion: good computation, good bandwidth, bad precision.
1398 -- * listNgramsChangedSince: good precision, good bandwidth, bad computation.
1399 -- * tableNgramsPull: good precision, good bandwidth (if you use the received data!), bad computation.
1400 listNgramsChangedSince :: RepoCmdM env err m
1401 => ListId -> TableNgrams.NgramsType -> Version -> m (Versioned Bool)
1402 listNgramsChangedSince listId ngramsType version
1404 Versioned <$> currentVersion <*> pure True
1406 tableNgramsPull listId ngramsType version & mapped . v_data %~ (== mempty)
1409 instance Arbitrary NgramsRepoElement where
1410 arbitrary = elements $ map ngramsElementToRepo ns
1412 NgramsTable ns = mockTable
1415 instance FromHttpApiData (Map TableNgrams.NgramsType (Versioned NgramsTableMap))
1417 parseUrlPiece x = maybeToEither x (decode $ cs x)