{"id":13687220,"url":"https://github.com/zmactep/hasbolt","last_synced_at":"2025-08-21T23:19:50.051Z","repository":{"id":41344736,"uuid":"75013565","full_name":"zmactep/hasbolt","owner":"zmactep","description":"Haskell driver for Neo4j 3+ (BOLT protocol)","archived":false,"fork":false,"pushed_at":"2024-03-04T21:23:40.000Z","size":150,"stargazers_count":81,"open_issues_count":10,"forks_count":13,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-06-20T11:44:59.508Z","etag":null,"topics":["hackage","haskell","haskell-driver","neo4j-driver"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zmactep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-28T20:57:44.000Z","updated_at":"2025-03-19T23:41:56.000Z","dependencies_parsed_at":"2024-06-18T18:13:26.479Z","dependency_job_id":"ede9739e-8401-4322-ac2f-f4f0daa6a062","html_url":"https://github.com/zmactep/hasbolt","commit_stats":{"total_commits":86,"total_committers":10,"mean_commits":8.6,"dds":"0.32558139534883723","last_synced_commit":"80de79a23131b59da005b66bd23796924e6d8a08"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/zmactep/hasbolt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zmactep%2Fhasbolt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zmactep%2Fhasbolt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zmactep%2Fhasbolt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zmactep%2Fhasbolt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zmactep","download_url":"https://codeload.github.com/zmactep/hasbolt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zmactep%2Fhasbolt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263342358,"owners_count":23451991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hackage","haskell","haskell-driver","neo4j-driver"],"created_at":"2024-08-02T15:00:50.516Z","updated_at":"2025-07-03T14:33:54.454Z","avatar_url":"https://github.com/zmactep.png","language":"Haskell","funding_links":[],"categories":["Haskell"],"sub_categories":[],"readme":"HasBOLT\n=======\n\n[![Travis](https://img.shields.io/travis/zmactep/hasbolt.svg)](https://travis-ci.org/zmactep/hasbolt)\n[![GitHub Build](https://github.com/zmactep/hasbolt/workflows/build/badge.svg)](https://github.com/zmactep/hasbolt/actions?query=workflow%3A%22build%22)\n[![hackage](https://img.shields.io/hackage/v/hasbolt.svg)](https://hackage.haskell.org/package/hasbolt)\n[![hackage-deps](https://img.shields.io/hackage-deps/v/hasbolt.svg)](https://hackage.haskell.org/package/hasbolt)\n\nHaskell driver for Neo4j 3+ (BOLT protocol)\n\nDocumentation\n-------------\n\nTo build Haddock documentation run:\n```bash\n$ stack haddock\n```\n\nUsage example\n-------------\n\n```haskell\n{-# LANGUAGE OverloadedStrings #-}\n\nimport Database.Bolt\n\nimport Data.Default\nimport Data.Text\nimport Control.Monad\nimport Control.Monad.Except\n\n-- Simple request can be done by using 'query' function. It returns a list of 'Record's which\n-- are special dictionaries from 'Text' to any serializable 'Value'. You can extract this values by key using 'at' function.\nnineties :: BoltActionT IO [Text]\nnineties = do records \u003c- query \"MATCH (nineties:Movie) WHERE nineties.released \u003e= 1990 AND nineties.released \u003c 2000 RETURN nineties.title\"\n              forM records $ \\record -\u003e record `at` \"nineties.title\"\n\n-- All types that can be processed by 'at' are instances of 'RecordValue' classtype.\n-- You can implement new unpackers by your own.\n--\n-- If you want to perform some request multiple times with neo4j caching speedup,\n-- you can use 'queryP' function that takes not only the Cypher request but also\n-- a parameters dictionary.\ngenericABN :: RecordValue a =\u003e Text -\u003e BoltActionT IO [a]\ngenericABN name = do toms' \u003c- queryP \"MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN tom\"\n                                     (props [\"name\" =: name])\n                     nodes \u003c- forM toms' $ \\record -\u003e record `at` \"tom\"\n                     forM nodes $ \\node -\u003e nodeProps node `at` \"name\"\n\n-- Hasbolt has a special 'Node' type to unpack graph nodes. You also can find 'Relationship',\n-- 'URelationship' and 'Path' as built-in types.\nactorsByNameYear :: Text -\u003e Int -\u003e BoltActionT IO [Node]\nactorsByNameYear name year = do toms' \u003c- queryP \"MATCH (n:Person {name: {props}.name, born: {props}.born}) RETURN n\" \n                                                (props [\"props\" =: props [\"name\" =: name, \"born\" =: year]])\n                                forM toms' $ \\record -\u003e record `at` \"n\"\n\nactorsByName :: Text -\u003e BoltActionT IO [Text]\nactorsByName = genericABN\n\n-- This request raises 'WrongMessageFormat' error, as it cannot unpack 'Text' values as 'Int's.\nwrongType :: Text -\u003e BoltActionT IO [Int]\nwrongType = genericABN \n\n-- Database server answers with a 'ResponseError' exception on any syntax error or internal database problem.\ntypoInRequest :: Text -\u003e BoltActionT IO [Text]\ntypoInRequest name = do toms' \u003c- queryP \"MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN not_tom\"\n                                        (props [\"name\" =: name])\n                        nodes \u003c- forM toms' $ \\record -\u003e record `at` \"tom\"\n                        forM nodes $ \\node -\u003e nodeProps node `at` \"name\"\n\n-- 'RecordHasNoKey' is thrown in case of a wrong key usage in 'at'.\ntypoInField :: Text -\u003e BoltActionT IO [Text]\ntypoInField name = do toms' \u003c- queryP \"MATCH (tom:Person) WHERE tom.name CONTAINS {name} RETURN tom\" \n                                      (props [\"name\" =: name])\n                      nodes \u003c- forM toms' $ \\record -\u003e record `at` \"not_tom\"\n                      forM nodes $ \\node -\u003e nodeProps node `at` \"name\"\n\nmain :: IO ()\nmain = do pipe \u003c- connect $ def { user = \"neo4j\", password = \"12345\" }\n          -- Prints nineties example from Movies tutorial\n          putStrLn \"Movies (nineties):\" \n          titles \u003c- run pipe nineties \n          forM_ titles print\n          -- Prints all actors called Tom \n          putStrLn \"\\nActors (Tom):\"\n          toms' \u003c- run pipe $ actorsByName \"Tom\" \n          forM_ toms' print\n          -- Prints Tom Hanks \n          putStrLn \"\\nNodes (Tom Cruise):\"\n          nodes' \u003c- run pipe $ actorsByNameYear \"Tom Cruise\" 1962 \n          forM_ nodes' print\n           -- Prints an error as type is wrong \n          putStrLn \"\\nWrong return type:\"\n          wtype' \u003c- run pipe $ wrongType \"Tom\" `catchError`\n                                 \\e@(WrongMessageFormat _) -\u003e liftIO (print e) \u003e\u003e pure [] \n          forM_ wtype' print\n          -- Prints an error as the request contains a typo\n          putStrLn \"\\nTypo in request:\"\n          typor' \u003c- run pipe $ typoInRequest \"Tom\" `catchError`\n                                 \\(ResponseError e) -\u003e liftIO (print e) \u003e\u003e pure [] \n          forM_ typor' print\n          -- Prints an error as the field name contains a typo\n          putStrLn \"\\nTypo in field:\"\n          typof' \u003c- run pipe $ typoInField \"Tom\" `catchError` \n                                 \\e@(RecordHasNoKey _) -\u003e liftIO (print e) \u003e\u003e pure [] \n          forM_ typof' print\n          close pipe\n```\n\nNotes\n-----\n\n* Do not forget to import `Data.Default` to use default connection configuration.\n* `OverloadedStrings` are very welcome, as the library doesn't use `String`s at all.\n* 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.\n* See [`test/TransactionSpec.hs`](https://github.com/zmactep/hasbolt/blob/master/test/TransactionSpec.hs) for an example of transactions usage.\n* Feel free to implement your own serialization procedures with `Database.Bolt.Serialization` module import.\n* Pipes work great with [resource-pool](https://hackage.haskell.org/package/resource-pool).\n* For neo4j 3.4+ use `version = 2` in connection configuration. This allows you to use [new datatypes](#new-types).\n* You can use both syntax variants to create properties dictionaries: `fromList [(\"born\", I 1962)]` or `props [\"born\" =: 1962]`.\n* Note that you have to make a type hint for `Text` values in the second construction, as Haskell cannot deduce it on its own.\n\nNew types\n---------\n\nNeo4j 3.4+ implements BOLT v2 protocol (that still doesn't have any specification). Code inspection of [neo4j sources](https://github.com/neo4j/neo4j) led me to these new data types in v2. All of them are just structures with different signatures and fields.\n* Point2D\n```haskell\nsignature = 'X'\nfields = { crs :: CoordinateReferenceSystem\n         , x   :: Double\n         , y   :: Double\n         }\n```\n* Point3D\n```haskell\nsignature = 'Y'\nfields = { crs :: CoordinateReferenceSystem\n         , x   :: Double\n         , y   :: Double\n         , z   :: Double\n         }\n```\n* Duration\n```haskell\nsignature = 'E'\nfields = { months  :: Integer\n         , days    :: Integer\n         , seconds :: Integer\n         , nanos   :: Integer\n         }\n```\n* Date\n```haskell\nsignature = 'D'\nfields = { epochDays :: Integer\n         }\n```\n* Time\n```haskell\nsignature = 'T'\nfields = { nanosOfDayLocal :: Integer\n         , offsetSeconds   :: Integer\n         }\n```\n* LocalTime\n```haskell\nsignature = 't'\nfields = { nanosOfDay :: Integer\n         }\n```\n* LocalDateTime\n```haskell\nsignature = 'd'\nfields = { epochSeconds :: Integer\n         , nano         :: Integer\n         }\n```\n* DateTimeWithZoneOffset\n```haskell\nsignature = 'F'\nfields = { epochSecondsLocal :: Integer\n         , nano              :: Integer\n         , offsetSeconds     :: Integer\n         }\n```\n* DateTimeWithZoneName\n```haskell\nsignature = 'f'\nfields = { epochSecondsLocal :: Integer\n         , nano              :: Integer\n         , zoneId            :: Integer\n         }\n```\n\nCodes of Coordinate Reference Systems:\n* Cartesian 2D — 7203\n* Cartesian 3D — 9157\n* WGS-84 2D — 4326\n* WGS-84 3D — 4979\n\n### Example\n\n```haskell\nλ\u003e :set -XScopedTypeVariables \nλ\u003e pipe \u003c- connect $ def { user = \"neo4j\", password = \"neo4j\", version = 2 }\nλ\u003e point :: Value \u003c- run pipe $ do records \u003c- query \"RETURN point({x: 1, y: 2, z: 3}) as point\"\n                                   (head records) `at` \"point\"\nλ\u003e point \nS (Structure {signature = 89, fields = [I 9157,F 1.0,F 2.0,F 3.0]})\nλ\u003e close pipe\n```\n\nHere `89` is an [ASCII code](https://en.wikipedia.org/wiki/ASCII#Character_set) for 'Y', `9157` shows default cartesian 3d coordinate reference system.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzmactep%2Fhasbolt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzmactep%2Fhasbolt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzmactep%2Fhasbolt/lists"}