{-# LANGUAGE OverloadedStrings #-}

module Database.EJDB2
    ( init
    , Database
    , KV.readonlyOpenFlags
    , KV.truncateOpenFlags
    , KV.noTrimOnCloseOpenFlags
    , minimalOptions
    , open
    , close
    , getById
    , getCount
    , getList
    , getList'
    , putNew
    , put
    , mergeOrPut
    , patch
    , delete
    , ensureCollection
    , removeCollection
    , renameCollection
    , getMeta
    , IndexMode.IndexMode
    , IndexMode.uniqueIndexMode
    , IndexMode.strIndexMode
    , IndexMode.f64IndexMode
    , IndexMode.i64IndexMode
    , ensureIndex
    , removeIndex
    , onlineBackup
    , fold
    , Query(..)
    , BindM
    , noBind
    , setBool
    , setBoolAtIndex
    , setI64
    , setI64AtIndex
    , setF64
    , setF64AtIndex
    , setString
    , setStringAtIndex
    , setRegex
    , setRegexAtIndex
    , setNull
    , setNullAtIndex
    ) where

import           Control.Exception
import           Control.Monad

import qualified Data.Aeson                             as Aeson
import qualified Data.ByteString                        as BS
import qualified Data.HashMap.Strict                    as Map
import           Data.IORef
import           Data.Int
import           Data.Word

import           Database.EJDB2.Bindings.EJDB2
import           Database.EJDB2.Bindings.JBL
import           Database.EJDB2.Bindings.Types.EJDB
import           Database.EJDB2.Bindings.Types.EJDBDoc  as EJDBDoc
import           Database.EJDB2.Bindings.Types.EJDBExec as EJDBExec
import qualified Database.EJDB2.IndexMode               as IndexMode
import           Database.EJDB2.JBL
import qualified Database.EJDB2.KV                      as KV
import           Database.EJDB2.Options                 as Options
import           Database.EJDB2.Query
import           Database.EJDB2.Result

import           Foreign
import           Foreign.C.String
import           Foreign.C.Types
import           Foreign.Marshal.Alloc
import           Foreign.Ptr
import           Foreign.Storable

import           Prelude                                hiding ( init )

-- | Reference to database. You can create it by 'open'.
data Database = Database (Ptr EJDB) EJDB

-- | Create minimal 'Options' for opening a database: just path to file and opening mode.
minimalOptions :: String -- ^ Database file path 
               -> [KV.OpenFlags] -- ^ Open mode
               -> Options -- ^ Options to use in 'open'

minimalOptions :: String -> [OpenFlags] -> Options
minimalOptions path :: String
path openFlags :: [OpenFlags]
openFlags =
    Options
Options.zero { kv :: Options
kv = Options
KV.zero { path :: Maybe String
KV.path = String -> Maybe String
forall a. a -> Maybe a
Just String
path, oflags :: [OpenFlags]
KV.oflags = [OpenFlags]
openFlags }
                 }

{-|
  ejdb2 initialization routine.

  /Must be called before using any of ejdb API function./
-}
init :: IO ()
init :: IO ()
init = IO RC
c_ejdb_init IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC

{-|
  Open storage file.

  Storage can be opened only by one single process at time.

  /Remember to release database by 'close' when database is no longer required./
-}
open :: Options -> IO Database
open :: Options -> IO Database
open opts :: Options
opts = do
    Ptr EJDB
ejdbPtr <- IO (Ptr EJDB)
forall a. Storable a => IO (Ptr a)
malloc
    OptionsB
optsB <- Options -> IO OptionsB
build Options
opts
    OptionsB -> (Ptr OptionsB -> IO Database) -> IO Database
forall a b. Storable a => a -> (Ptr a -> IO b) -> IO b
with OptionsB
optsB ((Ptr OptionsB -> IO Database) -> IO Database)
-> (Ptr OptionsB -> IO Database) -> IO Database
forall a b. (a -> b) -> a -> b
$ \optsPtr :: Ptr OptionsB
optsPtr -> do
        Result
result <- RC -> Result
decodeRC (RC -> Result) -> IO RC -> IO Result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr OptionsB -> Ptr EJDB -> IO RC
c_ejdb_open Ptr OptionsB
optsPtr Ptr EJDB
ejdbPtr
        if Result
result Result -> Result -> Bool
forall a. Eq a => a -> a -> Bool
== Result
Ok
            then Ptr EJDB -> EJDB -> Database
Database Ptr EJDB
ejdbPtr (EJDB -> Database) -> IO EJDB -> IO Database
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr EJDB -> IO EJDB
forall a. Storable a => Ptr a -> IO a
peek Ptr EJDB
ejdbPtr
            else Ptr EJDB -> IO ()
forall a. Ptr a -> IO ()
free Ptr EJDB
ejdbPtr IO () -> IO Database -> IO Database
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> IO Database
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (Result -> String
forall a. Show a => a -> String
show Result
result)

{-|
  Closes storage and frees up all resources.
-}
close :: Database -> IO ()
close :: Database -> IO ()
close (Database ejdbPtr :: Ptr EJDB
ejdbPtr _) = do
    Result
result <- RC -> Result
decodeRC (RC -> Result) -> IO RC -> IO Result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr EJDB -> IO RC
c_ejdb_close Ptr EJDB
ejdbPtr
    if Result
result Result -> Result -> Bool
forall a. Eq a => a -> a -> Bool
== Result
Ok then Ptr EJDB -> IO ()
forall a. Ptr a -> IO ()
free Ptr EJDB
ejdbPtr else String -> IO ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ Result -> String
forall a. Show a => a -> String
show Result
result

-- | Retrieve document identified by given id from collection.
getById :: Aeson.FromJSON a
        => Database
        -> String -- ^ Collection name
        -> Int64 -- ^ Document identifier. Not zero
        -> IO (Maybe a)
getById :: Database -> String -> Int64 -> IO (Maybe a)
getById (Database _ ejdb :: EJDB
ejdb) collection :: String
collection id :: Int64
id = (Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a))
-> (Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ \jblPtr :: Ptr EJDB
jblPtr ->
    IO (Maybe a) -> IO () -> IO (Maybe a)
forall a b. IO a -> IO b -> IO a
finally (do
                 RC
rc <- String -> (CString -> IO RC) -> IO RC
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO RC) -> IO RC) -> (CString -> IO RC) -> IO RC
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection ->
                     EJDB -> CString -> CIntMax -> Ptr EJDB -> IO RC
