diff --git a/Data/Aeson/TH/Smart.hs b/Data/Aeson/TH/Smart.hs
--- a/Data/Aeson/TH/Smart.hs
+++ b/Data/Aeson/TH/Smart.hs
@@ -1,9 +1,6 @@
 {-# LANGUAGE CPP, NoImplicitPrelude, TemplateHaskell, OverloadedStrings, ScopedTypeVariables #-}
 -- Shamelessly copied from Bryan O'Sullivan, 2011
 
--- EVENTUALLY MAKE THIS WORK BY GETTING THE CONSTRUCTOR BEFOREHAND.
--- 
-
 module Data.Aeson.TH.Smart
     ( deriveJSON
 
@@ -37,7 +34,7 @@
                            )
 import Data.Maybe          ( Maybe(Nothing, Just) )
 import Prelude             ( String, (-), Integer, fromIntegral, not,
-                             error, filter, fst, snd, Bool(..), flip, maybe)
+                             error, filter, fst, snd, Bool(..), flip, maybe, (>))
 import Text.Printf         ( printf )
 import Text.Show           ( show )
 #if __GLASGOW_HASKELL__ < 700
@@ -210,7 +207,7 @@
                                     litE (integerL ix) `appE` e | (ix, e) <- zip [(0::Integer)..] es]
                      ret = noBindS $ [e|return|] `appE` varE mv
                      fltr = [e| V.filter (not . (== Null))|]
