[pubmed] implement pubmed api key in hyperdata user

parent 6bc41d73
...@@ -87,6 +87,7 @@ data Query m ...@@ -87,6 +87,7 @@ data Query m
data Mutation m data Mutation m
= Mutation = Mutation
{ update_user_info :: GQLUserInfo.UserInfoMArgs -> m Int { update_user_info :: GQLUserInfo.UserInfoMArgs -> m Int
, update_user_pubmed_api_key :: GQLUser.UserPubmedAPIKeyMArgs -> m Int
, delete_team_membership :: GQLTeam.TeamDeleteMArgs -> m [Int] , delete_team_membership :: GQLTeam.TeamDeleteMArgs -> m [Int]
, update_node_context_category :: GQLCTX.NodeContextCategoryMArgs -> m [Int] , update_node_context_category :: GQLCTX.NodeContextCategoryMArgs -> m [Int]
} deriving (Generic, GQLType) } deriving (Generic, GQLType)
...@@ -128,6 +129,7 @@ rootResolver = ...@@ -128,6 +129,7 @@ rootResolver =
, tree = GQLTree.resolveTree , tree = GQLTree.resolveTree
, team = GQLTeam.resolveTeam } , team = GQLTeam.resolveTeam }
, mutationResolver = Mutation { update_user_info = GQLUserInfo.updateUserInfo , mutationResolver = Mutation { update_user_info = GQLUserInfo.updateUserInfo
, update_user_pubmed_api_key = GQLUser.updateUserPubmedAPIKey
, delete_team_membership = GQLTeam.deleteTeamMembership , delete_team_membership = GQLTeam.deleteTeamMembership
, update_node_context_category = GQLCTX.updateNodeContextCategory } , update_node_context_category = GQLCTX.updateNodeContextCategory }
, subscriptionResolver = Undefined } , subscriptionResolver = Undefined }
......
...@@ -30,7 +30,6 @@ data Corpus = Corpus ...@@ -30,7 +30,6 @@ data Corpus = Corpus
{ id :: Int { id :: Int
, name :: Text , name :: Text
, parent_id :: Maybe Int , parent_id :: Maybe Int
, pubmedAPIKey :: Maybe PUBMED.APIKey
, type_id :: Int , type_id :: Int
} deriving (Show, Generic, GQLType) } deriving (Show, Generic, GQLType)
...@@ -116,7 +115,6 @@ toCorpus :: NN.Node Value -> Corpus ...@@ -116,7 +115,6 @@ toCorpus :: NN.Node Value -> Corpus
toCorpus N.Node { .. } = Corpus { id = NN.unNodeId _node_id toCorpus N.Node { .. } = Corpus { id = NN.unNodeId _node_id
, name = _node_name , name = _node_name
, parent_id = NN.unNodeId <$> _node_parent_id , parent_id = NN.unNodeId <$> _node_parent_id
, pubmedAPIKey = pubmedAPIKeyFromValue _node_hyperdata
, type_id = _node_typename } , type_id = _node_typename }
pubmedAPIKeyFromValue :: Value -> Maybe PUBMED.APIKey pubmedAPIKeyFromValue :: Value -> Maybe PUBMED.APIKey
......
...@@ -11,6 +11,7 @@ import Gargantext.API.Admin.Types (HasSettings) ...@@ -11,6 +11,7 @@ import Gargantext.API.Admin.Types (HasSettings)
import Gargantext.API.GraphQL.Utils (authUser, AuthStatus (Invalid, Valid)) import Gargantext.API.GraphQL.Utils (authUser, AuthStatus (Invalid, Valid))
import Gargantext.API.Prelude (GargM, GargError) import Gargantext.API.Prelude (GargM, GargError)
import Gargantext.Core.Types (NodeId(..), unNodeId) import Gargantext.Core.Types (NodeId(..), unNodeId)
import qualified Gargantext.Core.Types.Individu as Individu
import Gargantext.Database.Action.Share (membersOf, deleteMemberShip) import Gargantext.Database.Action.Share (membersOf, deleteMemberShip)
import Gargantext.Database.Prelude (CmdCommon) import Gargantext.Database.Prelude (CmdCommon)
import Gargantext.Database.Query.Table.Node (getNode) import Gargantext.Database.Query.Table.Node (getNode)
...@@ -52,7 +53,7 @@ dbTeam nodeId = do ...@@ -52,7 +53,7 @@ dbTeam nodeId = do
let nId = NodeId nodeId let nId = NodeId nodeId
res <- lift $ membersOf nId res <- lift $ membersOf nId
teamNode <- lift $ getNode nId teamNode <- lift $ getNode nId
userNodes <- lift $ getUsersWithNodeHyperdata $ uId teamNode userNodes <- lift $ getUsersWithNodeHyperdata $ Individu.UserDBId $ uId teamNode
let username = getUsername userNodes let username = getUsername userNodes
pure $ Team { team_owner_username = username pure $ Team { team_owner_username = username
, team_members = map toTeamMember res , team_members = map toTeamMember res
...@@ -72,7 +73,7 @@ deleteTeamMembership :: (CmdCommon env, HasSettings env) => ...@@ -72,7 +73,7 @@ deleteTeamMembership :: (CmdCommon env, HasSettings env) =>
TeamDeleteMArgs -> GqlM' e env [Int] TeamDeleteMArgs -> GqlM' e env [Int]
deleteTeamMembership TeamDeleteMArgs { token, shared_folder_id, team_node_id } = do deleteTeamMembership TeamDeleteMArgs { token, shared_folder_id, team_node_id } = do
teamNode <- lift $ getNode $ NodeId team_node_id teamNode <- lift $ getNode $ NodeId team_node_id
userNodes <- lift (getUsersWithNodeHyperdata $ uId teamNode) userNodes <- lift (getUsersWithNodeHyperdata $ Individu.UserDBId $ uId teamNode)
case userNodes of case userNodes of
[] -> panic $ "[deleteTeamMembership] User with id " <> T.pack (show $ uId teamNode) <> " doesn't exist." [] -> panic $ "[deleteTeamMembership] User with id " <> T.pack (show $ uId teamNode) <> " doesn't exist."
(( _, node_u):_) -> do (( _, node_u):_) -> do
......
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Gargantext.API.GraphQL.User where module Gargantext.API.GraphQL.User where
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
import Data.Morpheus.Types import Data.Morpheus.Types
( GQLType ( GQLType
, Resolver, QUERY , Resolver, ResolverM, QUERY
, lift , lift
) )
import Data.Text (Text) import Data.Text (Text)
import Gargantext.API.Admin.Types (HasSettings)
import Gargantext.API.Prelude (GargM, GargError) import Gargantext.API.Prelude (GargM, GargError)
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser(..)) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser(..))
import Gargantext.Database.Admin.Types.Node (NodeId(..))
import Gargantext.Database.Prelude (CmdCommon) import Gargantext.Database.Prelude (CmdCommon)
import Gargantext.Database.Query.Table.User (getUsersWithId, getUserHyperdata) import qualified Gargantext.Database.Query.Table.User as DBUser
import Gargantext.Database.Schema.User (UserLight(..)) import Gargantext.Database.Schema.User (UserLight(..))
import Gargantext.Prelude import Gargantext.Prelude
import GHC.Generics (Generic) import GHC.Generics (Generic)
import qualified Gargantext.Core.Types.Individu as Individu
data User m = User data User m = User
{ u_email :: Text { u_email :: Text
...@@ -30,7 +34,14 @@ data UserArgs ...@@ -30,7 +34,14 @@ data UserArgs
{ user_id :: Int { user_id :: Int
} deriving (Generic, GQLType) } deriving (Generic, GQLType)
data UserPubmedAPIKeyMArgs
= UserPubmedAPIKeyMArgs
{ user_id :: Int
, api_key :: Text }
deriving (Generic, GQLType)
type GqlM e env = Resolver QUERY e (GargM env GargError) type GqlM e env = Resolver QUERY e (GargM env GargError)
type GqlM' e env a = ResolverM e (GargM env GargError) a
-- | Function to resolve user from a query. -- | Function to resolve user from a query.
resolveUsers resolveUsers
...@@ -42,7 +53,7 @@ resolveUsers UserArgs { user_id } = dbUsers user_id ...@@ -42,7 +53,7 @@ resolveUsers UserArgs { user_id } = dbUsers user_id
dbUsers dbUsers
:: (CmdCommon env) :: (CmdCommon env)
=> Int -> GqlM e env [User (GqlM e env)] => Int -> GqlM e env [User (GqlM e env)]
dbUsers user_id = lift (map toUser <$> getUsersWithId user_id) dbUsers user_id = lift (map toUser <$> DBUser.getUsersWithId (Individu.RootId $ NodeId user_id))
toUser toUser
:: (CmdCommon env) :: (CmdCommon env)
...@@ -55,4 +66,11 @@ toUser (UserLight { .. }) = User { u_email = userLight_email ...@@ -55,4 +66,11 @@ toUser (UserLight { .. }) = User { u_email = userLight_email
resolveHyperdata resolveHyperdata
:: (CmdCommon env) :: (CmdCommon env)
=> Int -> GqlM e env (Maybe HyperdataUser) => Int -> GqlM e env (Maybe HyperdataUser)
resolveHyperdata userid = lift (listToMaybe <$> getUserHyperdata userid) resolveHyperdata userid = lift (listToMaybe <$> DBUser.getUserHyperdata (Individu.UserDBId userid))
updateUserPubmedAPIKey :: ( CmdCommon env, HasSettings env) =>
UserPubmedAPIKeyMArgs -> GqlM' e env Int
updateUserPubmedAPIKey UserPubmedAPIKeyMArgs { user_id, api_key } = do
_ <- lift $ DBUser.updateUserPubmedAPIKey (Individu.RootId $ NodeId user_id) api_key
pure 1
...@@ -48,6 +48,7 @@ import Gargantext.Prelude ...@@ -48,6 +48,7 @@ import Gargantext.Prelude
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Gargantext.API.GraphQL.Utils (AuthStatus(Invalid, Valid), authUser) import Gargantext.API.GraphQL.Utils (AuthStatus(Invalid, Valid), authUser)
import Gargantext.API.Admin.Types (HasSettings) import Gargantext.API.Admin.Types (HasSettings)
import qualified Gargantext.Core.Types.Individu as Individu
data UserInfo = UserInfo data UserInfo = UserInfo
{ ui_id :: Int { ui_id :: Int
...@@ -115,7 +116,7 @@ updateUserInfo ...@@ -115,7 +116,7 @@ updateUserInfo
=> UserInfoMArgs -> GqlM' e env err => UserInfoMArgs -> GqlM' e env err
updateUserInfo (UserInfoMArgs { ui_id, .. }) = do updateUserInfo (UserInfoMArgs { ui_id, .. }) = do
-- lift $ printDebug "[updateUserInfo] ui_id" ui_id -- lift $ printDebug "[updateUserInfo] ui_id" ui_id
users <- lift (getUsersWithNodeHyperdata ui_id) users <- lift (getUsersWithNodeHyperdata (Individu.UserDBId ui_id))
case users of case users of
[] -> panic $ "[updateUserInfo] User with id " <> (T.pack $ show ui_id) <> " doesn't exist." [] -> panic $ "[updateUserInfo] User with id " <> (T.pack $ show ui_id) <> " doesn't exist."
((UserLight { .. }, node_u):_) -> do ((UserLight { .. }, node_u):_) -> do
...@@ -166,7 +167,7 @@ dbUsers user_id = do ...@@ -166,7 +167,7 @@ dbUsers user_id = do
-- user <- getUsersWithId user_id -- user <- getUsersWithId user_id
-- hyperdata <- getUserHyperdata user_id -- hyperdata <- getUserHyperdata user_id
-- lift (map toUser <$> zip user hyperdata) -- lift (map toUser <$> zip user hyperdata)
lift (map toUser <$> (getUsersWithHyperdata user_id)) lift (map toUser <$> getUsersWithHyperdata (Individu.UserDBId user_id))
toUser :: (UserLight, HyperdataUser) -> UserInfo toUser :: (UserLight, HyperdataUser) -> UserInfo
toUser (UserLight { .. }, u_hyperdata) = toUser (UserLight { .. }, u_hyperdata) =
......
...@@ -56,11 +56,12 @@ import Gargantext.Database.Action.User (getUserId) ...@@ -56,11 +56,12 @@ import Gargantext.Database.Action.User (getUserId)
import Gargantext.Database.Admin.Types.Hyperdata import Gargantext.Database.Admin.Types.Hyperdata
import Gargantext.Database.Admin.Types.Node (CorpusId, NodeType(..)) import Gargantext.Database.Admin.Types.Node (CorpusId, NodeType(..))
import Gargantext.Database.Prelude (hasConfig) import Gargantext.Database.Prelude (hasConfig)
import Gargantext.Database.Query.Table.Node (getNodeWith, updateCorpusPubmedAPIKey) import Gargantext.Database.Query.Table.Node (getNodeWith)
import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata) import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
import Gargantext.Database.Query.Table.User (getUserPubmedAPIKey)
import Gargantext.Database.Schema.Node (node_hyperdata) import Gargantext.Database.Schema.Node (node_hyperdata)
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config (gc_max_docs_parsers, gc_pubmed_api_key) import Gargantext.Prelude.Config (gc_max_docs_parsers)
import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..)) import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..))
import qualified Gargantext.Core.Text.Corpus.API as API import qualified Gargantext.Core.Text.Corpus.API as API
import qualified Gargantext.Core.Text.Corpus.Parsers as Parser (FileType(..), parseFormatC) import qualified Gargantext.Core.Text.Corpus.Parsers as Parser (FileType(..), parseFormatC)
...@@ -215,13 +216,6 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q ...@@ -215,13 +216,6 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q
markComplete jobHandle markComplete jobHandle
_ -> do _ -> do
case datafield of
Just (External PubMed) -> do
_api_key <- view $ hasConfig . gc_pubmed_api_key
printDebug "[addToCorpusWithQuery] pubmed api key" _api_key
_ <- updateCorpusPubmedAPIKey cid _api_key
pure ()
_ -> pure ()
markStarted 3 jobHandle markStarted 3 jobHandle
-- TODO add cid -- TODO add cid
...@@ -230,7 +224,9 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q ...@@ -230,7 +224,9 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q
-- if cid is root -> create corpus in Private -- if cid is root -> create corpus in Private
-- printDebug "[G.A.N.C.New] getDataText with query" q -- printDebug "[G.A.N.C.New] getDataText with query" q
let db = database2origin dbs let db = database2origin dbs
eTxt <- getDataText db (Multi l) q maybeLimit mPubmedAPIKey <- getUserPubmedAPIKey user
-- printDebug "[addToCorpusWithQuery] mPubmedAPIKey" mPubmedAPIKey
eTxt <- getDataText db (Multi l) q mPubmedAPIKey maybeLimit
-- printDebug "[G.A.N.C.New] lTxts" lTxts -- printDebug "[G.A.N.C.New] lTxts" lTxts
case eTxt of case eTxt of
......
...@@ -19,7 +19,6 @@ module Gargantext.Core.Text.Corpus.API ...@@ -19,7 +19,6 @@ module Gargantext.Core.Text.Corpus.API
) where ) where
import Conduit import Conduit
import Control.Lens ((^.))
import Data.Bifunctor import Data.Bifunctor
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Maybe import Data.Maybe
...@@ -28,13 +27,13 @@ import Gargantext.API.Admin.Orchestrator.Types (ExternalAPIs(..), externalAPIs) ...@@ -28,13 +27,13 @@ import Gargantext.API.Admin.Orchestrator.Types (ExternalAPIs(..), externalAPIs)
import Gargantext.Core (Lang(..)) import Gargantext.Core (Lang(..))
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..)) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..))
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config (GargConfig, gc_pubmed_api_key)
import qualified Gargantext.Core.Text.Corpus.API.Arxiv as Arxiv import qualified Gargantext.Core.Text.Corpus.API.Arxiv as Arxiv
import qualified Gargantext.Core.Text.Corpus.API.Hal as HAL import qualified Gargantext.Core.Text.Corpus.API.Hal as HAL
import qualified Gargantext.Core.Text.Corpus.API.Isidore as ISIDORE import qualified Gargantext.Core.Text.Corpus.API.Isidore as ISIDORE
import qualified Gargantext.Core.Text.Corpus.API.Istex as ISTEX import qualified Gargantext.Core.Text.Corpus.API.Istex as ISTEX
import qualified Gargantext.Core.Text.Corpus.API.Pubmed as PUBMED import qualified Gargantext.Core.Text.Corpus.API.Pubmed as PUBMED
import qualified Gargantext.Core.Text.Corpus.Query as Corpus import qualified Gargantext.Core.Text.Corpus.Query as Corpus
import qualified PUBMED.Types as PUBMED
import Servant.Client (ClientError) import Servant.Client (ClientError)
data GetCorpusError data GetCorpusError
...@@ -45,19 +44,19 @@ data GetCorpusError ...@@ -45,19 +44,19 @@ data GetCorpusError
deriving (Show, Eq) deriving (Show, Eq)
-- | Get External API metadata main function -- | Get External API metadata main function
get :: GargConfig get :: ExternalAPIs
-> ExternalAPIs
-> Lang -> Lang
-> Corpus.RawQuery -> Corpus.RawQuery
-> Maybe PUBMED.APIKey
-> Maybe Corpus.Limit -> Maybe Corpus.Limit
-- -> IO [HyperdataDocument] -- -> IO [HyperdataDocument]
-> IO (Either GetCorpusError (Maybe Integer, ConduitT () HyperdataDocument IO ())) -> IO (Either GetCorpusError (Maybe Integer, ConduitT () HyperdataDocument IO ()))
get cfg externalAPI la q limit = do get externalAPI la q mPubmedAPIKey limit = do
case Corpus.parseQuery q of case Corpus.parseQuery q of
Left err -> pure $ Left $ InvalidInputQuery q (T.pack err) Left err -> pure $ Left $ InvalidInputQuery q (T.pack err)
Right corpusQuery -> case externalAPI of Right corpusQuery -> case externalAPI of
PubMed -> first ExternalAPIError <$> PubMed -> first ExternalAPIError <$>
PUBMED.get (cfg ^. gc_pubmed_api_key) corpusQuery limit PUBMED.get (fromMaybe "" mPubmedAPIKey) corpusQuery limit
--docs <- PUBMED.get q default_limit -- EN only by default --docs <- PUBMED.get q default_limit -- EN only by default
--pure (Just $ fromIntegral $ length docs, yieldMany docs) --pure (Just $ fromIntegral $ length docs, yieldMany docs)
Arxiv -> Right <$> Arxiv.get la corpusQuery limit Arxiv -> Right <$> Arxiv.get la corpusQuery limit
......
...@@ -112,6 +112,7 @@ import Gargantext.Prelude.Crypto.Hash (Hash) ...@@ -112,6 +112,7 @@ import Gargantext.Prelude.Crypto.Hash (Hash)
import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..)) import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..))
import qualified Gargantext.Core.Text.Corpus.API as API import qualified Gargantext.Core.Text.Corpus.API as API
import qualified Gargantext.Database.Query.Table.Node.Document.Add as Doc (add) import qualified Gargantext.Database.Query.Table.Node.Document.Add as Doc (add)
import qualified PUBMED.Types as PUBMED
--import qualified Prelude --import qualified Prelude
------------------------------------------------------------------------ ------------------------------------------------------------------------
...@@ -151,14 +152,13 @@ getDataText :: FlowCmdM env err m ...@@ -151,14 +152,13 @@ getDataText :: FlowCmdM env err m
=> DataOrigin => DataOrigin
-> TermType Lang -> TermType Lang
-> API.RawQuery -> API.RawQuery
-> Maybe PUBMED.APIKey
-> Maybe API.Limit -> Maybe API.Limit
-> m (Either API.GetCorpusError DataText) -> m (Either API.GetCorpusError DataText)
getDataText (ExternalOrigin api) la q li = do getDataText (ExternalOrigin api) la q mPubmedAPIKey li = do
cfg <- view $ hasConfig eRes <- liftBase $ API.get api (_tt_lang la) q mPubmedAPIKey li
eRes <- liftBase $ API.get cfg api (_tt_lang la) q li
pure $ DataNew <$> eRes pure $ DataNew <$> eRes
getDataText (InternalOrigin _) _la q _ _li = do
getDataText (InternalOrigin _) _la q _li = do
(_masterUserId, _masterRootId, cId) <- getOrMk_RootWithCorpus (_masterUserId, _masterRootId, cId) <- getOrMk_RootWithCorpus
(UserName userMaster) (UserName userMaster)
(Left "") (Left "")
...@@ -173,7 +173,7 @@ getDataText_Debug :: FlowCmdM env err m ...@@ -173,7 +173,7 @@ getDataText_Debug :: FlowCmdM env err m
-> Maybe API.Limit -> Maybe API.Limit
-> m () -> m ()
getDataText_Debug a l q li = do getDataText_Debug a l q li = do
result <- getDataText a l q li result <- getDataText a l q Nothing li
case result of case result of
Left err -> liftBase $ putStrLn $ show err Left err -> liftBase $ putStrLn $ show err
Right res -> liftBase $ printDataText res Right res -> liftBase $ printDataText res
......
...@@ -26,7 +26,7 @@ import Gargantext.Prelude ...@@ -26,7 +26,7 @@ import Gargantext.Prelude
------------------------------------------------------------------------ ------------------------------------------------------------------------
getUserLightWithId :: HasNodeError err => Int -> Cmd err UserLight getUserLightWithId :: HasNodeError err => Int -> Cmd err UserLight
getUserLightWithId i = do getUserLightWithId i = do
candidates <- head <$> getUsersWithId i candidates <- head <$> getUsersWithId (UserDBId i)
case candidates of case candidates of
Nothing -> nodeError NoUserFound Nothing -> nodeError NoUserFound
Just u -> pure u Just u -> pure u
...@@ -70,8 +70,8 @@ getUsername :: HasNodeError err ...@@ -70,8 +70,8 @@ getUsername :: HasNodeError err
=> User => User
-> Cmd err Username -> Cmd err Username
getUsername (UserName u) = pure u getUsername (UserName u) = pure u
getUsername (UserDBId i) = do getUsername user@(UserDBId _) = do
users <- getUsersWithId i users <- getUsersWithId user
case head users of case head users of
Just u -> pure $ userLight_username u Just u -> pure $ userLight_username u
Nothing -> nodeError $ NodeError "G.D.A.U.getUserName: User not found with that id" Nothing -> nodeError $ NodeError "G.D.A.U.getUserName: User not found with that id"
...@@ -82,4 +82,3 @@ getUsername UserPublic = pure "UserPublic" ...@@ -82,4 +82,3 @@ getUsername UserPublic = pure "UserPublic"
-------------------------------------------------------------------------- --------------------------------------------------------------------------
-- getRootId is in Gargantext.Database.Query.Tree.Root -- getRootId is in Gargantext.Database.Query.Tree.Root
...@@ -31,6 +31,7 @@ import Gargantext.Database.Admin.Types.Hyperdata.Prelude ...@@ -31,6 +31,7 @@ import Gargantext.Database.Admin.Types.Hyperdata.Prelude
import Gargantext.Database.Admin.Types.Hyperdata.Contact import Gargantext.Database.Admin.Types.Hyperdata.Contact
import Gargantext.Database.Admin.Types.Node (DocumentId) import Gargantext.Database.Admin.Types.Node (DocumentId)
import Gargantext.Prelude import Gargantext.Prelude
import qualified PUBMED.Types as PUBMED
-- import Gargantext.Database.Schema.Node -- (Node(..)) -- import Gargantext.Database.Schema.Node -- (Node(..))
...@@ -38,6 +39,7 @@ data HyperdataUser = ...@@ -38,6 +39,7 @@ data HyperdataUser =
HyperdataUser { _hu_private :: !(Maybe HyperdataPrivate) HyperdataUser { _hu_private :: !(Maybe HyperdataPrivate)
, _hu_shared :: !(Maybe HyperdataContact) , _hu_shared :: !(Maybe HyperdataContact)
, _hu_public :: !(Maybe HyperdataPublic) , _hu_public :: !(Maybe HyperdataPublic)
, _hu_pubmed_api_key :: !(Maybe PUBMED.APIKey)
} deriving (Eq, Show, Generic) } deriving (Eq, Show, Generic)
instance GQLType HyperdataUser where instance GQLType HyperdataUser where
...@@ -68,7 +70,8 @@ defaultHyperdataUser = ...@@ -68,7 +70,8 @@ defaultHyperdataUser =
HyperdataUser HyperdataUser
{ _hu_private = Just defaultHyperdataPrivate { _hu_private = Just defaultHyperdataPrivate
, _hu_shared = Just defaultHyperdataContact , _hu_shared = Just defaultHyperdataContact
, _hu_public = Just defaultHyperdataPublic } , _hu_public = Just defaultHyperdataPublic
, _hu_pubmed_api_key = Nothing }
defaultHyperdataPublic :: HyperdataPublic defaultHyperdataPublic :: HyperdataPublic
defaultHyperdataPublic = HyperdataPublic "pseudo" [1..10] defaultHyperdataPublic = HyperdataPublic "pseudo" [1..10]
...@@ -97,7 +100,7 @@ $(deriveJSON (unPrefix "_hpu_") ''HyperdataPublic) ...@@ -97,7 +100,7 @@ $(deriveJSON (unPrefix "_hpu_") ''HyperdataPublic)
-- | Arbitrary instances -- | Arbitrary instances
instance Arbitrary HyperdataUser where instance Arbitrary HyperdataUser where
arbitrary = HyperdataUser <$> arbitrary <*> arbitrary <*> arbitrary arbitrary = HyperdataUser <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance Arbitrary HyperdataPrivate where instance Arbitrary HyperdataPrivate where
arbitrary = pure defaultHyperdataPrivate arbitrary = pure defaultHyperdataPrivate
...@@ -143,4 +146,3 @@ instance DefaultFromField SqlJsonb HyperdataPrivate where ...@@ -143,4 +146,3 @@ instance DefaultFromField SqlJsonb HyperdataPrivate where
instance DefaultFromField SqlJsonb HyperdataPublic where instance DefaultFromField SqlJsonb HyperdataPublic where
defaultFromField = fromPGSFromField defaultFromField = fromPGSFromField
...@@ -29,7 +29,6 @@ import Data.Text (Text) ...@@ -29,7 +29,6 @@ import Data.Text (Text)
import Database.PostgreSQL.Simple.SqlQQ (sql) import Database.PostgreSQL.Simple.SqlQQ (sql)
import Opaleye hiding (FromField) import Opaleye hiding (FromField)
import Prelude hiding (null, id, map, sum) import Prelude hiding (null, id, map, sum)
import qualified PUBMED.Types as PUBMED
import Gargantext.Core import Gargantext.Core
import Gargantext.Core.Types import Gargantext.Core.Types
...@@ -203,7 +202,7 @@ getCorporaWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeCorpus) ...@@ -203,7 +202,7 @@ getCorporaWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeCorpus)
selectNodesWithParentID :: NodeId -> Select NodeRead selectNodesWithParentID :: NodeId -> Select NodeRead
selectNodesWithParentID n = proc () -> do selectNodesWithParentID n = proc () -> do
row@(Node _ _ _ _ parent_id _ _ _) <- queryNodeTable -< () row@(Node _ _ _ _ parent_id _ _ _) <- queryNodeTable -< ()
restrict -< parent_id .== (pgNodeId n) restrict -< parent_id .== pgNodeId n
returnA -< row returnA -< row
...@@ -217,7 +216,22 @@ getNodesWithType nt _ = runOpaQuery $ selectNodesWithType nt ...@@ -217,7 +216,22 @@ getNodesWithType nt _ = runOpaQuery $ selectNodesWithType nt
=> NodeType -> Select NodeRead => NodeType -> Select NodeRead
selectNodesWithType nt' = proc () -> do selectNodesWithType nt' = proc () -> do
row@(Node _ _ tn _ _ _ _ _) <- queryNodeTable -< () row@(Node _ _ tn _ _ _ _ _) <- queryNodeTable -< ()
restrict -< tn .== (sqlInt4 $ toDBid nt') restrict -< tn .== sqlInt4 (toDBid nt')
returnA -< row
getNodeWithType :: (HasNodeError err, JSONB a, HasDBid NodeType)
=> NodeId
-> NodeType
-> proxy a
-> Cmd err [Node a]
getNodeWithType nId nt _ = runOpaQuery $ selectNodeWithType nId nt
where
selectNodeWithType :: HasDBid NodeType
=> NodeId -> NodeType -> Select NodeRead
selectNodeWithType (NodeId nId') nt' = proc () -> do
row@(Node ti _ tn _ _ _ _ _) <- queryNodeTable -< ()
restrict -< ti .== sqlInt4 nId'
restrict -< tn .== sqlInt4 (toDBid nt')
returnA -< row returnA -< row
getNodesIdWithType :: (HasNodeError err, HasDBid NodeType) => NodeType -> Cmd err [NodeId] getNodesIdWithType :: (HasNodeError err, HasDBid NodeType) => NodeType -> Cmd err [NodeId]
...@@ -328,31 +342,6 @@ insertNodesWithParent pid ns = insertNodes (set node_parent_id (pgNodeId <$> pid ...@@ -328,31 +342,6 @@ insertNodesWithParent pid ns = insertNodes (set node_parent_id (pgNodeId <$> pid
insertNodesWithParentR :: Maybe ParentId -> [NodeWrite] -> Cmd err [NodeId] insertNodesWithParentR :: Maybe ParentId -> [NodeWrite] -> Cmd err [NodeId]
insertNodesWithParentR pid ns = insertNodesR (set node_parent_id (pgNodeId <$> pid) <$> ns) insertNodesWithParentR pid ns = insertNodesR (set node_parent_id (pgNodeId <$> pid) <$> ns)
getCorpusPubmedAPIKey :: NodeId -> Cmd err (Maybe PUBMED.APIKey)
getCorpusPubmedAPIKey cId = do
res <- runPGSQuery query params
pure $ (\(PGS.Only apiKey) -> apiKey) <$> head res
where
query :: PGS.Query
query = [sql|
SELECT hyperdata -> 'pubmed_api_key'
FROM nodes
WHERE id = ?
|]
params = PGS.Only cId
updateCorpusPubmedAPIKey :: NodeId -> PUBMED.APIKey -> Cmd err Int64
updateCorpusPubmedAPIKey cId apiKey =
execPGSQuery query params
where
query :: PGS.Query
query = [sql|
UPDATE nodes
SET hyperdata = hyperdata || ?
WHERE id = ?
|]
params = (encode $ object [ "pubmed_api_key" .= apiKey ], cId)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- TODO -- TODO
-- currently this function removes the child relation -- currently this function removes the child relation
......
...@@ -27,7 +27,7 @@ import Gargantext.Database.Schema.Node ...@@ -27,7 +27,7 @@ import Gargantext.Database.Schema.Node
import Gargantext.Database.Schema.User import Gargantext.Database.Schema.User
import Gargantext.Database.Query.Table.User import Gargantext.Database.Query.Table.User
selectNodesWithUsername :: NodeType -> Username -> Cmd err [NodeId] selectNodesWithUsername :: (HasDBid NodeType) => NodeType -> Username -> Cmd err [NodeId]
selectNodesWithUsername nt u = runOpaQuery $ proc () -> do selectNodesWithUsername nt u = runOpaQuery $ proc () -> do
n <- queryNodeTable -< () n <- queryNodeTable -< ()
usrs <- optionalRestrict queryUserTable -< usrs <- optionalRestrict queryUserTable -<
......
...@@ -39,10 +39,10 @@ updateHyperdataQuery i h = seq h' $ trace "updateHyperdataQuery: encoded JSON" $ ...@@ -39,10 +39,10 @@ updateHyperdataQuery i h = seq h' $ trace "updateHyperdataQuery: encoded JSON" $
-> Node { _node_hyperdata = h', .. } -> Node { _node_hyperdata = h', .. }
-- -> trace "updating mate" $ Node _ni _nh _nt _nu _np _nn _nd h' -- -> trace "updating mate" $ Node _ni _nh _nt _nu _np _nn _nd h'
) )
, uWhere = (\row -> {-trace "uWhere" $-} _node_id row .== pgNodeId i ) , uWhere = \row -> {-trace "uWhere" $-} _node_id row .== pgNodeId i
, uReturning = rCount , uReturning = rCount
} }
where h' = (sqlJSONB $ cs $ encode $ h) where h' = sqlJSONB $ cs $ encode h
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
updateNodesWithType :: ( HasNodeError err updateNodesWithType :: ( HasNodeError err
...@@ -54,6 +54,19 @@ updateNodesWithType nt p f = do ...@@ -54,6 +54,19 @@ updateNodesWithType nt p f = do
ns <- getNodesWithType nt p ns <- getNodesWithType nt p
mapM (\n -> updateHyperdata (_node_id n) (f $ _node_hyperdata n)) ns mapM (\n -> updateHyperdata (_node_id n) (f $ _node_hyperdata n)) ns
updateNodeWithType :: ( HasNodeError err
, JSONB a
, ToJSON a
, HasDBid NodeType
) => NodeId
-> NodeType
-> proxy a
-> (a -> a)
-> Cmd err [Int64]
updateNodeWithType nId nt p f = do
ns <- getNodeWithType nId nt p
mapM (\n -> updateHyperdata (_node_id n) (f $ _node_hyperdata n)) ns
-- | In case the Hyperdata Types are not compatible -- | In case the Hyperdata Types are not compatible
updateNodesWithType_ :: ( HasNodeError err updateNodesWithType_ :: ( HasNodeError err
......
...@@ -13,9 +13,10 @@ Functions to deal with users, database side. ...@@ -13,9 +13,10 @@ Functions to deal with users, database side.
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE Arrows #-} {-# LANGUAGE Arrows #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
module Gargantext.Database.Query.Table.User module Gargantext.Database.Query.Table.User
( insertUsers ( insertUsers
...@@ -29,6 +30,8 @@ module Gargantext.Database.Query.Table.User ...@@ -29,6 +30,8 @@ module Gargantext.Database.Query.Table.User
, updateUserEmail , updateUserEmail
, updateUserPassword , updateUserPassword
, updateUserForgotPasswordUUID , updateUserForgotPasswordUUID
, getUserPubmedAPIKey
, updateUserPubmedAPIKey
, getUser , getUser
, insertNewUsers , insertNewUsers
, selectUsersLightWith , selectUsersLightWith
...@@ -44,22 +47,27 @@ module Gargantext.Database.Query.Table.User ...@@ -44,22 +47,27 @@ module Gargantext.Database.Query.Table.User
where where
import Control.Arrow (returnA) import Control.Arrow (returnA)
import Control.Lens ((^.)) import Control.Lens ((^.), (?~))
import Data.Maybe (fromMaybe)
import Data.List (find) import Data.List (find)
import Data.Maybe (fromMaybe)
import Data.Proxy
import Data.Text (Text) import Data.Text (Text)
import Data.Time (UTCTime) import Data.Time (UTCTime)
import qualified Data.UUID as UUID import qualified Data.UUID as UUID
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import qualified Gargantext.Prelude.Crypto.Auth as Auth import qualified Gargantext.Prelude.Crypto.Auth as Auth
import Gargantext.Database.Admin.Config (nodeTypeId) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser(..), hu_pubmed_api_key)
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser) import Gargantext.Database.Admin.Types.Node (NodeType(NodeUser), Node, NodeId(..), pgNodeId)
import Gargantext.Database.Admin.Types.Node (NodeType(NodeUser), Node)
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
import Gargantext.Database.Schema.Node (NodeRead, node_hyperdata, queryNodeTable, node_user_id, node_typename) import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateNodeWithType)
import Gargantext.Database.Schema.Node (NodeRead, node_hyperdata, queryNodeTable, node_id, node_user_id, node_typename)
import Gargantext.Database.Schema.User import Gargantext.Database.Schema.User
import Gargantext.Prelude import Gargantext.Prelude
import Opaleye import Opaleye
import qualified PUBMED.Types as PUBMED
import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
import Gargantext.Core (HasDBid)
import Gargantext.Database.Admin.Config (nodeTypeId)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- TODO: on conflict, nice message -- TODO: on conflict, nice message
...@@ -86,7 +94,7 @@ updateUserDB us = mkCmd $ \c -> runUpdate_ c (updateUserQuery us) ...@@ -86,7 +94,7 @@ updateUserDB us = mkCmd $ \c -> runUpdate_ c (updateUserQuery us)
, user_email = em' , user_email = em'
, .. } , .. }
) )
, uWhere = (\row -> user_username row .== un') , uWhere = \row -> user_username row .== un'
, uReturning = rCount , uReturning = rCount
} }
where where
...@@ -139,52 +147,82 @@ selectUsersLightWithForgotPasswordUUID uuid = proc () -> do ...@@ -139,52 +147,82 @@ selectUsersLightWithForgotPasswordUUID uuid = proc () -> do
returnA -< row returnA -< row
---------------------------------------------------------- ----------------------------------------------------------
getUsersWithId :: Int -> Cmd err [UserLight] getUsersWithId :: User -> Cmd err [UserLight]
getUsersWithId i = map toUserLight <$> runOpaQuery (selectUsersLightWithId i) getUsersWithId (UserDBId i) = map toUserLight <$> runOpaQuery (selectUsersLightWithId i)
where where
selectUsersLightWithId :: Int -> Select UserRead selectUsersLightWithId :: Int -> Select UserRead
selectUsersLightWithId i' = proc () -> do selectUsersLightWithId i' = proc () -> do
row <- queryUserTable -< () row <- queryUserTable -< ()
restrict -< user_id row .== sqlInt4 i' restrict -< user_id row .== sqlInt4 i'
returnA -< row returnA -< row
getUsersWithId (RootId i) = map toUserLight <$> runOpaQuery (selectUsersLightWithId i)
where
selectUsersLightWithId :: NodeId -> Select UserRead
selectUsersLightWithId i' = proc () -> do
n <- queryNodeTable -< ()
restrict -< n^.node_id .== pgNodeId i'
restrict -< n^.node_typename .== sqlInt4 (nodeTypeId NodeUser)
row <- queryUserTable -< ()
restrict -< user_id row .== n^.node_user_id
returnA -< row
getUsersWithId _ = undefined
queryUserTable :: Select UserRead queryUserTable :: Select UserRead
queryUserTable = selectTable userTable queryUserTable = selectTable userTable
---------------------------------------------------------------------- ----------------------------------------------------------------------
getUserHyperdata :: Int -> Cmd err [HyperdataUser] -- | Get hyperdata associated with user node.
getUserHyperdata i = do getUserHyperdata :: User -> Cmd err [HyperdataUser]
runOpaQuery (selectUserHyperdataWithId i) getUserHyperdata (RootId uId) = do
runOpaQuery (selectUserHyperdataWithId uId)
where
selectUserHyperdataWithId :: NodeId -> Select (Field SqlJsonb)
selectUserHyperdataWithId i' = proc () -> do
row <- queryNodeTable -< ()
restrict -< row^.node_id .== pgNodeId i'
returnA -< row^.node_hyperdata
getUserHyperdata (UserDBId uId) = do
runOpaQuery (selectUserHyperdataWithId uId)
where where
selectUserHyperdataWithId :: Int -> Select (Column SqlJsonb) selectUserHyperdataWithId :: Int -> Select (Field SqlJsonb)
selectUserHyperdataWithId i' = proc () -> do selectUserHyperdataWithId i' = proc () -> do
row <- queryNodeTable -< () row <- queryNodeTable -< ()
restrict -< row^.node_user_id .== (sqlInt4 i') restrict -< row^.node_user_id .== sqlInt4 i'
restrict -< row^.node_typename .== (sqlInt4 $ nodeTypeId NodeUser) restrict -< row^.node_typename .== sqlInt4 (nodeTypeId NodeUser)
returnA -< row^.node_hyperdata returnA -< row^.node_hyperdata
getUserHyperdata _ = undefined
getUserNodeHyperdata :: Int -> Cmd err [Node HyperdataUser] -- | Same as `getUserHyperdata` but returns a `Node` type.
getUserNodeHyperdata i = do getUserNodeHyperdata :: User -> Cmd err [Node HyperdataUser]
runOpaQuery (selectUserHyperdataWithId i) getUserNodeHyperdata (RootId uId) = do
runOpaQuery (selectUserHyperdataWithId uId)
where
selectUserHyperdataWithId :: NodeId -> Select NodeRead
selectUserHyperdataWithId i' = proc () -> do
row <- queryNodeTable -< ()
restrict -< row^.node_id .== pgNodeId i'
returnA -< row
getUserNodeHyperdata (UserDBId uId) = do
runOpaQuery (selectUserHyperdataWithId uId)
where where
selectUserHyperdataWithId :: Int -> Select NodeRead selectUserHyperdataWithId :: Int -> Select NodeRead
selectUserHyperdataWithId i' = proc () -> do selectUserHyperdataWithId i' = proc () -> do
row <- queryNodeTable -< () row <- queryNodeTable -< ()
restrict -< row^.node_user_id .== (sqlInt4 i') restrict -< row^.node_user_id .== sqlInt4 i'
restrict -< row^.node_typename .== (sqlInt4 $ nodeTypeId NodeUser) restrict -< row^.node_typename .== sqlInt4 (nodeTypeId NodeUser)
returnA -< row returnA -< row
getUserNodeHyperdata _ = undefined
getUsersWithHyperdata :: User -> Cmd err [(UserLight, HyperdataUser)]
getUsersWithHyperdata :: Int -> Cmd err [(UserLight, HyperdataUser)]
getUsersWithHyperdata i = do getUsersWithHyperdata i = do
u <- getUsersWithId i u <- getUsersWithId i
h <- getUserHyperdata i h <- getUserHyperdata i
-- printDebug "[getUsersWithHyperdata]" (u,h) -- printDebug "[getUsersWithHyperdata]" (u,h)
pure $ zip u h pure $ zip u h
getUsersWithNodeHyperdata :: Int -> Cmd err [(UserLight, Node HyperdataUser)] getUsersWithNodeHyperdata :: User -> Cmd err [(UserLight, Node HyperdataUser)]
getUsersWithNodeHyperdata i = do getUsersWithNodeHyperdata i = do
u <- getUsersWithId i u <- getUsersWithId i
h <- getUserNodeHyperdata i h <- getUserNodeHyperdata i
...@@ -208,8 +246,8 @@ updateUserPassword (UserLight { userLight_password = GargPassword password, .. } ...@@ -208,8 +246,8 @@ updateUserPassword (UserLight { userLight_password = GargPassword password, .. }
updateUserQuery :: Update Int64 updateUserQuery :: Update Int64
updateUserQuery = Update updateUserQuery = Update
{ uTable = userTable { uTable = userTable
, uUpdateWith = updateEasy (\ (UserDB { .. }) -> UserDB { user_password = sqlStrictText password, .. } ) , uUpdateWith = updateEasy (\(UserDB { .. }) -> UserDB { user_password = sqlStrictText password, .. } )
, uWhere = (\row -> user_id row .== (sqlInt4 userLight_id)) , uWhere = \row -> user_id row .== sqlInt4 userLight_id
, uReturning = rCount } , uReturning = rCount }
updateUserForgotPasswordUUID :: UserLight -> Cmd err Int64 updateUserForgotPasswordUUID :: UserLight -> Cmd err Int64
...@@ -219,9 +257,23 @@ updateUserForgotPasswordUUID (UserLight { .. }) = mkCmd $ \c -> runUpdate_ c upd ...@@ -219,9 +257,23 @@ updateUserForgotPasswordUUID (UserLight { .. }) = mkCmd $ \c -> runUpdate_ c upd
updateUserQuery :: Update Int64 updateUserQuery :: Update Int64
updateUserQuery = Update updateUserQuery = Update
{ uTable = userTable { uTable = userTable
, uUpdateWith = updateEasy (\ (UserDB { .. }) -> UserDB { user_forgot_password_uuid = pass, .. }) , uUpdateWith = updateEasy (\(UserDB { .. }) -> UserDB { user_forgot_password_uuid = pass, .. })
, uWhere = (\row -> user_id row .== (sqlInt4 userLight_id)) , uWhere = \row -> user_id row .== sqlInt4 userLight_id
, uReturning = rCount } , uReturning = rCount }
getUserPubmedAPIKey :: User -> Cmd err (Maybe PUBMED.APIKey)
getUserPubmedAPIKey user = do
hs <- getUserHyperdata user
case hs of
[] -> pure Nothing
(x:_) -> pure $ _hu_pubmed_api_key x
updateUserPubmedAPIKey :: (HasDBid NodeType, HasNodeError err)
=> User -> PUBMED.APIKey -> Cmd err Int64
updateUserPubmedAPIKey (RootId uId) apiKey = do
_ <- updateNodeWithType uId NodeUser (Proxy :: Proxy HyperdataUser) (\h -> h & hu_pubmed_api_key ?~ apiKey)
pure 1
updateUserPubmedAPIKey _ _ = undefined
------------------------------------------------------------------ ------------------------------------------------------------------
-- | Select User with some parameters -- | Select User with some parameters
-- Not optimized version -- Not optimized version
......
...@@ -72,23 +72,27 @@ nodeTable = Table "nodes" (pNode Node { _node_id = optionalTableField "i ...@@ -72,23 +72,27 @@ nodeTable = Table "nodes" (pNode Node { _node_id = optionalTableField "i
queryNodeTable :: Query NodeRead queryNodeTable :: Query NodeRead
queryNodeTable = selectTable nodeTable queryNodeTable = selectTable nodeTable
------------------------------------------------------------------------ ------------------------------------------------------------------------
type NodeWrite = NodePoly (Maybe (Field SqlInt4) ) type NodeHWrite a = NodePoly (Maybe (Field SqlInt4) )
(Maybe (Field SqlText) ) (Maybe (Field SqlText) )
(Field SqlInt4) (Field SqlInt4)
(Field SqlInt4) (Field SqlInt4)
(Maybe (Field SqlInt4) ) (Maybe (Field SqlInt4) )
(Field SqlText) (Field SqlText)
(Maybe (Field SqlTimestamptz)) (Maybe (Field SqlTimestamptz))
(Field SqlJsonb) (Field a)
type NodeRead = NodePoly (Field SqlInt4 ) type NodeHRead a = NodePoly (Field SqlInt4 )
(Field SqlText ) (Field SqlText )
(Field SqlInt4 ) (Field SqlInt4 )
(Field SqlInt4 ) (Field SqlInt4 )
(Field SqlInt4 ) (Field SqlInt4 )
(Field SqlText ) (Field SqlText )
(Field SqlTimestamptz ) (Field SqlTimestamptz )
(Field SqlJsonb ) (Field a )
------------------------------------------------------------------------
type NodeWrite = NodeHWrite SqlJsonb
type NodeRead = NodeHRead SqlJsonb
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Node(Read|Write)Search is slower than Node(Write|Read) use it -- | Node(Read|Write)Search is slower than Node(Write|Read) use it
-- for full text search only -- for full text search only
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment