{-# LANGUAGE OverloadedStrings #-} -- | -- Module: Database.Neo4j -- Copyright: (c) 2014, Antoni Silvestre -- License: MIT -- Maintainer: Antoni Silvestre -- Stability: experimental -- Portability: portable -- -- Library to interact with the Neo4j REST API. -- module Database.Neo4j ( -- * How to use this library -- $use -- * Connection handling objects Connection, Hostname, Port, newConnection, withConnection, -- * Main monadic type to handle sequences of commands to Neo4j Neo4j(..), -- * Constructing and managing node/relationship properties Val(..), PropertyValue(..), newval, (|:), Properties, emptyProperties, getProperties, getProperty, setProperties, setProperty, deleteProperties, deleteProperty, -- * Managing nodes Node, getNodeProperties, createNode, getNode, deleteNode, nodeId, nodePath, runNodeIdentifier, NodeIdentifier(..), NodePath(..), -- * Managing relationships Relationship, Direction(..), RelationshipType, createRelationship, getRelationship, deleteRelationship, getRelationships, relId, relPath, allRelationshipTypes, getRelProperties, getRelType, runRelIdentifier, getRelationshipFrom, getRelationshipTo, RelIdentifier(..), RelPath(..), -- * Managing labels and getting nodes by label EntityIdentifier(..), Label, allLabels, getLabels, getNodesByLabelAndProperty, addLabels, changeLabels, removeLabel, -- * Indexes Index(..), createIndex, getIndexes, dropIndex, -- * Exceptions Neo4jException(..) ) where import Database.Neo4j.Index import Database.Neo4j.Http import Database.Neo4j.Label import Database.Neo4j.Node import Database.Neo4j.Relationship import Database.Neo4j.Property import Database.Neo4j.Types -- $use -- -- In order to start issuing commands to neo4j you must establish a connection, in order to do that you can use -- the function 'withConnection': -- -- > withConnection "127.0.0.1" 7474 $ do -- > neo <- createNode M.empty -- > cypher <- createNode M.empty -- > r <- createRelationship "KNOWS" M.empty neo cypher -- > ... -- -- Also most calls have a batch analogue version, with batch mode you can issue several commands to Neo4j at once. -- In order to issue batches you must use the "Database.Neo4j.Batch" monad, parameters in batch mode can be actual -- entities already obtained by issuing regular commands or previous batch commands, or even batch futures, -- that is you can refer to entities created in the same batch, for instance: -- -- > withConnection "127.0.0.1" 7474 $ do -- > g <- B.runBatch $ do -- > neo <- B.createNode M.empty -- > cypher <- B.createNode M.empty -- > B.createRelationship "KNOWS" M.empty neo cypher -- > ... -- -- As you can see this example does the same thing the previous one does but it will be more efficient as it will -- be translated into only one request to the database. -- -- Batch commands return a "Database.Neo4j.Graph" object that holds all the information about relationships, nodes -- and their labels that can be inferred from running a batch command. -- -- Another example with batches would be for instance remove all the nodes in a "Database.Neo4j.Graph" object -- -- > withConnection "127.0.0.1" 7474 $ do -- > ... -- > B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) -- -- For more information about batch commands and graph objects you can refer to their "Database.Neo4j.Batch" and -- "Database.Neo4j.Graph" modules. -- -- Properties are hashmaps with key 'Data.Text' and values a custom type called 'PropertyValue'. -- This custom type tries to use Haskell's type system to match property values to what Neo4j expects, we only allow -- 'Int64', 'Double', 'Bool' and 'Text' like values and one-level arrays of these. -- The only restriction we cannot guarantee with these types is that arrays of values must be of the same type. -- -- In order to create a 'PropertyValue' from a literal or a value of one of the allowed types you can use the 'newval' -- function or the operator '|:' to create pairs of key values: -- -- > import qualified Data.HashMap.Lazy as M -- > -- > myval = newval False -- > someProperties = M.fromList ["mytext" |: ("mytext" :: T.Text), -- > "textarrayprop" |: ["a" :: T.Text, "", "adeu"], -- > "int" |: (-12 :: Int64), -- > "intarray" |: [1 :: Int64, 2], -- > "double" |: (-12.23 :: Double), -- > "doublearray" |: [0.1, -12.23 :: Double], -- > "bool" |: False, -- > "aboolproparray" |: [False, True] -- > ] -- -- When unexpected errors occur a 'Neo4jException' will be raised, sometimes with a specific exception value like for -- instance 'Neo4jNoEntityException', or more generic ones like 'Neo4jHttpException' or 'Neo4jParseException' -- if the server returns something totally unexpected. (I'm sure there's still work to do here preparing the code -- to return more specific exceptions for known scenarios) -- -- About Cypher support for now we allow sending queries with parameters, the result is a collection of column headers -- and JSON data values, the Graph object has the function addCypher that tries to find -- nodes and relationships in a cypher query result and insert them in a "Database.Neo4j.Graph" object -- -- > import qualified Database.Neo4j.Cypher as C -- > -- > withConnection host port $ do -- > ... -- > -- Run a cypher query with parameters -- > res <- C.cypher "CREATE (n:Person { name : {name} }) RETURN n" M.fromList [("name", C.newparam ("Pep" :: T.Text))] -- > -- > -- Get all nodes and relationships that this query returned and insert them in a Graph object -- > let graph = G.addCypher (C.fromSuccess res) G.empty -- > -- > -- Get the column headers -- > let columnHeaders = C.cols $ C.fromSuccess res -- > -- > -- Get the rows of JSON values received -- > let values = C.vals $ C.fromSuccess res