All Projects → zmactep → hasbolt

zmactep / hasbolt

Licence: BSD-3-Clause license
Haskell driver for Neo4j 3+ (BOLT protocol)

Programming Languages

haskell
3896 projects

Projects that are alternatives of or similar to hasbolt

neo4j-java-driver-spring-boot-starter
Automatic configuration of Neo4j's Java Driver for Spring Boot applications
Stars: ✭ 33 (-56%)
Mutual labels:  neo4j-driver
email-validate-hs
Email address validation for Haskell
Stars: ✭ 39 (-48%)
Mutual labels:  hackage
neo4j.cr
Pure-Crystal implementation of Neo4j's Bolt protocol
Stars: ✭ 29 (-61.33%)
Mutual labels:  neo4j-driver
OGMNeo
[No Maintenance] Neo4j nodeJS OGM(object-graph mapping) abstraction layer
Stars: ✭ 54 (-28%)
Mutual labels:  neo4j-driver
neo4j-jdbc
JDBC driver for Neo4j
Stars: ✭ 110 (+46.67%)
Mutual labels:  neo4j-driver
angular-neo4j
Neo4j Bolt driver wrapper for Angular
Stars: ✭ 18 (-76%)
Mutual labels:  neo4j-driver
hip
Haskell Image Processing Library
Stars: ✭ 108 (+44%)
Mutual labels:  hackage
neo4rs
Neo4j driver for rust
Stars: ✭ 41 (-45.33%)
Mutual labels:  neo4j-driver
neo4j-graphql-py
A GraphQL to Cypher query execution layer for Neo4j and Python GraphQL implementations.
Stars: ✭ 14 (-81.33%)
Mutual labels:  neo4j-driver
seabolt
Neo4j Bolt Connector for C
Stars: ✭ 37 (-50.67%)
Mutual labels:  neo4j-driver
py2neo
Py2neo is a comprehensive Neo4j driver library and toolkit for Python.
Stars: ✭ 1,105 (+1373.33%)
Mutual labels:  neo4j-driver
clock
High-resolution clock functions: monotonic, realtime, cputime.
Stars: ✭ 52 (-30.67%)
Mutual labels:  hackage
hackage-ui
Follow us: https://twitter.com/HaskellSpot Support the project: https://ko-fi.com/visortelle
Stars: ✭ 84 (+12%)
Mutual labels:  hackage
hackage-cli
CLI tool for Hackage
Stars: ✭ 22 (-70.67%)
Mutual labels:  hackage
hPDB
PDB parser in Haskell
Stars: ✭ 20 (-73.33%)
Mutual labels:  hackage
sdl2-image
Haskell bindings to SDL2_image.
Stars: ✭ 16 (-78.67%)
Mutual labels:  hackage
hackage-diff
Compare the public API of different versions of a Hackage library
Stars: ✭ 50 (-33.33%)
Mutual labels:  hackage
htoml
TOML file format parser in Haskell
Stars: ✭ 39 (-48%)
Mutual labels:  hackage

HasBOLT

Travis GitHub Build hackage hackage-deps

Haskell driver for Neo4j 3+ (BOLT protocol)

Documentation

To build Haddock documentation run:

$ stack haddock

Usage example

