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
58 , NgramsRepoElement(..)
67 , ngramsTypeFromTabType
85 , listNgramsChangedSince
89 import Codec.Serialise (Serialise())
90 import Control.Category ((>>>))
91 import Control.Concurrent
92 import Control.Lens (makeLenses, makePrisms, Getter, Iso', iso, from, (.~), (?=), (#), to, folded, {-withIndex, ifolded,-} view, use, (^.), (^..), (^?), (+~), (%~), (.~), (%=), sumOf, at, _Just, Each(..), itraverse_, both, forOf_, (%%~), (?~), mapped)
93 import Control.Monad.Base (MonadBase, liftBase)
94 import Control.Monad.Error.Class (MonadError)
95 import Control.Monad.Reader
96 import Control.Monad.State
97 import Control.Monad.Trans.Control (MonadBaseControl)
98 import Data.Aeson hiding ((.=))
99 import Data.Aeson.TH (deriveJSON)
100 import qualified Data.Aeson.Text as DAT
101 import Data.Either (Either(..))
103 import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
104 import qualified Data.List as List
105 import Data.Map.Strict (Map)
106 import qualified Data.Map.Strict as Map
107 import qualified Data.Map.Strict.Patch as PM
108 import Data.Maybe (fromMaybe)
110 import Data.Ord (Down(..))
111 import Data.Patch.Class (Replace, replace, Action(act), Group, Applicable(..), Composable(..), Transformable(..),
112 PairPatch(..), Patched, ConflictResolution, ConflictResolutionReplace, ours,
113 MaybePatch(Mod), unMod, old, new)
114 import Data.Set (Set)
115 import qualified Data.Set as S
116 import qualified Data.Set as Set
117 import Data.Swagger hiding (version, patch)
118 import Data.Text (Text, isInfixOf, unpack)
119 import Data.Text.Lazy.IO as DTL
121 import Database.PostgreSQL.Simple.FromField (FromField, fromField)
122 import Formatting (hprint, int, (%))
123 import Formatting.Clock (timeSpecs)
124 import GHC.Generics (Generic)
125 import Servant hiding (Patch)
126 import System.Clock (getTime, TimeSpec, Clock(..))
127 import System.FileLock (FileLock)
128 import System.IO (stderr)
129 import Test.QuickCheck (elements, frequency)
130 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
132 import Prelude (error)
133 import Protolude (maybeToEither)
134 import Gargantext.Prelude
136 import Gargantext.Core.Types (ListType(..), NodeId, ListId, DocId, Limit, Offset, HasInvalidError, assertValid)
137 import Gargantext.Core.Types (TODO)
138 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixUntagged, unPrefixSwagger, wellNamedSchema)
139 import Gargantext.Database.Action.Metrics.NgramsByNode (getOccByNgramsOnlyFast')
140 import Gargantext.Database.Query.Table.Node.Select
141 import Gargantext.Database.Query.Table.Ngrams hiding (NgramsType(..), ngrams, ngramsType, ngrams_terms)
142 import Gargantext.Database.Admin.Config (userMaster)
143 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
144 import Gargantext.Database.Admin.Types.Node (NodeType(..))
145 import Gargantext.Database.Prelude (fromField', HasConnectionPool, HasConfig)
146 import qualified Gargantext.Database.Query.Table.Ngrams as TableNgrams
147 import qualified Gargantext.Core.Text as GCT
149 ------------------------------------------------------------------------
150 --data FacetFormat = Table | Chart
151 data TabType = Docs | Trash | MoreFav | MoreTrash
152 | Terms | Sources | Authors | Institutes
154 deriving (Generic, Enum, Bounded, Show)
156 instance FromHttpApiData TabType
158 parseUrlPiece "Docs" = pure Docs
159 parseUrlPiece "Trash" = pure Trash
160 parseUrlPiece "MoreFav" = pure MoreFav
161 parseUrlPiece "MoreTrash" = pure MoreTrash
163 parseUrlPiece "Terms" = pure Terms
164 parseUrlPiece "Sources" = pure Sources
165 parseUrlPiece "Institutes" = pure Institutes
166 parseUrlPiece "Authors" = pure Authors
168 parseUrlPiece "Contacts" = pure Contacts
170 parseUrlPiece _ = Left "Unexpected value of TabType"
172 instance ToParamSchema TabType
173 instance ToJSON TabType
174 instance FromJSON TabType
175 instance ToSchema TabType
176 instance Arbitrary TabType
178 arbitrary = elements [minBound .. maxBound]
180 newtype MSet a = MSet (Map a ())
181 deriving (Eq, Ord, Show, Generic, Arbitrary, Semigroup, Monoid)
183 instance ToJSON a => ToJSON (MSet a) where
184 toJSON (MSet m) = toJSON (Map.keys m)
185 toEncoding (MSet m) = toEncoding (Map.keys m)
187 mSetFromSet :: Set a -> MSet a
188 mSetFromSet = MSet . Map.fromSet (const ())
190 mSetFromList :: Ord a => [a] -> MSet a
191 mSetFromList = MSet . Map.fromList . map (\x -> (x, ()))
193 -- mSetToSet :: Ord a => MSet a -> Set a
194 -- mSetToSet (MSet a) = Set.fromList ( Map.keys a)
195 mSetToSet :: Ord a => MSet a -> Set a
196 mSetToSet = Set.fromList . mSetToList
198 mSetToList :: MSet a -> [a]
199 mSetToList (MSet a) = Map.keys a
201 instance Foldable MSet where
202 foldMap f (MSet m) = Map.foldMapWithKey (\k _ -> f k) m
204 instance (Ord a, FromJSON a) => FromJSON (MSet a) where
205 parseJSON = fmap mSetFromList . parseJSON
207 instance (ToJSONKey a, ToSchema a) => ToSchema (MSet a) where
209 declareNamedSchema _ = wellNamedSchema "" (Proxy :: Proxy TODO)
211 ------------------------------------------------------------------------
212 type NgramsTerm = Text
214 data RootParent = RootParent
215 { _rp_root :: NgramsTerm
216 , _rp_parent :: NgramsTerm
218 deriving (Ord, Eq, Show, Generic)
220 deriveJSON (unPrefix "_rp_") ''RootParent
221 makeLenses ''RootParent
223 data NgramsRepoElement = NgramsRepoElement
225 , _nre_list :: ListType
226 --, _nre_root_parent :: Maybe RootParent
227 , _nre_root :: Maybe NgramsTerm
228 , _nre_parent :: Maybe NgramsTerm
229 , _nre_children :: MSet NgramsTerm
231 deriving (Ord, Eq, Show, Generic)
233 deriveJSON (unPrefix "_nre_") ''NgramsRepoElement
235 -- if ngrams & not size => size
238 makeLenses ''NgramsRepoElement
240 instance ToSchema NgramsRepoElement where
241 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_nre_")
243 instance Serialise (MSet NgramsTerm)
244 instance Serialise NgramsRepoElement
247 NgramsElement { _ne_ngrams :: NgramsTerm
249 , _ne_list :: ListType
250 , _ne_occurrences :: Int
251 , _ne_root :: Maybe NgramsTerm
252 , _ne_parent :: Maybe NgramsTerm
253 , _ne_children :: MSet NgramsTerm
255 deriving (Ord, Eq, Show, Generic)
257 deriveJSON (unPrefix "_ne_") ''NgramsElement
258 makeLenses ''NgramsElement
260 mkNgramsElement :: NgramsTerm
265 mkNgramsElement ngrams list rp children =
266 NgramsElement ngrams (GCT.size ngrams) list 1 (_rp_root <$> rp) (_rp_parent <$> rp) children
268 newNgramsElement :: Maybe ListType -> NgramsTerm -> NgramsElement
269 newNgramsElement mayList ngrams =
270 mkNgramsElement ngrams (fromMaybe MapTerm mayList) Nothing mempty
272 instance ToSchema NgramsElement where
273 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_ne_")
274 instance Arbitrary NgramsElement where
275 arbitrary = elements [newNgramsElement Nothing "sport"]
277 ngramsElementToRepo :: NgramsElement -> NgramsRepoElement
279 (NgramsElement { _ne_size = s
293 ngramsElementFromRepo :: NgramsTerm -> NgramsRepoElement -> NgramsElement
294 ngramsElementFromRepo
303 NgramsElement { _ne_size = s
308 , _ne_ngrams = ngrams
309 , _ne_occurrences = panic $ "API.Ngrams._ne_occurrences"
311 -- Here we could use 0 if we want to avoid any `panic`.
312 -- It will not happen using getTableNgrams if
313 -- getOccByNgramsOnly provides a count of occurrences for
314 -- all the ngrams given.
318 ------------------------------------------------------------------------
319 newtype NgramsTable = NgramsTable [NgramsElement]
320 deriving (Ord, Eq, Generic, ToJSON, FromJSON, Show)
322 type NgramsList = NgramsTable
324 makePrisms ''NgramsTable
326 -- | Question: why these repetition of Type in this instance
327 -- may you document it please ?
328 instance Each NgramsTable NgramsTable NgramsElement NgramsElement where
329 each = _NgramsTable . each
332 -- | TODO Check N and Weight
334 toNgramsElement :: [NgramsTableData] -> [NgramsElement]
335 toNgramsElement ns = map toNgramsElement' ns
337 toNgramsElement' (NgramsTableData _ p t _ lt w) = NgramsElement t lt' (round w) p' c'
341 Just x -> lookup x mapParent
342 c' = maybe mempty identity $ lookup t mapChildren
343 lt' = maybe (panic "API.Ngrams: listypeId") identity lt
345 mapParent :: Map Int Text
346 mapParent = Map.fromListWith (<>) $ map (\(NgramsTableData i _ t _ _ _) -> (i,t)) ns
348 mapChildren :: Map Text (Set Text)
349 mapChildren = Map.mapKeys (\i -> (maybe (panic "API.Ngrams.mapChildren: ParentId with no Terms: Impossible") identity $ lookup i mapParent))
350 $ Map.fromListWith (<>)
351 $ map (first fromJust)
352 $ filter (isJust . fst)
353 $ map (\(NgramsTableData _ p t _ _ _) -> (p, Set.singleton t)) ns
356 mockTable :: NgramsTable
357 mockTable = NgramsTable
358 [ mkNgramsElement "animal" MapTerm Nothing (mSetFromList ["dog", "cat"])
359 , mkNgramsElement "cat" MapTerm (rp "animal") mempty
360 , mkNgramsElement "cats" StopTerm Nothing mempty
361 , mkNgramsElement "dog" MapTerm (rp "animal") (mSetFromList ["dogs"])
362 , mkNgramsElement "dogs" StopTerm (rp "dog") mempty
363 , mkNgramsElement "fox" MapTerm Nothing mempty
364 , mkNgramsElement "object" CandidateTerm Nothing mempty
365 , mkNgramsElement "nothing" StopTerm Nothing mempty
366 , mkNgramsElement "organic" MapTerm Nothing (mSetFromList ["flower"])
367 , mkNgramsElement "flower" MapTerm (rp "organic") mempty
368 , mkNgramsElement "moon" CandidateTerm Nothing mempty
369 , mkNgramsElement "sky" StopTerm Nothing mempty
372 rp n = Just $ RootParent n n
374 instance Arbitrary NgramsTable where
375 arbitrary = pure mockTable
377 instance ToSchema NgramsTable
379 ------------------------------------------------------------------------
380 type NgramsTableMap = Map NgramsTerm NgramsRepoElement
381 ------------------------------------------------------------------------
382 -- On the Client side:
383 --data Action = InGroup NgramsId NgramsId
384 -- | OutGroup NgramsId NgramsId
385 -- | SetListType NgramsId ListType
387 data PatchSet a = PatchSet
391 deriving (Eq, Ord, Show, Generic)
393 makeLenses ''PatchSet
394 makePrisms ''PatchSet
396 instance ToJSON a => ToJSON (PatchSet a) where
397 toJSON = genericToJSON $ unPrefix "_"
398 toEncoding = genericToEncoding $ unPrefix "_"
400 instance (Ord a, FromJSON a) => FromJSON (PatchSet a) where
401 parseJSON = genericParseJSON $ unPrefix "_"
404 instance (Ord a, Arbitrary a) => Arbitrary (PatchSet a) where
405 arbitrary = PatchSet <$> arbitrary <*> arbitrary
407 type instance Patched (PatchSet a) = Set a
409 type ConflictResolutionPatchSet a = SimpleConflictResolution' (Set a)
410 type instance ConflictResolution (PatchSet a) = ConflictResolutionPatchSet a
412 instance Ord a => Semigroup (PatchSet a) where
413 p <> q = PatchSet { _rem = (q ^. rem) `Set.difference` (p ^. add) <> p ^. rem
414 , _add = (q ^. add) `Set.difference` (p ^. rem) <> p ^. add
417 instance Ord a => Monoid (PatchSet a) where
418 mempty = PatchSet mempty mempty
420 instance Ord a => Group (PatchSet a) where
421 invert (PatchSet r a) = PatchSet a r
423 instance Ord a => Composable (PatchSet a) where
424 composable _ _ = undefined
426 instance Ord a => Action (PatchSet a) (Set a) where
427 act p source = (source `Set.difference` (p ^. rem)) <> p ^. add
429 instance Applicable (PatchSet a) (Set a) where
430 applicable _ _ = mempty
432 instance Ord a => Validity (PatchSet a) where
433 validate p = check (Set.disjoint (p ^. rem) (p ^. add)) "_rem and _add should be dijoint"
435 instance Ord a => Transformable (PatchSet a) where
436 transformable = undefined
438 conflicts _p _q = undefined
440 transformWith conflict p q = undefined conflict p q
442 instance ToSchema a => ToSchema (PatchSet a)
445 type AddRem = Replace (Maybe ())
447 instance Serialise AddRem
449 remPatch, addPatch :: AddRem
450 remPatch = replace (Just ()) Nothing
451 addPatch = replace Nothing (Just ())
453 isRem :: Replace (Maybe ()) -> Bool
454 isRem = (== remPatch)
456 type PatchMap = PM.PatchMap
459 newtype PatchMSet a = PatchMSet (PatchMap a AddRem)
460 deriving (Eq, Show, Generic, Validity, Semigroup, Monoid, Group,
461 Transformable, Composable)
463 type ConflictResolutionPatchMSet a = a -> ConflictResolutionReplace (Maybe ())
464 type instance ConflictResolution (PatchMSet a) = ConflictResolutionPatchMSet a
466 instance (Serialise a, Ord a) => Serialise (PatchMap a AddRem)
467 instance (Serialise a, Ord a) => Serialise (PatchMSet a)
469 -- TODO this breaks module abstraction
470 makePrisms ''PM.PatchMap
472 makePrisms ''PatchMSet
474 _PatchMSetIso :: Ord a => Iso' (PatchMSet a) (PatchSet a)
475 _PatchMSetIso = _PatchMSet . _PatchMap . iso f g . from _PatchSet
477 f :: Ord a => Map a (Replace (Maybe ())) -> (Set a, Set a)
478 f = Map.partition isRem >>> both %~ Map.keysSet
480 g :: Ord a => (Set a, Set a) -> Map a (Replace (Maybe ()))
481 g (rems, adds) = Map.fromSet (const remPatch) rems
482 <> Map.fromSet (const addPatch) adds
484 instance Ord a => Action (PatchMSet a) (MSet a) where
485 act (PatchMSet p) (MSet m) = MSet $ act p m
487 instance Ord a => Applicable (PatchMSet a) (MSet a) where
488 applicable (PatchMSet p) (MSet m) = applicable p m
490 instance (Ord a, ToJSON a) => ToJSON (PatchMSet a) where
491 toJSON = toJSON . view _PatchMSetIso
492 toEncoding = toEncoding . view _PatchMSetIso
494 instance (Ord a, FromJSON a) => FromJSON (PatchMSet a) where
495 parseJSON = fmap (_PatchMSetIso #) . parseJSON
497 instance (Ord a, Arbitrary a) => Arbitrary (PatchMSet a) where
498 arbitrary = (PatchMSet . PM.fromMap) <$> arbitrary
500 instance ToSchema a => ToSchema (PatchMSet a) where
502 declareNamedSchema _ = wellNamedSchema "" (Proxy :: Proxy TODO)
504 type instance Patched (PatchMSet a) = MSet a
506 instance (Eq a, Arbitrary a) => Arbitrary (Replace a) where
507 arbitrary = uncurry replace <$> arbitrary
508 -- If they happen to be equal then the patch is Keep.
510 instance ToSchema a => ToSchema (Replace a) where
511 declareNamedSchema (_ :: Proxy (Replace a)) = do
512 -- TODO Keep constructor is not supported here.
513 aSchema <- declareSchemaRef (Proxy :: Proxy a)
514 return $ NamedSchema (Just "Replace") $ mempty
515 & type_ ?~ SwaggerObject
517 InsOrdHashMap.fromList
521 & required .~ [ "old", "new" ]
524 = NgramsPatch { _patch_children :: PatchMSet NgramsTerm
525 , _patch_list :: Replace ListType -- TODO Map UserId ListType
527 | NgramsReplace { _patch_old :: Maybe NgramsRepoElement
528 , _patch_new :: Maybe NgramsRepoElement
530 deriving (Eq, Show, Generic)
532 -- The JSON encoding is untagged, this is OK since the field names are disjoints and thus the encoding is unambiguous.
533 -- TODO: the empty object should be accepted and treated as mempty.
534 deriveJSON (unPrefixUntagged "_") ''NgramsPatch
535 makeLenses ''NgramsPatch
537 -- TODO: This instance is simplified since we should either have the fields children and/or list
538 -- or the fields old and/or new.
539 instance ToSchema NgramsPatch where
540 declareNamedSchema _ = do
541 childrenSch <- declareSchemaRef (Proxy :: Proxy (PatchMSet NgramsTerm))
542 listSch <- declareSchemaRef (Proxy :: Proxy (Replace ListType))
543 nreSch <- declareSchemaRef (Proxy :: Proxy NgramsRepoElement)
544 return $ NamedSchema (Just "NgramsPatch") $ mempty
545 & type_ ?~ SwaggerObject
547 InsOrdHashMap.fromList
548 [ ("children", childrenSch)
554 instance Arbitrary NgramsPatch where
555 arbitrary = frequency [ (9, NgramsPatch <$> arbitrary <*> (replace <$> arbitrary <*> arbitrary))
556 , (1, NgramsReplace <$> arbitrary <*> arbitrary)
559 instance Serialise NgramsPatch
560 instance Serialise (Replace ListType)
562 instance Serialise ListType
564 type NgramsPatchIso =
565 MaybePatch NgramsRepoElement (PairPatch (PatchMSet NgramsTerm) (Replace ListType))
567 _NgramsPatch :: Iso' NgramsPatch NgramsPatchIso
568 _NgramsPatch = iso unwrap wrap
570 unwrap (NgramsPatch c l) = Mod $ PairPatch (c, l)
571 unwrap (NgramsReplace o n) = replace o n
574 Just (PairPatch (c, l)) -> NgramsPatch c l
575 Nothing -> NgramsReplace (x ^? old . _Just) (x ^? new . _Just)
577 instance Semigroup NgramsPatch where
578 p <> q = _NgramsPatch # (p ^. _NgramsPatch <> q ^. _NgramsPatch)
580 instance Monoid NgramsPatch where
581 mempty = _NgramsPatch # mempty
583 instance Validity NgramsPatch where
584 validate p = p ^. _NgramsPatch . to validate
586 instance Transformable NgramsPatch where
587 transformable p q = transformable (p ^. _NgramsPatch) (q ^. _NgramsPatch)
589 conflicts p q = conflicts (p ^. _NgramsPatch) (q ^. _NgramsPatch)
591 transformWith conflict p q = (_NgramsPatch # p', _NgramsPatch # q')
593 (p', q') = transformWith conflict (p ^. _NgramsPatch) (q ^. _NgramsPatch)
595 type ConflictResolutionNgramsPatch =
596 ( ConflictResolutionReplace (Maybe NgramsRepoElement)
597 , ( ConflictResolutionPatchMSet NgramsTerm
598 , ConflictResolutionReplace ListType
602 type instance ConflictResolution NgramsPatch =
603 ConflictResolutionNgramsPatch
605 type PatchedNgramsPatch = Maybe NgramsRepoElement
606 type instance Patched NgramsPatch = PatchedNgramsPatch
608 instance Applicable (PairPatch (PatchMSet NgramsTerm) (Replace ListType)) NgramsRepoElement where
609 applicable (PairPatch (c, l)) n = applicable c (n ^. nre_children) <> applicable l (n ^. nre_list)
611 instance Action (PairPatch (PatchMSet NgramsTerm) (Replace ListType)) NgramsRepoElement where
612 act (PairPatch (c, l)) = (nre_children %~ act c)
613 . (nre_list %~ act l)
615 instance Applicable NgramsPatch (Maybe NgramsRepoElement) where
616 applicable p = applicable (p ^. _NgramsPatch)
618 instance Action NgramsPatch (Maybe NgramsRepoElement) where
619 act p = act (p ^. _NgramsPatch)
621 newtype NgramsTablePatch = NgramsTablePatch (PatchMap NgramsTerm NgramsPatch)
622 deriving (Eq, Show, Generic, ToJSON, FromJSON, Semigroup, Monoid, Validity, Transformable)
624 instance Serialise NgramsTablePatch
625 instance Serialise (PatchMap NgramsTerm NgramsPatch)
627 instance FromField NgramsTablePatch
629 fromField = fromField'
631 instance FromField (PatchMap TableNgrams.NgramsType (PatchMap NodeId NgramsTablePatch))
633 fromField = fromField'
635 type instance ConflictResolution NgramsTablePatch =
636 NgramsTerm -> ConflictResolutionNgramsPatch
638 type PatchedNgramsTablePatch = Map NgramsTerm PatchedNgramsPatch
639 -- ~ Patched (PatchMap NgramsTerm NgramsPatch)
640 type instance Patched NgramsTablePatch = PatchedNgramsTablePatch
642 makePrisms ''NgramsTablePatch
643 instance ToSchema (PatchMap NgramsTerm NgramsPatch)
644 instance ToSchema NgramsTablePatch
646 instance Applicable NgramsTablePatch (Maybe NgramsTableMap) where
647 applicable p = applicable (p ^. _NgramsTablePatch)
649 instance Action NgramsTablePatch (Maybe NgramsTableMap) where
651 fmap (execState (reParentNgramsTablePatch p)) .
652 act (p ^. _NgramsTablePatch)
654 instance Arbitrary NgramsTablePatch where
655 arbitrary = NgramsTablePatch <$> PM.fromMap <$> arbitrary
657 -- Should it be less than an Lens' to preserve PatchMap's abstraction.
658 -- ntp_ngrams_patches :: Lens' NgramsTablePatch (Map NgramsTerm NgramsPatch)
659 -- ntp_ngrams_patches = _NgramsTablePatch . undefined
661 type ReParent a = forall m. MonadState NgramsTableMap m => a -> m ()
663 reRootChildren :: NgramsTerm -> ReParent NgramsTerm
664 reRootChildren root ngram = do
665 nre <- use $ at ngram
666 forOf_ (_Just . nre_children . folded) nre $ \child -> do
667 at child . _Just . nre_root ?= root
668 reRootChildren root child
670 reParent :: Maybe RootParent -> ReParent NgramsTerm
671 reParent rp child = do
672 at child . _Just %= ( (nre_parent .~ (_rp_parent <$> rp))
673 . (nre_root .~ (_rp_root <$> rp))
675 reRootChildren (fromMaybe child (rp ^? _Just . rp_root)) child
677 reParentAddRem :: RootParent -> NgramsTerm -> ReParent AddRem
678 reParentAddRem rp child p =
679 reParent (if isRem p then Nothing else Just rp) child
681 reParentNgramsPatch :: NgramsTerm -> ReParent NgramsPatch
682 reParentNgramsPatch parent ngramsPatch = do
683 root_of_parent <- use (at parent . _Just . nre_root)
685 root = fromMaybe parent root_of_parent
686 rp = RootParent { _rp_root = root, _rp_parent = parent }
687 itraverse_ (reParentAddRem rp) (ngramsPatch ^. patch_children . _PatchMSet . _PatchMap)
688 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
690 reParentNgramsTablePatch :: ReParent NgramsTablePatch
691 reParentNgramsTablePatch p = itraverse_ reParentNgramsPatch (p ^. _NgramsTablePatch. _PatchMap)
692 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
694 ------------------------------------------------------------------------
695 ------------------------------------------------------------------------
698 data Versioned a = Versioned
699 { _v_version :: Version
702 deriving (Generic, Show, Eq)
703 deriveJSON (unPrefix "_v_") ''Versioned
704 makeLenses ''Versioned
705 instance (Typeable a, ToSchema a) => ToSchema (Versioned a) where
706 declareNamedSchema = wellNamedSchema "_v_"
707 instance Arbitrary a => Arbitrary (Versioned a) where
708 arbitrary = Versioned 1 <$> arbitrary -- TODO 1 is constant so far
712 -- TODO sequences of modifications (Patchs)
713 type NgramsIdPatch = Patch NgramsId NgramsPatch
715 ngramsPatch :: Int -> NgramsPatch
716 ngramsPatch n = NgramsPatch (DM.fromList [(1, StopTerm)]) (Set.fromList [n]) Set.empty
718 toEdit :: NgramsId -> NgramsPatch -> Edit NgramsId NgramsPatch
719 toEdit n p = Edit n p
720 ngramsIdPatch :: Patch NgramsId NgramsPatch
721 ngramsIdPatch = fromList $ catMaybes $ reverse [ replace (1::NgramsId) (Just $ ngramsPatch 1) Nothing
722 , replace (1::NgramsId) Nothing (Just $ ngramsPatch 2)
723 , replace (2::NgramsId) Nothing (Just $ ngramsPatch 2)
726 -- applyPatchBack :: Patch -> IO Patch
727 -- isEmptyPatch = Map.all (\x -> Set.isEmpty (add_children x) && Set.isEmpty ... )
729 ------------------------------------------------------------------------
730 ------------------------------------------------------------------------
731 ------------------------------------------------------------------------
734 -- TODO: Replace.old is ignored which means that if the current list
735 -- `MapTerm` and that the patch is `Replace CandidateTerm StopTerm` then
736 -- the list is going to be `StopTerm` while it should keep `MapTerm`.
737 -- However this should not happen in non conflicting situations.
738 mkListsUpdate :: NgramsType -> NgramsTablePatch -> [(NgramsTypeId, NgramsTerm, ListTypeId)]
739 mkListsUpdate nt patches =
740 [ (ngramsTypeId nt, ng, listTypeId lt)
741 | (ng, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
742 , lt <- patch ^.. patch_list . new
745 mkChildrenGroups :: (PatchSet NgramsTerm -> Set NgramsTerm)
748 -> [(NgramsTypeId, NgramsParent, NgramsChild)]
749 mkChildrenGroups addOrRem nt patches =
750 [ (ngramsTypeId nt, parent, child)
751 | (parent, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
752 , child <- patch ^.. patch_children . to addOrRem . folded
756 ngramsTypeFromTabType :: TabType -> TableNgrams.NgramsType
757 ngramsTypeFromTabType tabType =
758 let lieu = "Garg.API.Ngrams: " :: Text in
760 Sources -> TableNgrams.Sources
761 Authors -> TableNgrams.Authors
762 Institutes -> TableNgrams.Institutes
763 Terms -> TableNgrams.NgramsTerms
764 _ -> panic $ lieu <> "No Ngrams for this tab"
765 -- TODO: This `panic` would disapear with custom NgramsType.
767 ------------------------------------------------------------------------
769 { _r_version :: Version
772 -- first patch in the list is the most recent
776 instance (FromJSON s, FromJSON p) => FromJSON (Repo s p) where
777 parseJSON = genericParseJSON $ unPrefix "_r_"
779 instance (ToJSON s, ToJSON p) => ToJSON (Repo s p) where
780 toJSON = genericToJSON $ unPrefix "_r_"
781 toEncoding = genericToEncoding $ unPrefix "_r_"
783 instance (Serialise s, Serialise p) => Serialise (Repo s p)
787 initRepo :: Monoid s => Repo s p
788 initRepo = Repo 1 mempty []
790 type NgramsRepo = Repo NgramsState NgramsStatePatch
791 type NgramsState = Map TableNgrams.NgramsType (Map NodeId NgramsTableMap)
792 type NgramsStatePatch = PatchMap TableNgrams.NgramsType (PatchMap NodeId NgramsTablePatch)
794 instance Serialise (PM.PatchMap NodeId NgramsTablePatch)
795 instance Serialise NgramsStatePatch
797 initMockRepo :: NgramsRepo
798 initMockRepo = Repo 1 s []
800 s = Map.singleton TableNgrams.NgramsTerms
801 $ Map.singleton 47254
803 [ (n ^. ne_ngrams, ngramsElementToRepo n) | n <- mockTable ^. _NgramsTable ]
805 data RepoEnv = RepoEnv
806 { _renv_var :: !(MVar NgramsRepo)
807 , _renv_saver :: !(IO ())
808 , _renv_lock :: !FileLock
814 class HasRepoVar env where
815 repoVar :: Getter env (MVar NgramsRepo)
817 instance HasRepoVar (MVar NgramsRepo) where
820 class HasRepoSaver env where
821 repoSaver :: Getter env (IO ())
823 class (HasRepoVar env, HasRepoSaver env) => HasRepo env where
824 repoEnv :: Getter env RepoEnv
826 instance HasRepo RepoEnv where
829 instance HasRepoVar RepoEnv where
832 instance HasRepoSaver RepoEnv where
833 repoSaver = renv_saver
835 type RepoCmdM env err m =
838 , MonadBaseControl IO m
841 ------------------------------------------------------------------------
843 saveRepo :: ( MonadReader env m, MonadBase IO m, HasRepoSaver env )
845 saveRepo = liftBase =<< view repoSaver
847 listTypeConflictResolution :: ListType -> ListType -> ListType
848 listTypeConflictResolution _ _ = undefined -- TODO Use Map User ListType
850 ngramsStatePatchConflictResolution
851 :: TableNgrams.NgramsType
854 -> ConflictResolutionNgramsPatch
855 ngramsStatePatchConflictResolution _ngramsType _nodeId _ngramsTerm
856 = (ours, (const ours, ours), (False, False))
857 -- (False, False) mean here that Mod has always priority.
858 -- (True, False) <- would mean priority to the left (same as ours).
860 -- undefined {- TODO think this through -}, listTypeConflictResolution)
863 -- Insertions are not considered as patches,
864 -- they do not extend history,
865 -- they do not bump version.
866 insertNewOnly :: a -> Maybe b -> a
867 insertNewOnly m = maybe m (const $ error "insertNewOnly: impossible")
868 -- TODO error handling
870 something :: Monoid a => Maybe a -> a
871 something Nothing = mempty
872 something (Just a) = a
875 -- TODO refactor with putListNgrams
876 copyListNgrams :: RepoCmdM env err m
877 => NodeId -> NodeId -> NgramsType
879 copyListNgrams srcListId dstListId ngramsType = do
881 liftBase $ modifyMVar_ var $
882 pure . (r_state . at ngramsType %~ (Just . f . something))
885 f :: Map NodeId NgramsTableMap -> Map NodeId NgramsTableMap
886 f m = m & at dstListId %~ insertNewOnly (m ^. at srcListId)
888 -- TODO refactor with putListNgrams
889 -- The list must be non-empty!
890 -- The added ngrams must be non-existent!
891 addListNgrams :: RepoCmdM env err m
892 => NodeId -> NgramsType
893 -> [NgramsElement] -> m ()
894 addListNgrams listId ngramsType nes = do
896 liftBase $ modifyMVar_ var $
897 pure . (r_state . at ngramsType . _Just . at listId . _Just <>~ m)
900 m = Map.fromList $ (\n -> (n ^. ne_ngrams, n)) <$> nes
904 rmListNgrams :: RepoCmdM env err m
906 -> TableNgrams.NgramsType
908 rmListNgrams l nt = setListNgrams l nt mempty
910 -- | TODO: incr the Version number
911 -- && should use patch
913 setListNgrams :: RepoCmdM env err m
915 -> TableNgrams.NgramsType
916 -> Map NgramsTerm NgramsRepoElement
918 setListNgrams listId ngramsType ns = do
920 liftBase $ modifyMVar_ var $
924 (at listId .~ ( Just ns))
931 -- This is no longer part of the API.
932 -- This function is maintained for its usage in Database.Action.Flow.List.
933 -- If the given list of ngrams elements contains ngrams already in
934 -- the repo, they will be ignored.
935 putListNgrams :: (HasInvalidError err, RepoCmdM env err m)
937 -> TableNgrams.NgramsType
938 -> [NgramsElement] -> m ()
939 putListNgrams _ _ [] = pure ()
940 putListNgrams nodeId ngramsType nes = putListNgrams' nodeId ngramsType m
942 m = Map.fromList $ map (\n -> (n ^. ne_ngrams, ngramsElementToRepo n)) nes
944 putListNgrams' :: (HasInvalidError err, RepoCmdM env err m)
946 -> TableNgrams.NgramsType
947 -> Map NgramsTerm NgramsRepoElement
949 putListNgrams' nodeId ngramsType ns = do
950 -- printDebug "[putLictNgrams'] nodeId" nodeId
951 -- printDebug "[putLictNgrams'] ngramsType" ngramsType
952 -- printDebug "[putListNgrams'] ns" ns
954 let p1 = NgramsTablePatch . PM.fromMap $ NgramsReplace Nothing . Just <$> ns
955 (p0, p0_validity) = PM.singleton nodeId p1
956 (p, p_validity) = PM.singleton ngramsType p0
957 assertValid p0_validity
958 assertValid p_validity
962 q <- commitStatePatch (Versioned v p)
964 -- What if another commit comes in between?
965 -- Shall we have a blindCommitStatePatch? It would not ask for a version but just a patch.
966 -- The modifyMVar_ would test the patch with applicable first.
967 -- If valid the rest would be atomic and no merge is required.
970 liftBase $ modifyMVar_ var $ \r -> do
971 pure $ r & r_version +~ 1
973 & r_state . at ngramsType %~
986 currentVersion :: RepoCmdM env err m
990 r <- liftBase $ readMVar var
991 pure $ r ^. r_version
994 -- tableNgramsPut :: (HasInvalidError err, RepoCmdM env err m)
995 commitStatePatch :: RepoCmdM env err m => Versioned NgramsStatePatch -> m (Versioned NgramsStatePatch)
996 commitStatePatch (Versioned p_version p) = do
998 vq' <- liftBase $ modifyMVar var $ \r -> do
1000 q = mconcat $ take (r ^. r_version - p_version) (r ^. r_history)
1001 (p', q') = transformWith ngramsStatePatchConflictResolution p q
1002 r' = r & r_version +~ 1
1004 & r_history %~ (p' :)
1006 -- Ideally we would like to check these properties. However:
1007 -- * They should be checked only to debug the code. The client data
1008 -- should be able to trigger these.
1009 -- * What kind of error should they throw (we are in IO here)?
1010 -- * Should we keep modifyMVar?
1011 -- * Should we throw the validation in an Exception, catch it around
1012 -- modifyMVar and throw it back as an Error?
1013 assertValid $ transformable p q
1014 assertValid $ applicable p' (r ^. r_state)
1016 pure (r', Versioned (r' ^. r_version) q')
1021 -- This is a special case of tableNgramsPut where the input patch is empty.
1022 tableNgramsPull :: RepoCmdM env err m
1024 -> TableNgrams.NgramsType
1026 -> m (Versioned NgramsTablePatch)
1027 tableNgramsPull listId ngramsType p_version = do
1029 r <- liftBase $ readMVar var
1032 q = mconcat $ take (r ^. r_version - p_version) (r ^. r_history)
1033 q_table = q ^. _PatchMap . at ngramsType . _Just . _PatchMap . at listId . _Just
1035 pure (Versioned (r ^. r_version) q_table)
1037 -- Apply the given patch to the DB and returns the patch to be applied on the
1039 -- TODO-ACCESS check
1040 tableNgramsPut :: (HasInvalidError err, RepoCmdM env err m)
1041 => TabType -> ListId
1042 -> Versioned NgramsTablePatch
1043 -> m (Versioned NgramsTablePatch)
1044 tableNgramsPut tabType listId (Versioned p_version p_table)
1045 | p_table == mempty = do
1046 let ngramsType = ngramsTypeFromTabType tabType
1047 tableNgramsPull listId ngramsType p_version
1050 let ngramsType = ngramsTypeFromTabType tabType
1051 (p0, p0_validity) = PM.singleton listId p_table
1052 (p, p_validity) = PM.singleton ngramsType p0
1054 assertValid p0_validity
1055 assertValid p_validity
1057 commitStatePatch (Versioned p_version p)
1058 <&> v_data %~ (view (_PatchMap . at ngramsType . _Just . _PatchMap . at listId . _Just))
1060 mergeNgramsElement :: NgramsRepoElement -> NgramsRepoElement -> NgramsRepoElement
1061 mergeNgramsElement _neOld neNew = neNew
1063 { _ne_list :: ListType
1064 If we merge the parents/children we can potentially create cycles!
1065 , _ne_parent :: Maybe NgramsTerm
1066 , _ne_children :: MSet NgramsTerm
1070 getNgramsTableMap :: RepoCmdM env err m
1072 -> TableNgrams.NgramsType
1073 -> m (Versioned NgramsTableMap)
1074 getNgramsTableMap nodeId ngramsType = do
1076 repo <- liftBase $ readMVar v
1077 pure $ Versioned (repo ^. r_version)
1078 (repo ^. r_state . at ngramsType . _Just . at nodeId . _Just)
1080 dumpJsonTableMap :: RepoCmdM env err m
1083 -> TableNgrams.NgramsType
1085 dumpJsonTableMap fpath nodeId ngramsType = do
1086 m <- getNgramsTableMap nodeId ngramsType
1087 liftBase $ DTL.writeFile (unpack fpath) (DAT.encodeToLazyText m)
1093 -- | TODO Errors management
1094 -- TODO: polymorphic for Annuaire or Corpus or ...
1095 -- | Table of Ngrams is a ListNgrams formatted (sorted and/or cut).
1096 -- TODO: should take only one ListId
1098 getTime' :: MonadBase IO m => m TimeSpec
1099 getTime' = liftBase $ getTime ProcessCPUTime
1102 getTableNgrams :: forall env err m.
1103 (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1104 => NodeType -> NodeId -> TabType
1105 -> ListId -> Limit -> Maybe Offset
1107 -> Maybe MinSize -> Maybe MaxSize
1109 -> (NgramsTerm -> Bool)
1110 -> m (Versioned NgramsTable)
1111 getTableNgrams _nType nId tabType listId limit_ offset
1112 listType minSize maxSize orderBy searchQuery = do
1115 -- lIds <- selectNodesWithUsername NodeList userMaster
1117 ngramsType = ngramsTypeFromTabType tabType
1118 offset' = maybe 0 identity offset
1119 listType' = maybe (const True) (==) listType
1120 minSize' = maybe (const True) (<=) minSize
1121 maxSize' = maybe (const True) (>=) maxSize
1123 selected_node n = minSize' s
1125 && searchQuery (n ^. ne_ngrams)
1126 && listType' (n ^. ne_list)
1130 selected_inner roots n = maybe False (`Set.member` roots) (n ^. ne_root)
1132 ---------------------------------------
1133 sortOnOrder Nothing = identity
1134 sortOnOrder (Just TermAsc) = List.sortOn $ view ne_ngrams
1135 sortOnOrder (Just TermDesc) = List.sortOn $ Down . view ne_ngrams
1136 sortOnOrder (Just ScoreAsc) = List.sortOn $ view ne_occurrences
1137 sortOnOrder (Just ScoreDesc) = List.sortOn $ Down . view ne_occurrences
1139 ---------------------------------------
1140 selectAndPaginate :: Map NgramsTerm NgramsElement -> [NgramsElement]
1141 selectAndPaginate tableMap = roots <> inners
1143 list = tableMap ^.. each
1144 rootOf ne = maybe ne (\r -> fromMaybe (panic "getTableNgrams: invalid root") (tableMap ^. at r))
1146 selected_nodes = list & take limit_
1148 . filter selected_node
1149 . sortOnOrder orderBy
1150 roots = rootOf <$> selected_nodes
1151 rootsSet = Set.fromList (_ne_ngrams <$> roots)
1152 inners = list & filter (selected_inner rootsSet)
1154 ---------------------------------------
1155 setScores :: forall t. Each t t NgramsElement NgramsElement => Bool -> t -> m t
1156 setScores False table = pure table
1157 setScores True table = do
1158 let ngrams_terms = (table ^.. each . ne_ngrams)
1160 occurrences <- getOccByNgramsOnlyFast' nId
1165 liftBase $ hprint stderr
1166 ("getTableNgrams/setScores #ngrams=" % int % " time=" % timeSpecs % "\n")
1167 (length ngrams_terms) t1 t2
1169 occurrences <- getOccByNgramsOnlySlow nType nId
1175 setOcc ne = ne & ne_occurrences .~ sumOf (at (ne ^. ne_ngrams) . _Just) occurrences
1177 pure $ table & each %~ setOcc
1178 ---------------------------------------
1180 -- lists <- catMaybes <$> listsWith userMaster
1181 -- trace (show lists) $
1182 -- getNgramsTableMap ({-lists <>-} listIds) ngramsType
1184 let scoresNeeded = needsScores orderBy
1185 tableMap1 <- getNgramsTableMap listId ngramsType
1187 tableMap2 <- tableMap1 & v_data %%~ setScores scoresNeeded
1188 . Map.mapWithKey ngramsElementFromRepo
1190 tableMap3 <- tableMap2 & v_data %%~ fmap NgramsTable
1191 . setScores (not scoresNeeded)
1194 liftBase $ hprint stderr
1195 ("getTableNgrams total=" % timeSpecs
1196 % " map1=" % timeSpecs
1197 % " map2=" % timeSpecs
1198 % " map3=" % timeSpecs
1199 % " sql=" % (if scoresNeeded then "map2" else "map3")
1201 ) t0 t3 t0 t1 t1 t2 t2 t3
1205 scoresRecomputeTableNgrams :: forall env err m. (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env) => NodeId -> TabType -> ListId -> m Int
1206 scoresRecomputeTableNgrams nId tabType listId = do
1207 tableMap <- getNgramsTableMap listId ngramsType
1208 _ <- tableMap & v_data %%~ setScores
1209 . Map.mapWithKey ngramsElementFromRepo
1213 ngramsType = ngramsTypeFromTabType tabType
1215 setScores :: forall t. Each t t NgramsElement NgramsElement => t -> m t
1216 setScores table = do
1217 let ngrams_terms = (table ^.. each . ne_ngrams)
1218 occurrences <- getOccByNgramsOnlyFast' nId
1223 setOcc ne = ne & ne_occurrences .~ sumOf (at (ne ^. ne_ngrams) . _Just) occurrences
1225 pure $ table & each %~ setOcc
1231 -- TODO: find a better place for the code above, All APIs stay here
1232 type QueryParamR = QueryParam' '[Required, Strict]
1234 data OrderBy = TermAsc | TermDesc | ScoreAsc | ScoreDesc
1235 deriving (Generic, Enum, Bounded, Read, Show)
1237 instance FromHttpApiData OrderBy
1239 parseUrlPiece "TermAsc" = pure TermAsc
1240 parseUrlPiece "TermDesc" = pure TermDesc
1241 parseUrlPiece "ScoreAsc" = pure ScoreAsc
1242 parseUrlPiece "ScoreDesc" = pure ScoreDesc
1243 parseUrlPiece _ = Left "Unexpected value of OrderBy"
1246 instance ToParamSchema OrderBy
1247 instance FromJSON OrderBy
1248 instance ToJSON OrderBy
1249 instance ToSchema OrderBy
1250 instance Arbitrary OrderBy
1252 arbitrary = elements [minBound..maxBound]
1254 needsScores :: Maybe OrderBy -> Bool
1255 needsScores (Just ScoreAsc) = True
1256 needsScores (Just ScoreDesc) = True
1257 needsScores _ = False
1259 type TableNgramsApiGet = Summary " Table Ngrams API Get"
1260 :> QueryParamR "ngramsType" TabType
1261 :> QueryParamR "list" ListId
1262 :> QueryParamR "limit" Limit
1263 :> QueryParam "offset" Offset
1264 :> QueryParam "listType" ListType
1265 :> QueryParam "minTermSize" MinSize
1266 :> QueryParam "maxTermSize" MaxSize
1267 :> QueryParam "orderBy" OrderBy
1268 :> QueryParam "search" Text
1269 :> Get '[JSON] (Versioned NgramsTable)
1271 type TableNgramsApiPut = Summary " Table Ngrams API Change"
1272 :> QueryParamR "ngramsType" TabType
1273 :> QueryParamR "list" ListId
1274 :> ReqBody '[JSON] (Versioned NgramsTablePatch)
1275 :> Put '[JSON] (Versioned NgramsTablePatch)
1277 type RecomputeScoresNgramsApiGet = Summary " Recompute scores for ngrams table"
1278 :> QueryParamR "ngramsType" TabType
1279 :> QueryParamR "list" ListId
1280 :> "recompute" :> Post '[JSON] Int
1282 type TableNgramsApiGetVersion = Summary " Table Ngrams API Get Version"
1283 :> QueryParamR "ngramsType" TabType
1284 :> QueryParamR "list" ListId
1285 :> Get '[JSON] Version
1287 type TableNgramsApi = TableNgramsApiGet
1288 :<|> TableNgramsApiPut
1289 :<|> RecomputeScoresNgramsApiGet
1290 :<|> "version" :> TableNgramsApiGetVersion
1292 getTableNgramsCorpus :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1299 -> Maybe MinSize -> Maybe MaxSize
1301 -> Maybe Text -- full text search
1302 -> m (Versioned NgramsTable)
1303 getTableNgramsCorpus nId tabType listId limit_ offset listType minSize maxSize orderBy mt =
1304 getTableNgrams NodeCorpus nId tabType listId limit_ offset listType minSize maxSize orderBy searchQuery
1306 searchQuery = maybe (const True) isInfixOf mt
1308 getTableNgramsVersion :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1313 getTableNgramsVersion _nId _tabType _listId = currentVersion
1315 -- Versioned { _v_version = v } <- getTableNgramsCorpus nId tabType listId 100000 Nothing Nothing Nothing Nothing Nothing Nothing
1316 -- This line above looks like a waste of computation to finally get only the version.
1317 -- See the comment about listNgramsChangedSince.
1320 -- | Text search is deactivated for now for ngrams by doc only
1321 getTableNgramsDoc :: (RepoCmdM env err m, HasNodeError err, HasConnectionPool env, HasConfig env)
1323 -> ListId -> Limit -> Maybe Offset
1325 -> Maybe MinSize -> Maybe MaxSize
1327 -> Maybe Text -- full text search
1328 -> m (Versioned NgramsTable)
1329 getTableNgramsDoc dId tabType listId limit_ offset listType minSize maxSize orderBy _mt = do
1330 ns <- selectNodesWithUsername NodeList userMaster
1331 let ngramsType = ngramsTypeFromTabType tabType
1332 ngs <- selectNgramsByDoc (ns <> [listId]) dId ngramsType
1333 let searchQuery = flip S.member (S.fromList ngs)
1334 getTableNgrams NodeDocument dId tabType listId limit_ offset listType minSize maxSize orderBy searchQuery
1338 apiNgramsTableCorpus :: ( RepoCmdM env err m
1340 , HasInvalidError err
1341 , HasConnectionPool env
1344 => NodeId -> ServerT TableNgramsApi m
1345 apiNgramsTableCorpus cId = getTableNgramsCorpus cId
1347 :<|> scoresRecomputeTableNgrams cId
1348 :<|> getTableNgramsVersion cId
1350 apiNgramsTableDoc :: ( RepoCmdM env err m
1352 , HasInvalidError err
1353 , HasConnectionPool env
1356 => DocId -> ServerT TableNgramsApi m
1357 apiNgramsTableDoc dId = getTableNgramsDoc dId
1359 :<|> scoresRecomputeTableNgrams dId
1360 :<|> getTableNgramsVersion dId
1361 -- > index all the corpus accordingly (TODO AD)
1363 -- Did the given list of ngrams changed since the given version?
1364 -- The returned value is versioned boolean value, meaning that one always retrieve the
1366 -- If the given version is negative then one simply receive the latest version and True.
1367 -- Using this function is more precise than simply comparing the latest version number
1368 -- with the local version number. Indeed there might be no change to this particular list
1369 -- and still the version number has changed because of other lists.
1371 -- Here the added value is to make a compromise between precision, computation, and bandwidth:
1372 -- * currentVersion: good computation, good bandwidth, bad precision.
1373 -- * listNgramsChangedSince: good precision, good bandwidth, bad computation.
1374 -- * tableNgramsPull: good precision, good bandwidth (if you use the received data!), bad computation.
1375 listNgramsChangedSince :: RepoCmdM env err m
1376 => ListId -> TableNgrams.NgramsType -> Version -> m (Versioned Bool)
1377 listNgramsChangedSince listId ngramsType version
1379 Versioned <$> currentVersion <*> pure True
1381 tableNgramsPull listId ngramsType version & mapped . v_data %~ (== mempty)
1384 instance Arbitrary NgramsRepoElement where
1385 arbitrary = elements $ map ngramsElementToRepo ns
1387 NgramsTable ns = mockTable
1390 instance FromHttpApiData (Map TableNgrams.NgramsType (Versioned NgramsTableMap))
1392 parseUrlPiece x = maybeToEither x (decode $ cs x)