hypher-0.1.0: src/Database/Neo4j.hs
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module: Database.Neo4j
-- Copyright: (c) 2014, Antoni Silvestre
-- License: MIT
-- Maintainer: Antoni Silvestre <antoni.silvestre@gmail.com>
-- 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