c_ejdb_get EJDB
ejdb CString
cCollection (Int64 -> CIntMax
CIntMax Int64
id) Ptr EJDB
jblPtr
                 let result :: Result
result = RC -> Result
decodeRC RC
rc
                 case Result
result of
                     Ok -> Ptr EJDB -> IO EJDB
forall a. Storable a => Ptr a -> IO a
peek Ptr EJDB
jblPtr IO EJDB -> (EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= EJDB -> IO (Maybe a)
forall a. FromJSON a => EJDB -> IO (Maybe a)
decode
                     ErrorNotFound -> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
                     _ -> String -> IO (Maybe a)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO (Maybe a)) -> String -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ Result -> String
forall a. Show a => a -> String
show Result
result)
            (Ptr EJDB -> IO ()
c_jbl_destroy Ptr EJDB
jblPtr)

-- | Executes a given query and returns the number of documents.
getCount :: Database -> Query q -> IO Int64
getCount :: Database -> Query q -> IO Int64
getCount (Database _ ejdb :: EJDB
ejdb) query :: Query q
query = Query q -> (EJDB -> IO Int64) -> IO Int64
forall a b. Query a -> (EJDB -> IO b) -> IO b
withQuery Query q
query ((EJDB -> IO Int64) -> IO Int64) -> (EJDB -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$ \jql :: EJDB
jql -> (Ptr CIntMax -> IO Int64) -> IO Int64
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CIntMax -> IO Int64) -> IO Int64)
-> (Ptr CIntMax -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$
    \countPtr :: Ptr CIntMax
countPtr -> EJDB -> EJDB -> Ptr CIntMax -> CIntMax -> IO RC
c_ejdb_count EJDB
ejdb EJDB
jql Ptr CIntMax
countPtr 0 IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC IO () -> IO CIntMax -> IO CIntMax
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr CIntMax -> IO CIntMax
forall a. Storable a => Ptr a -> IO a
peek Ptr CIntMax
countPtr
    IO CIntMax -> (CIntMax -> IO Int64) -> IO Int64
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \(CIntMax int :: Int64
int) -> Int64 -> IO Int64
forall (m :: * -> *) a. Monad m => a -> m a
return Int64
int

exec :: EJDBExecVisitor -> Database -> Query q -> IO ()
exec :: EJDBExecVisitor -> Database -> Query q -> IO ()
exec visitor :: EJDBExecVisitor
visitor (Database _ ejdb :: EJDB
ejdb) query :: Query q
query = Query q -> (EJDB -> IO ()) -> IO ()
forall a b. Query a -> (EJDB -> IO b) -> IO b
withQuery Query q
query ((EJDB -> IO ()) -> IO ()) -> (EJDB -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \jql :: EJDB
jql -> do
    EJDB_EXEC_VISITOR
cVisitor <- EJDBExecVisitor -> IO EJDB_EXEC_VISITOR
mkEJDBExecVisitor EJDBExecVisitor
visitor
    let exec :: EJDBExec
exec =
            EJDBExec
EJDBExec.zero { db :: EJDB
db = EJDB
ejdb, q :: EJDB
q = EJDB
jql, visitor :: EJDB_EXEC_VISITOR
EJDBExec.visitor = EJDB_EXEC_VISITOR
cVisitor }
    IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
finally (EJDBExec -> (Ptr EJDBExec -> IO RC) -> IO RC
forall a b. Storable a => a -> (Ptr a -> IO b) -> IO b
with EJDBExec
exec Ptr EJDBExec -> IO RC
c_ejdb_exec IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC) (EJDB_EXEC_VISITOR -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr EJDB_EXEC_VISITOR
cVisitor)

-- | Iterate over query result building the result
fold :: Aeson.FromJSON b
     => Database
     -> (a
         -> (Int64, Maybe b)
         -> a) -- ^ The second argument is a tuple with the object id and the object
     -> a -- ^ Initial result
     -> Query q
     -> IO a
fold :: Database -> (a -> (Int64, Maybe b) -> a) -> a -> Query q -> IO a
fold database :: Database
database f :: a -> (Int64, Maybe b) -> a
f i :: a
i query :: Query q
query = (a -> (Int64, Maybe b) -> a, a)
-> IO (IORef (a -> (Int64, Maybe b) -> a, a))
forall a. a -> IO (IORef a)
newIORef (a -> (Int64, Maybe b) -> a
f, a
i) IO (IORef (a -> (Int64, Maybe b) -> a, a))
-> (IORef (a -> (Int64, Maybe b) -> a, a) -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ref :: IORef (a -> (Int64, Maybe b) -> a, a)
ref ->
    EJDBExecVisitor -> Database -> Query q -> IO ()
forall q. EJDBExecVisitor -> Database -> Query q -> IO ()
exec (IORef (a -> (Int64, Maybe b) -> a, a) -> EJDBExecVisitor
forall b a.
FromJSON b =>
IORef (a -> (Int64, Maybe b) -> a, a) -> EJDBExecVisitor
foldVisitor IORef (a -> (Int64, Maybe b) -> a, a)
ref) Database
database Query q
query IO () -> IO a -> IO a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (a -> (Int64, Maybe b) -> a, a) -> a
forall a b. (a, b) -> b
snd ((a -> (Int64, Maybe b) -> a, a) -> a)
-> IO (a -> (Int64, Maybe b) -> a, a) -> IO a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (a -> (Int64, Maybe b) -> a, a)
-> IO (a -> (Int64, Maybe b) -> a, a)
forall a. IORef a -> IO a
readIORef IORef (a -> (Int64, Maybe b) -> a, a)
ref

foldVisitor :: Aeson.FromJSON b
            => IORef ((a -> (Int64, Maybe b) -> a), a)
            -> EJDBExecVisitor
foldVisitor :: IORef (a -> (Int64, Maybe b) -> a, a) -> EJDBExecVisitor
foldVisitor ref :: IORef (a -> (Int64, Maybe b) -> a, a)
ref _ docPtr :: Ptr EJDBDoc
docPtr _ = do
    EJDBDoc
doc <- Ptr EJDBDoc -> IO EJDBDoc
forall a. Storable a => Ptr a -> IO a
peek Ptr EJDBDoc
docPtr
    Maybe b
value <- EJDB -> IO (Maybe b)
forall a. FromJSON a => EJDB -> IO (Maybe a)
decode (EJDBDoc -> EJDB
raw EJDBDoc
doc)
    let id :: Int64
id = CIntMax -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CIntMax -> Int64) -> CIntMax -> Int64
forall a b. (a -> b) -> a -> b
$ EJDBDoc -> CIntMax
EJDBDoc.id EJDBDoc
doc
    IORef (a -> (Int64, Maybe b) -> a, a)
-> ((a -> (Int64, Maybe b) -> a, a)
    -> (a -> (Int64, Maybe b) -> a, a))
-> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' IORef (a -> (Int64, Maybe b) -> a, a)
ref (((a -> (Int64, Maybe b) -> a, a)
  -> (a -> (Int64, Maybe b) -> a, a))
 -> IO ())
-> ((a -> (Int64, Maybe b) -> a, a)
    -> (a -> (Int64, Maybe b) -> a, a))
-> IO ()
forall a b. (a -> b) -> a -> b
$ \(f :: a -> (Int64, Maybe b) -> a
f, partial :: a
partial) -> (a -> (Int64, Maybe b) -> a
f, a -> (Int64, Maybe b) -> a
f a
partial (Int64
id, Maybe b
value))
    RC -> IO RC
forall (m :: * -> *) a. Monad m => a -> m a
return 0

{-|
  Executes a given query and builds a query result as list of tuple with id and document.
-}
getList :: Aeson.FromJSON a => Database -> Query q -> IO [(Int64, Maybe a)]
getList :: Database -> Query q -> IO [(Int64, Maybe a)]
getList database :: Database
database query :: Query q
query = [(Int64, Maybe a)] -> [(Int64, Maybe a)]
forall a. [a] -> [a]
reverse ([(Int64, Maybe a)] -> [(Int64, Maybe a)])
-> IO [(Int64, Maybe a)] -> IO [(Int64, Maybe a)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Database
-> ([(Int64, Maybe a)] -> (Int64, Maybe a) -> [(Int64, Maybe a)])
-> [(Int64, Maybe a)]
-> Query q
-> IO [(Int64, Maybe a)]
forall b a q.
FromJSON b =>
Database -> (a -> (Int64, Maybe b) -> a) -> a -> Query q -> IO a
fold Database
database [(Int64, Maybe a)] -> (Int64, Maybe a) -> [(Int64, Maybe a)]
forall a.
FromJSON a =>
[(Int64, Maybe a)] -> (Int64, Maybe a) -> [(Int64, Maybe a)]
foldList [] Query q
query

foldList :: Aeson.FromJSON a
         => [(Int64, Maybe a)]
         -> (Int64, Maybe a)
         -> [(Int64, Maybe a)]
foldList :: [(Int64, Maybe a)] -> (Int64, Maybe a) -> [(Int64, Maybe a)]
foldList = ((Int64, Maybe a) -> [(Int64, Maybe a)] -> [(Int64, Maybe a)])
-> [(Int64, Maybe a)] -> (Int64, Maybe a) -> [(Int64, Maybe a)]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (:)

{-|
  Executes a given query and builds a query result as list of documents with id injected as attribute.
-}
getList' :: Aeson.FromJSON a => Database -> Query q -> IO [Maybe a]
getList' :: Database -> Query q -> IO [Maybe a]
getList' database :: Database
database query :: Query q
query = [Maybe a] -> [Maybe a]
forall a. [a] -> [a]
reverse ([Maybe a] -> [Maybe a]) -> IO [Maybe a] -> IO [Maybe a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Database
-> ([Maybe a] -> (Int64, Maybe Value) -> [Maybe a])
-> [Maybe a]
-> Query q
-> IO [Maybe a]
forall b a q.
FromJSON b =>
Database -> (a -> (Int64, Maybe b) -> a) -> a -> Query q -> IO a
fold Database
database [Maybe a] -> (Int64, Maybe Value) -> [Maybe a]
forall a.
FromJSON a =>
[Maybe a] -> (Int64, Maybe Value) -> [Maybe a]
foldList' [] Query q
query

foldList'
    :: Aeson.FromJSON a => [Maybe a] -> (Int64, Maybe Aeson.Value) -> [Maybe a]
foldList' :: [Maybe a] -> (Int64, Maybe Value) -> [Maybe a]
foldList' list :: [Maybe a]
list (id :: Int64
id, value :: Maybe Value
value) = Maybe Value -> Maybe a
forall a. FromJSON a => Maybe Value -> Maybe a
parse (Int64 -> Maybe Value -> Maybe Value
setId Int64
id Maybe Value
value) Maybe a -> [Maybe a] -> [Maybe a]
forall a. a -> [a] -> [a]
: [Maybe a]
list

parse :: Aeson.FromJSON a => Maybe Aeson.Value -> Maybe a
parse :: Maybe Value -> Maybe a
parse Nothing = Maybe a
forall a. Maybe a
Nothing
parse (Just value :: Value
value) = case Value -> Result a
forall a. FromJSON a => Value -> Result a
Aeson.fromJSON Value
value of
    Aeson.Success v :: a
v -> a -> Maybe a
forall a. a -> Maybe a
Just a
v
    Aeson.Error _ -> Maybe a
forall a. Maybe a
Nothing

setId :: Int64 -> Maybe Aeson.Value -> Maybe Aeson.Value
setId :: Int64 -> Maybe Value -> Maybe Value
setId id :: Int64
id (Just (Aeson.Object map :: Object
map)) =
    Value -> Maybe Value
forall a. a -> Maybe a
Just (Object -> Value
Aeson.Object (Text -> Value -> Object -> Object
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
Map.insert "id" (Scientific -> Value
Aeson.Number (Scientific -> Value) -> Scientific -> Value
forall a b. (a -> b) -> a -> b
$ Int64 -> Scientific
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
id) Object
map))
setId _ Nothing = Maybe Value
forall a. Maybe a
Nothing
setId _ value :: Maybe Value
value = Maybe Value
value

{-|
  Save new document into collection under new generated identifier.
-}
putNew :: Aeson.ToJSON a
       => Database
       -> String -- ^ Collection name
       -> a -- ^ Document
       -> IO Int64 -- ^ New document identifier. Not zero

putNew :: Database -> String -> a -> IO Int64
putNew (Database _ ejdb :: EJDB
ejdb) collection :: String
collection obj :: a
obj = a -> (EJDB -> IO Int64) -> IO Int64
forall a b. ToJSON a => a -> (EJDB -> IO b) -> IO b
encode a
obj ((EJDB -> IO Int64) -> IO Int64) -> (EJDB -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$
    \doc :: EJDB
doc -> String -> (CString -> IO Int64) -> IO Int64
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO Int64) -> IO Int64)
-> (CString -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection -> (Ptr CIntMax -> IO Int64) -> IO Int64
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CIntMax -> IO Int64) -> IO Int64)
-> (Ptr CIntMax -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$ \idPtr :: Ptr CIntMax
idPtr ->
    EJDB -> CString -> EJDB -> Ptr CIntMax -> IO RC
c_ejdb_put_new EJDB
ejdb CString
cCollection EJDB
doc Ptr CIntMax
idPtr IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC IO () -> IO CIntMax -> IO CIntMax
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr CIntMax -> IO CIntMax
forall a. Storable a => Ptr a -> IO a
peek Ptr CIntMax
idPtr
    IO CIntMax -> (CIntMax -> IO Int64) -> IO Int64
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \(CIntMax int :: Int64
int) -> Int64 -> IO Int64
forall (m :: * -> *) a. Monad m => a -> m a
return Int64
int

{-|
  Save a given document under specified id.
-}
put :: Aeson.ToJSON a
    => Database
    -> String -- ^ Collection name
    -> a -- ^ Document
    -> Int64 -- ^ Document identifier. Not zero
    -> IO ()
put :: Database -> String -> a -> Int64 -> IO ()
put (Database _ ejdb :: EJDB
ejdb) collection :: String
collection obj :: a
obj id :: Int64
id =
    a -> (EJDB -> IO ()) -> IO ()
forall a b. ToJSON a => a -> (EJDB -> IO b) -> IO b
encode a
obj ((EJDB -> IO ()) -> IO ()) -> (EJDB -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \doc :: EJDB
doc -> String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection ->
    EJDB -> CString -> EJDB -> CIntMax -> IO RC
c_ejdb_put EJDB
ejdb CString
cCollection EJDB
doc (Int64 -> CIntMax
CIntMax Int64
id) IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC

{-|
  Apply JSON merge patch (rfc7396) to the document identified by id or insert new document under specified id.

  /This is an atomic operation./
-}
mergeOrPut :: Aeson.ToJSON a
           => Database
           -> String -- ^ Collection name
           -> a -- ^ JSON merge patch conformed to rfc7396 specification
           -> Int64 -- ^ Document identifier. Not zero
           -> IO ()
mergeOrPut :: Database -> String -> a -> Int64 -> IO ()
mergeOrPut (Database _ ejdb :: EJDB
ejdb) collection :: String
collection obj :: a
obj id :: Int64
id = String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$
    \cCollection :: CString
cCollection -> ByteString -> (CString -> IO ()) -> IO ()
forall a. ByteString -> (CString -> IO a) -> IO a
BS.useAsCString (a -> ByteString
forall a. ToJSON a => a -> ByteString
encodeToByteString a
obj) ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \jsonPatch :: CString
jsonPatch ->
    EJDB -> CString -> CString -> CIntMax -> IO RC
c_ejdb_merge_or_put EJDB
ejdb CString
cCollection CString
jsonPatch (Int64 -> CIntMax
CIntMax Int64
id) IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC

{-|
  Apply rfc6902\/rfc7396 JSON patch to the document identified by id.
-}
patch :: Aeson.ToJSON a
      => Database
      -> String -- ^ Collection name
      -> a -- ^ JSON patch conformed to rfc6902 or rfc7396 specification
      -> Int64 -- ^ Document identifier. Not zero
      -> IO ()
patch :: Database -> String -> a -> Int64 -> IO ()
patch (Database _ ejdb :: EJDB
ejdb) collection :: String
collection obj :: a
obj id :: Int64
id = String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$
    \cCollection :: CString
cCollection -> ByteString -> (CString -> IO ()) -> IO ()
forall a. ByteString -> (CString -> IO a) -> IO a
BS.useAsCString (a -> ByteString
forall a. ToJSON a => a -> ByteString
encodeToByteString a
obj) ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \jsonPatch :: CString
jsonPatch ->
    EJDB -> CString -> CString -> CIntMax -> IO RC
c_ejdb_patch EJDB
ejdb CString
cCollection CString
jsonPatch (Int64 -> CIntMax
CIntMax Int64
id) IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC

{-|
   Remove document identified by given id from collection coll.
-}
delete :: Database
       -> String -- ^ Collection name
       -> Int64 -- ^ Document identifier. Not zero
       -> IO ()
delete :: Database -> String -> Int64 -> IO ()
delete (Database _ ejdb :: EJDB
ejdb) collection :: String
collection id :: Int64
id = String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$
    \cCollection :: CString
cCollection -> EJDB -> CString -> CIntMax -> IO RC
c_ejdb_del EJDB
ejdb CString
cCollection (Int64 -> CIntMax
CIntMax Int64
id) IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC

{-|
  Create collection with given name if it has not existed before
-}
ensureCollection :: Database
                 -> String -- ^ Collection name
                 -> IO ()
ensureCollection :: Database -> String -> IO ()
ensureCollection (Database _ ejdb :: EJDB
ejdb) collection :: String
collection =
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection (EJDB -> CString -> IO RC
c_ejdb_ensure_collection EJDB
ejdb (CString -> IO RC) -> (RC -> IO ()) -> CString -> IO ()
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> RC -> IO ()
checkRC)

{-|
  Remove collection under the given name.
-}
removeCollection :: Database
                 -> String -- ^ Collection name
                 -> IO ()
removeCollection :: Database -> String -> IO ()
removeCollection (Database _ ejdb :: EJDB
ejdb) collection :: String
collection =
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection (EJDB -> CString -> IO RC
c_ejdb_remove_collection EJDB
ejdb (CString -> IO RC) -> (RC -> IO ()) -> CString -> IO ()
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> RC -> IO ()
checkRC)

{-|
  Rename collection to new name.
-}
renameCollection :: Database
                 -> String -- ^ Old collection name
                 -> String -- ^ New collection name
                 -> IO ()
renameCollection :: Database -> String -> String -> IO ()
renameCollection (Database _ ejdb :: EJDB
ejdb) collection :: String
collection newCollection :: String
newCollection =
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection ->
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
newCollection
                (EJDB -> CString -> CString -> IO RC
c_ejdb_rename_collection EJDB
ejdb CString
cCollection (CString -> IO RC) -> (RC -> IO ()) -> CString -> IO ()
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> RC -> IO ()
checkRC)

{-|
  Returns JSON document describing database structure. You can use the convenient data 'Database.EJDB2.Meta.Meta'
-}
getMeta :: Aeson.FromJSON a
        => Database
        -> IO (Maybe a) -- ^ JSON object describing ejdb storage. See data 'Database.EJDB2.Meta.Meta'

getMeta :: Database -> IO (Maybe a)
getMeta (Database _ ejdb :: EJDB
ejdb) = (Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a))
-> (Ptr EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ \jblPtr :: Ptr EJDB
jblPtr -> EJDB -> Ptr EJDB -> IO RC
c_ejdb_get_meta EJDB
ejdb Ptr EJDB
jblPtr
    IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC IO () -> IO (Maybe a) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO (Maybe a) -> IO () -> IO (Maybe a)
forall a b. IO a -> IO b -> IO a
finally (Ptr EJDB -> IO EJDB
forall a. Storable a => Ptr a -> IO a
peek Ptr EJDB
jblPtr IO EJDB -> (EJDB -> IO (Maybe a)) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= EJDB -> IO (Maybe a)
forall a. FromJSON a => EJDB -> IO (Maybe a)
decode) (Ptr EJDB -> IO ()
c_jbl_destroy Ptr EJDB
jblPtr)

{-|
  Create index with specified parameters if it has not existed before.

  /Index path must be fully specified as rfc6901 JSON pointer and must not countain unspecified *\/** element in middle sections./

  > ensureIndex database "mycoll" "/address/street" [uniqueIndexMode | strIndexMode]
-}
ensureIndex :: Database
            -> String -- ^ Collection name
            -> String -- ^ rfc6901 JSON pointer to indexed field
            -> [IndexMode.IndexMode] -- ^ Index mode
            -> IO ()
ensureIndex :: Database -> String -> String -> [IndexMode] -> IO ()
ensureIndex (Database _ ejdb :: EJDB
ejdb) collection :: String
collection path :: String
path indexMode :: [IndexMode]
indexMode =
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection -> String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
path ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$
    \cPath :: CString
cPath -> EJDB -> CString -> CString -> CUChar -> IO RC
c_ejdb_ensure_index EJDB
ejdb CString
cCollection CString
cPath CUChar
mode IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC
  where
    mode :: CUChar
mode = IndexMode -> CUChar
IndexMode.unIndexMode (IndexMode -> CUChar) -> IndexMode -> CUChar
forall a b. (a -> b) -> a -> b
$ [IndexMode] -> IndexMode
IndexMode.combineIndexMode [IndexMode]
indexMode

{-|
  Remove index if it has existed before.
-}
removeIndex :: Database
            -> String -- ^ Collection name
            -> String -- ^ rfc6901 JSON pointer to indexed field
            -> [IndexMode.IndexMode] -- ^ Index mode
            -> IO ()
removeIndex :: Database -> String -> String -> [IndexMode] -> IO ()
removeIndex (Database _ ejdb :: EJDB
ejdb) collection :: String
collection path :: String
path indexMode :: [IndexMode]
indexMode =
    String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
collection ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \cCollection :: CString
cCollection -> String -> (CString -> IO ()) -> IO ()
forall a. String -> (CString -> IO a) -> IO a
withCString String
path ((CString -> IO ()) -> IO ()) -> (CString -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$
    \cPath :: CString
cPath -> EJDB -> CString -> CString -> CUChar -> IO RC
c_ejdb_remove_index EJDB
ejdb CString
cCollection CString
cPath CUChar
mode IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC
  where
    mode :: CUChar
mode = IndexMode -> CUChar
IndexMode.unIndexMode (IndexMode -> CUChar) -> IndexMode -> CUChar
forall a b. (a -> b) -> a -> b
$ [IndexMode] -> IndexMode
IndexMode.combineIndexMode [IndexMode]
indexMode

{-|
  Creates an online database backup image and copies it into the specified target file.
  During online backup phase read/write database operations are allowed and not
  blocked for significant amount of time. Backup finish time is placed into result
  as number of milliseconds since epoch.

  Online backup guaranties what all records before timestamp will
  be stored in backup image. Later, online backup image can be
  opened as ordinary database file.

  /In order to avoid deadlocks: close all opened database cursors before calling this method or do call in separate thread./
-}
onlineBackup :: Database
             -> String -- ^ Backup file path
             -> IO Word64 -- ^ Backup completion timestamp

onlineBackup :: Database -> String -> IO Word64
onlineBackup (Database _ ejdb :: EJDB
ejdb) filePath :: String
filePath = String -> (CString -> IO Word64) -> IO Word64
forall a. String -> (CString -> IO a) -> IO a
withCString String
filePath ((CString -> IO Word64) -> IO Word64)
-> (CString -> IO Word64) -> IO Word64
forall a b. (a -> b) -> a -> b
$ \cFilePath :: CString
cFilePath ->
    (Ptr RC -> IO Word64) -> IO Word64
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr RC -> IO Word64) -> IO Word64)
-> (Ptr RC -> IO Word64) -> IO Word64
forall a b. (a -> b) -> a -> b
$ \timestampPtr :: Ptr RC
timestampPtr -> EJDB -> Ptr RC -> CString -> IO RC
c_ejdb_online_backup EJDB
ejdb Ptr RC
timestampPtr CString
cFilePath
    IO RC -> (RC -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= RC -> IO ()
checkRC IO () -> IO RC -> IO RC
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr RC -> IO RC
forall a. Storable a => Ptr a -> IO a
peek Ptr RC
timestampPtr IO RC -> (RC -> IO Word64) -> IO Word64
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \(CUIntMax t :: Word64
t) -> Word64 -> IO Word64
forall (m :: * -> *) a. Monad m => a -> m a
return Word64
t