-                 [e|Array|] `appE` (fltr `appE` (varE 'V.create `appE` doE (newMV:stmts++[ret])))
+                 [e|\x-> if V.length x > 0 then Array x else Null|] `appE` (fltr `appE` (varE 'V.create `appE` doE (newMV:stmts++[ret])))
     let b = case wrapper of
               Nothing -> js
               (Just wrapper') -> wrapper' [infixApp (litE (stringL "value")) [e|(.=)|] js]
@@ -223,7 +220,7 @@
              | (arg, (field, _, _)) <- zip args' ts
              ]
     let b = case withExp of
-              Nothing -> [e|object|] `appE` ([e| filter (not . (==Null) . snd) |] `appE` listE js)
+              Nothing -> [e|object|] `appE` ([e| filter (not . disposable . snd) |] `appE` listE js)
               (Just wrapper) -> wrapper js
     match (conP conName $ map varP args) (normalB b) []
 -- Infix constructors.
@@ -239,6 +236,10 @@
 encodeArgs withExp withField (ForallC _ _ con) =
     encodeArgs withExp withField con
 
+disposable Null = True
+disposable (Array x) = V.null x
+disposable _ = False
+
 --------------------------------------------------------------------------------
 -- FromJSON
 --------------------------------------------------------------------------------
@@ -390,8 +391,8 @@
     match (conP 'Object [varP obj])
       (guardedB [tupSeq (conGuard mcon conName, foldl' (\a b -> infixApp a [|(<*>)|] b)
                     (infixApp (conE conName) [|(<$>)|] x) xs)]) []
-parseArgs tName _ (InfixC _ conName _) mcon = parseProduct tName conName 2 mcon
-parseArgs tName withField (ForallC _ _ con) mcon = parseArgs tName withField con mcon
+parseCon tName _ (InfixC _ conName _) mcon = parseProduct tName conName 2 mcon
+parseCon tName withField (ForallC _ _ con) mcon = parseCon tName withField con mcon
 
 
 -- | Generates code to parse the JSON encoding of an n-ary
diff --git a/Data/Default/NewTH.hs b/Data/Default/NewTH.hs
new file mode 100644
--- /dev/null
+++ b/Data/Default/NewTH.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Data.Default.NewTH (deriveDefault) where
+
+import Control.Applicative
+import Data.Default
+import Data.List
+import Language.Haskell.TH
+
+createInstance :: Bool -> Name -> [Name] -> Name -> [Type] -> Q Dec
+createInstance b typeConstructorName typeVariables constructorName constructorArgumentTypes = do
+	return $ InstanceD (if b then constraints typeVariables else [])
+		(AppT (ConT ''Default) (foldl' (\x y -> AppT x (VarT y)) (ConT typeConstructorName) typeVariables))
+		[FunD 'def [Clause [] (NormalB (foldl' (\x _ -> AppE x (VarE 'def)) (ConE constructorName) constructorArgumentTypes)) []]]
+
+
+constraints :: [Name] -> [Pred]
+constraints = map (ClassP ''Default . return . VarT)
+
+instanceQ :: Bool -> Name -> [TyVarBndr] -> Name -> [Type] -> Q [Dec]
+instanceQ b t vs c as = return <$> createInstance b t (map name vs) c as
+
+name :: TyVarBndr -> Name
+name (PlainTV n) = n
+name (KindedTV n k) = n
+
+deriveDefault :: Bool -> Name -> Q [Dec]
+deriveDefault b n = do
+	info <- reify n
+	case info of
+		TyConI (DataD _ qn tvars (con:_) _) -> case con of
+			NormalC  conName ts -> instanceQ b qn tvars conName (map snd ts)
+			RecC     conName ts -> instanceQ b qn tvars conName (map (\(v,s,t) -> t) ts)
+			InfixC t conName t' -> instanceQ b qn tvars conName (map snd [t, t'])
+			_ -> fail $ "Dunno how to derive Default instances for existential types"
+		TyConI (DataD _ _ _ [] _) -> fail $ "Really? You want to derive a Default instance for an uninhabited type?"
+		_ -> fail $ "Couldn't derive a Default instance; didn't know what to do with " ++ pprint info
diff --git a/Data/Default/TH.hs b/Data/Default/TH.hs
deleted file mode 100644
--- a/Data/Default/TH.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-module Data.Default.TH (deriveDefault) where
-
-import Control.Applicative
-import Data.Default
-import Data.List
-import Language.Haskell.TH
-
-createInstance :: Name -> [Name] -> Name -> [Type] -> Q Dec
-createInstance typeConstructorName typeVariables constructorName constructorArgumentTypes = do
-	let reqs = constraints typeVariables
-	return $ InstanceD reqs
-		(AppT (ConT ''Default) (foldl' (\x y -> AppT x (VarT y)) (ConT typeConstructorName) typeVariables))
-		[FunD 'def [Clause [] (NormalB (foldl' (\x _ -> AppE x (VarE 'def)) (ConE constructorName) constructorArgumentTypes)) []]]
-
-constraints :: [Name] -> [Pred]
-constraints = map (ClassP ''Default . return . VarT)
-
-instanceQ :: Name -> [TyVarBndr] -> Name -> [Type] -> Q [Dec]
-instanceQ t vs c as = return <$> createInstance t (map name vs) c as
-
-name :: TyVarBndr -> Name
-name (PlainTV n) = n
-name (KindedTV n k) = n
-
-deriveDefault :: Name -> Q [Dec]
-deriveDefault n = do
-	info <- reify n
-	case info of
-		TyConI (DataD _ qn tvars (con:_) _) -> case con of
-			NormalC  conName ts -> instanceQ qn tvars conName (map snd ts)
-			RecC     conName ts -> instanceQ qn tvars conName (map (\(v,s,t) -> t) ts)
-			InfixC t conName t' -> instanceQ qn tvars conName (map snd [t, t'])
-			_ -> fail $ "Dunno how to derive Default instances for existential types"
-		TyConI (DataD _ _ _ [] _) -> fail $ "Really? You want to derive a Default instance for an uninhabited type?"
-		_ -> fail $ "Couldn't derive a Default instance; didn't know what to do with " ++ pprint info
diff --git a/Database/Cypher.hs b/Database/Cypher.hs
--- a/Database/Cypher.hs
+++ b/Database/Cypher.hs
@@ -1,16 +1,30 @@
 {-# LANGUAGE OverloadedStrings, TemplateHaskell, DeriveDataTypeable, ScopedTypeVariables, FlexibleInstances #-}
 module Database.Cypher (
 	Cypher,
-	Entity,
+	Entity(..),
 	CypherResult(..),
+	LuceneQuery,
 	runCypher,
 	cypher,
+	cypherGetNode,
+	cypherCreate,
+	cypherGet,
+	cypherSet,
+	luceneEncode,
+	withCypherManager,
 	CypherException(..),
+	DBInfo(..),
 	Hostname,
 	Port,
-	OneTuple(..)
+	CypherVal(..),
+	CypherVals(..),
+	CypherCol(..),
+	CypherCols(..),
+	CypherMaybe(..),
+	CypherUnit(..)
 	) where
 
+import Database.Cypher.Lucene
 import Data.Aeson
 import Data.Aeson.TH
 import Data.Aeson.Types
@@ -19,22 +33,31 @@
 import Data.Conduit
 import Data.Typeable
 import Data.Text (Text)
-import Data.Tuple.OneTuple
 import Control.Exception
 import Control.Applicative
 import Control.Monad
 import Data.Monoid
-import Control.Monad.IO.Class (liftIO)
+import Control.Monad.IO.Class
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Lazy as L
+import Data.Text.Lazy.Encoding (encodeUtf8)
+import qualified Data.HashMap.Strict as H
+import Data.Text.Lazy.Builder
+import Data.Aeson.Encode
 
-data DBInfo = DBInfo Hostname Port Manager
+-- | Information about your neo4j configuration needed to make requests over the REST api.
+data DBInfo = DBInfo {
+	cypher_hostname :: Hostname,
+	cypher_port :: Port
+} deriving (Show, Eq)
+
 type Hostname = S.ByteString
 type Port = Int
+$(deriveJSON (drop 7) ''DBInfo)
 
 -- | All interaction with Neo4j is done through the Cypher monad. Use 'cypher' to add a query to the monad.
 newtype Cypher a = Cypher {
-	uncypher :: (DBInfo -> ResourceT IO a)
+	uncypher :: ((DBInfo, Manager) -> ResourceT IO a)
 }
 
 -- | Raw result data returned by Neo4j. Only use this if you care about column headers.
@@ -43,6 +66,24 @@
 	resdata :: a
 } deriving (Show, Eq)
 
+-- | A single result returned by Neo4j.
+newtype CypherVal a = CypherVal a deriving (Eq, Show)
+
+-- | A single column returned by Neo4j.
+newtype CypherCol a = CypherCol a deriving (Eq, Show)
+
+-- | Columns returned by Neo4j.
+newtype CypherCols a = CypherCols a deriving (Eq, Show)
+
+-- | Values returned by Neo4j.
+newtype CypherVals a = CypherVals [a] deriving (Eq, Show)
+
+-- | Possibly a value returned by Neo4j
+data CypherMaybe a = CypherJust a | CypherNothing deriving (Eq, Show)
+
+-- | No value returned from Neo4j
+data CypherUnit = CypherUnit deriving (Show)
+
 data CypherRequest = CypherRequest {
 	req_query :: Text,
 	req_params :: Value
@@ -50,27 +91,21 @@
 
 -- | A neo4j node or edge
 data Entity a = Entity {
-	entity_id :: Text,
+	entity_id :: String,
+	entity_properties :: String,
 	entity_data :: a
 } deriving (Show, Eq)
 
 instance FromJSON a => FromJSON (Entity a) where
 	parseJSON (Object v) = Entity <$>
 							v .: "self" <*>
+							v .: "properties" <*>
 							v .: "data"
 	parseJSON _ = mempty
 
 instance ToJSON (Entity a) where
 	toJSON = toJSON . entity_id
 
-instance FromJSON a => FromJSON (OneTuple a) where
-	parseJSON x = do
-		[l] <- parseJSON x
-		return $ OneTuple l
-
-instance ToJSON a => ToJSON (OneTuple a) where
-	toJSON = toJSON . (\x->[x])
-
 $(deriveJSON (drop 3) ''CypherResult)
 $(deriveJSON (drop 4) ''CypherRequest)
 
@@ -88,52 +123,45 @@
 			a <- cmd con
 			uncypher (f a) con
 
-class FromCypher a where
-	fromCypher :: L.ByteString -> a
+instance MonadIO Cypher where
+	liftIO f = Cypher $ const (liftIO f)
 
-instance FromCypher () where
-	fromCypher _ = ()
+instance FromJSON a => FromJSON (CypherVal a) where
+	parseJSON x = do
+		(CypherResult _ [[d]]) <- parseJSON x
+		return $ CypherVal d
 
-instance FromJSON a => FromCypher (CypherResult a) where
-	fromCypher bs = 
-		case decode bs of
-			Just x -> x
-			Nothing -> throwClientParse bs
+instance FromJSON a => FromJSON (CypherCol a) where
+	parseJSON x = do
+		(CypherResult _ [d]) <- parseJSON x
+		return $ CypherCol d
 
-instance FromJSON a => FromCypher [a] where
-	fromCypher bs =
- 		case decode bs of
-			Just (CypherResult _ ds) -> ds
-			_ -> throwClientParse bs
+instance FromJSON a => FromJSON (CypherCols a) where
+	parseJSON x = do
+		(CypherResult _ d) <- parseJSON x
+		return $ CypherCols d
 
-instance FromJSON a => FromCypher (OneTuple a) where
-	fromCypher bs =
-		case decode bs of
-			Just (CypherResult _ [d]) -> d
-			_ -> throwClientParse bs
+instance FromJSON a => FromJSON (CypherVals a) where
+	parseJSON x = do
+		(CypherResult _ d) <- parseJSON x
+		liftM CypherVals (mapM safeHead d)
 
-instance (FromJSON a, FromJSON b) => FromCypher (a,b) where
-	fromCypher bs =
-		case decode bs of
-			Just (CypherResult _ [d]) -> d
-			_ -> throwClientParse bs
+instance FromJSON a => FromJSON (CypherMaybe a) where
+	parseJSON x = do
+		(CypherResult _ ds) <- parseJSON x
+		case ds of
+			[[d]] -> return $ CypherJust d
+			_ -> return CypherNothing
 
-instance (FromJSON a, FromJSON b, FromJSON c) => FromCypher (a,b,c) where
-	fromCypher bs =
-		case decode bs of
-			Just (CypherResult _ [d]) -> d
-			_ -> throwClientParse bs
+instance FromJSON CypherUnit where parseJSON _ = return CypherUnit
 
-instance FromJSON a => FromCypher (Maybe a) where
-	fromCypher bs =
-		case decode bs of
-			Just (CypherResult _ [a]) -> Just a
-			Just (CypherResult _ []) -> Nothing
-			_ -> throwClientParse bs
+safeHead :: [a] -> Parser a
+safeHead [a] = return a
+safeHead _ = mzero
 
 -- | Perform a cypher query
-cypher :: FromCypher a => Text -> Value -> Cypher a
-cypher txt params = Cypher $ \(DBInfo h p m)-> do
+cypher :: FromJSON a => Text -> Value -> Cypher a
+cypher txt params = Cypher $ \(DBInfo h p, m)-> do
 	let req = def { host = h, port = p,
 					path = "db/data/cypher",
 					requestBody = RequestBodyLBS (encode $ CypherRequest txt params),
@@ -143,13 +171,77 @@
 				  }
 	r <- httpLbs req m
 	let sci = statusCode (responseStatus r)
-	if 200 <= sci && sci < 300 then return (fromCypher (responseBody r))
+	if 200 <= sci && sci < 300
+		then (case decode (responseBody r) of
+				Nothing -> throwClientParse (responseBody r)
+				Just x-> return x)
 		else throw $ CypherServerException (responseStatus r) (responseHeaders r) (responseBody r)
 
+-- | Create a cypher node
+cypherCreate :: (ToJSON a, FromJSON b) => a -> Cypher b
+cypherCreate obj = Cypher $ \(DBInfo h p, m)-> do
+	let req = def { host = h, port = p,
+					path = "db/data/node",
+					requestBody = RequestBodyLBS (encode obj),
+					requestHeaders = headerAccept "application/json" : headerContentType "application/json" : requestHeaders def,
+					method = "POST",
+					checkStatus = (\_ _-> Nothing)
+				  }
+	r <- httpLbs req m
+	let sci = statusCode (responseStatus r)
+	if 200 <= sci && sci < 300
+		then (case decode (responseBody r) of
+				Nothing -> throwClientParse (responseBody r)
+				Just x-> return x)
+		else throw $ CypherServerException (responseStatus r) (responseHeaders r) (responseBody r)
+
+-- | Get a cypher node
+cypherGetNode :: FromJSON b => Entity b -> Cypher (Entity b)
+cypherGetNode e = Cypher $ \(DBInfo h p, m)-> do
+	req <- liftIO $ parseUrl (entity_id e)
+	let req' = req { host = h, port = p,
+					requestHeaders = headerAccept "application/json" : headerContentType "application/json" : requestHeaders def,
+					method = "GET",
+					checkStatus = (\_ _-> Nothing)
+				  }
+	r <- httpLbs req m
+	let sci = statusCode (responseStatus r)
+	if 200 <= sci && sci < 300
+		then (case decode (responseBody r) of
+				Nothing -> throwClientParse (responseBody r)
+				Just x-> return x)
+		else throw $ CypherServerException (responseStatus r) (responseHeaders r) (responseBody r)
+
+-- | Set cypher properties. This currently cannot be done through cypher queries.
+cypherSet :: (ToJSON a, ToJSON a1) => (Entity a) -> a1 -> Cypher ()
+cypherSet e obj = Cypher $ \(DBInfo h p, m)-> do
+	let Object o1 = toJSON (entity_data e)
+	let Object o2 = toJSON obj
+	let body = RequestBodyLBS $ encodeUtf8 $ toLazyText $ fromValue $ Object (o2 `H.union` o1)
+	req <- liftIO $ parseUrl (entity_properties e)
+	let req' = req { host = h, port = p,
+					requestBody = body,
+					requestHeaders = headerAccept "application/json" : headerContentType "application/json" : requestHeaders def,
+					method = "PUT",
+					checkStatus = (\_ _-> Nothing)
+				  }
+	r <- httpLbs req' m
+	let sci = statusCode (responseStatus r)
+	if 200 <= sci && sci < 300
+		then return ()
+		else (let e = CypherServerException (responseStatus r) (responseHeaders r) (responseBody r)
+			 in traceShow e (throw e))
+
+-- | Get the nodes matching the given lucene query
+cypherGet :: (ToJSON a1, FromJSON a) => a1 -> Cypher a
+cypherGet lc = cypher "start a = node:node_auto_index({lc}) return a" $ object ["lc" .= lc]
+
+-- | Get the http connection manager for a Cypher monad
+withCypherManager :: (Manager -> ResourceT IO a) -> Cypher a
+withCypherManager f = Cypher (\(_,m)-> f m)
+
 -- | Execute some number of cypher queries
-runCypher :: Cypher a -> Hostname -> Port -> IO a
-runCypher c h p =
+runCypher :: Cypher a -> DBInfo -> Manager -> IO a
+runCypher c dbi m =
 	runResourceT $ do
-    	manager <- liftIO $ newManager def
-    	uncypher c (DBInfo h p manager)
-
+    	uncypher c (dbi, m)
diff --git a/Database/Cypher/Lucene.hs b/Database/Cypher/Lucene.hs
--- a/Database/Cypher/Lucene.hs
+++ b/Database/Cypher/Lucene.hs
@@ -1,21 +1,51 @@
 {-# LANGUAGE BangPatterns, OverloadedStrings #-}
 -- Code shamelessly copied from Data.Aeson.Encode by MailRank, Inc.
 
-module Database.Cypher.Lucene (luceneEncode) where
+module Database.Cypher.Lucene (
+  luceneEncode, LuceneQuery,
+  (.>.), (.<.), (.=.), (.&.), (.|.), (.-.),
+  to, xto
+  ) where
 import Data.Aeson.Types (ToJSON(..), Value(..))
 import Data.Attoparsec.Number (Number(..))
 import Data.Monoid
-import Data.Text.Lazy.Encoding (encodeUtf8)
 import Data.Text.Lazy.Builder
 import Data.Text.Lazy.Builder.Int (decimal)
 import Data.Text.Lazy.Builder.RealFloat (realFloat)
 import Numeric (showHex)
 import qualified Data.ByteString.Lazy as L
+import qualified Data.Text.Lazy.Encoding as LE
 import qualified Data.HashMap.Strict as H
 import qualified Data.Text as T
+import qualified Data.Text.Encoding as TE
 import qualified Data.Vector as V
 
+type LuceneQuery = L.ByteString
 
+(.>.) :: ToJSON a => T.Text -> a -> LuceneQuery
+t .>. o = L.fromChunks[TE.encodeUtf8 t] <> LE.encodeUtf8 " > " <> luceneEncode o
+
+(.<.) :: ToJSON a => T.Text -> a -> LuceneQuery
+t .<. o = L.fromChunks[TE.encodeUtf8 t] <> LE.encodeUtf8 " < " <> luceneEncode o
+
+(.=.) :: ToJSON a => T.Text -> a -> LuceneQuery
+t .=. o = L.fromChunks[TE.encodeUtf8 t] <> LE.encodeUtf8 " = " <> luceneEncode o
+
+(.&.) :: LuceneQuery -> LuceneQuery -> LuceneQuery
+a .&. b =  a <> LE.encodeUtf8 " AND " <> b
+
+(.|.) :: LuceneQuery -> LuceneQuery -> LuceneQuery
+a .|. b = a <> LE.encodeUtf8 " OR " <> b
+
+(.-.) :: LuceneQuery -> LuceneQuery -> LuceneQuery
+a .-. b = a <> LE.encodeUtf8 " NOT " <> b
+
+to :: ToJSON a => a -> a -> LuceneQuery
+a `to` b = LE.encodeUtf8 "[" <> luceneEncode a <> LE.encodeUtf8 " TO " <> luceneEncode b <> LE.encodeUtf8 "]"
+
+xto :: ToJSON a => a -> a -> LuceneQuery
+a `xto` b = LE.encodeUtf8 "{" <> luceneEncode a <> LE.encodeUtf8 " TO " <> luceneEncode b <> LE.encodeUtf8 "}"
+
 fromValue :: Value -> Builder
 fromValue Null = mempty
 fromValue (Bool b) = if b then "true" else "false"
@@ -59,5 +89,6 @@
     | otherwise               = realFloat d
 
 -- | Convert an object to a Lucene query encoded as an 'L.ByteString'.
-luceneEncode :: ToJSON a => a -> L.ByteString
-luceneEncode = encodeUtf8 . toLazyText . fromValue . toJSON
+luceneEncode :: ToJSON a => a -> LuceneQuery
+luceneEncode = LE.encodeUtf8 . toLazyText . fromValue . toJSON
+
diff --git a/cypher.cabal b/cypher.cabal
--- a/cypher.cabal
+++ b/cypher.cabal
@@ -7,7 +7,7 @@
 -- The package version. See the Haskell package versioning policy
 -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
 -- standards guiding when and how versions should be incremented.
-Version:             0.2
+Version:             0.4
 
 -- A short (one-line) description of the package.
 Synopsis:            Haskell bindings for the neo4j "cypher" query language
@@ -47,7 +47,7 @@
 
 Library
   -- Modules exported by the library.
-  Exposed-modules:     Database.Cypher, Database.Cypher.Lucene, Data.Aeson.TH.Smart, Data.Default.TH
+  Exposed-modules:     Database.Cypher, Database.Cypher.Lucene, Data.Aeson.TH.Smart, Data.Default.NewTH
   
   -- Packages needed in order to build this package.
   Build-depends:         base < 5
@@ -61,7 +61,6 @@
                        , vector <1
                        , unordered-containers <1
                        , attoparsec <1
-                       , OneTuple <1
                        , data-default <1
                        , template-haskell >= 2.7
   