{-# LANGUAGE OverloadedStrings #-}

import Database.Bolt

import Data.Default
import Data.Text
import Control.Monad
import Control.Monad.Except

-- Simple request can be done by using 'query' function. It returns a list of 'Record's which
-- are special dictionaries from 'Text' to any serializable 'Value'. You can extract this values by key using 'at' function.
nineties :: BoltActionT IO [Text]
nineties = do records <- query "MATCH (nineties:Movie) WHERE nineties.released >= 1990 AND nineties.released < 2000 RETURN nineties.title"
              forM records $ \record -> record `at` "nineties.title"

-- All types that can be processed by 'at' are instances of 'RecordValue' classtype.
-- You can implement new unpackers by your own.
--
-- If you want to perform some request multiple times with neo4j caching speedup,
-- you can use 'queryP' function that takes not only the Cypher request but also
-- a parameters dictionary.
genericABN :: RecordValue a => Text -> BoltActionT IO [a]
genericABN name = do toms' <- queryP "MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN tom"
                                     (props ["name" =: name])
                     nodes <- forM toms' $ \record -> record `at` "tom"
                     forM nodes $ \node -> nodeProps node `at` "name"

-- Hasbolt has a special 'Node' type to unpack graph nodes. You also can find 'Relationship',
-- 'URelationship' and 'Path' as built-in types.
actorsByNameYear :: Text -> Int -> BoltActionT IO [Node]
actorsByNameYear name year = do toms' <- queryP "MATCH (n:Person {name: {props}.name, born: {props}.born}) RETURN n" 
                                                (props ["props" =: props ["name" =: name, "born" =: year]])
                                forM toms' $ \record -> record `at` "n"

actorsByName :: Text -> BoltActionT IO [Text]
actorsByName = genericABN

-- This request raises 'WrongMessageFormat' error, as it cannot unpack 'Text' values as 'Int's.
wrongType :: Text -> BoltActionT IO [Int]
wrongType = genericABN 

-- Database server answers with a 'ResponseError' exception on any syntax error or internal database problem.
typoInRequest :: Text -> BoltActionT IO [Text]
typoInRequest name = do toms' <- queryP "MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN not_tom"
                                        (props ["name" =: name])
                        nodes <- forM toms' $ \record -> record `at` "tom"
                        forM nodes $ \node -> nodeProps node `at` "name"

-- 'RecordHasNoKey' is thrown in case of a wrong key usage in 'at'.
typoInField :: Text -> BoltActionT IO [Text]
typoInField name = do toms' <- queryP "MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN tom" 
                                      (props ["name" =: name])
                      nodes <- forM toms' $ \record -> record `at` "not_tom"
                      forM nodes $ \node -> nodeProps node `at` "name"

main :: IO ()
main = do pipe <- connect $ def { user = "neo4j", password = "12345" }
          -- Prints nineties example from Movies tutorial
          putStrLn "Movies (nineties):" 
          titles <- run pipe nineties 
          forM_ titles print
          -- Prints all actors called Tom 
          putStrLn "\nActors (Tom):"
          toms' <- run pipe $ actorsByName "Tom" 
          forM_ toms' print
          -- Prints Tom Hanks 
          putStrLn "\nNodes (Tom Cruise):"
          nodes' <- run pipe $ actorsByNameYear "Tom Cruise" 1962 
          forM_ nodes' print
           -- Prints an error as type is wrong 
          putStrLn "\nWrong return type:"
          wtype' <- run pipe $ wrongType "Tom" `catchError`
                                 \e@(WrongMessageFormat _) -> liftIO (print e) >> pure [] 
          forM_ wtype' print
          -- Prints an error as the request contains a typo
          putStrLn "\nTypo in request:"
          typor' <- run pipe $ typoInRequest "Tom" `catchError`
                                 \(ResponseError e) -> liftIO (print e) >> pure [] 
          forM_ typor' print
          -- Prints an error as the field name contains a typo
          putStrLn "\nTypo in field:"
          typof' <- run pipe $ typoInField "Tom" `catchError` 
                                 \e@(RecordHasNoKey _) -> liftIO (print e) >> pure [] 
          forM_ typof' print
          close pipe

Notes

  • Do not forget to import Data.Default to use default connection configuration.
  • OverloadedStrings are very welcome, as the library doesn't use Strings at all.
  • You can use Database.Bolt.Lazy to work with lazy IO. In this case do not forget to read all the records before you send a next query.
  • See test/TransactionSpec.hs for an example of transactions usage.
  • Feel free to implement your own serialization procedures with Database.Bolt.Serialization module import.
  • Pipes work great with resource-pool.
  • For neo4j 3.4+ use version = 2 in connection configuration. This allows you to use new datatypes.
  • You can use both syntax variants to create properties dictionaries: fromList [("born", I 1962)] or props ["born" =: 1962].
  • Note that you have to make a type hint for Text values in the second construction, as Haskell cannot deduce it on its own.

New types

Neo4j 3.4+ implements BOLT v2 protocol (that still doesn't have any specification). Code inspection of neo4j sources led me to these new data types in v2. All of them are just structures with different signatures and fields.

  • Point2D
signature = 'X'
fields = { crs :: CoordinateReferenceSystem
         , x   :: Double
         , y   :: Double
         }
  • Point3D
signature = 'Y'
fields = { crs :: CoordinateReferenceSystem
         , x   :: Double
         , y   :: Double
         , z   :: Double
         }
  • Duration
signature = 'E'
fields = { months  :: Integer
         , days    :: Integer
         , seconds :: Integer
         , nanos   :: Integer
         }
  • Date
signature = 'D'
fields = { epochDays :: Integer
         }
  • Time
signature = 'T'
fields = { nanosOfDayLocal :: Integer
         , offsetSeconds   :: Integer
         }
  • LocalTime
signature = 't'
fields = { nanosOfDay :: Integer
         }
  • LocalDateTime
signature = 'd'
fields = { epochSeconds :: Integer
         , nano         :: Integer
         }
  • DateTimeWithZoneOffset
signature = 'F'
fields = { epochSecondsLocal :: Integer
         , nano              :: Integer
         , offsetSeconds     :: Integer
         }
  • DateTimeWithZoneName
signature = 'f'
fields = { epochSecondsLocal :: Integer
         , nano              :: Integer
         , zoneId            :: Integer
         }

Codes of Coordinate Reference Systems:

  • Cartesian 2D — 7203
  • Cartesian 3D — 9157
  • WGS-84 2D — 4326
  • WGS-84 3D — 4979

Example

λ> :set -XScopedTypeVariables 
λ> pipe <- connect $ def { user = "neo4j", password = "neo4j", version = 2 }
λ> point :: Value <- run pipe $ do records <- query "RETURN point({x: 1, y: 2, z: 3}) as point"
                                   (head records) `at` "point"
λ> point 
S (Structure {signature = 89, fields = [I 9157,F 1.0,F 2.0,F 3.0]})
λ> close pipe

Here 89 is an ASCII code for 'Y', 9157 shows default cartesian 3d coordinate reference system.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].