code: rename identifiers `with` since it is a keyword now
[nit.git] / lib / neo4j / neo4j.nit
index b84a60b..4694f8d 100644 (file)
@@ -64,6 +64,7 @@
 module neo4j
 
 import curl_json
+import error
 
 # Handles Neo4j server start and stop command
 #
@@ -123,7 +124,7 @@ class Neo4jClient
                self.cypher_url = root["cypher"].to_s
        end
 
-       fun service_root: Jsonable do return get("{base_url}/db/data")
+       fun service_root: Jsonable do return get(base_url / "db/data")
 
        # Is the connection with the Neo4j server ok?
        fun is_ok: Bool do return service_root isa JsonObject
@@ -259,7 +260,7 @@ class Neo4jClient
        #     assert nodes.has(andres)
        #     assert nodes.has(kate)
        fun nodes_with_label(lbl: String): Array[NeoNode] do
-               var res = get("{base_url}/db/data/label/{lbl}/nodes")
+               var res = get(base_url / "db/data/label/{lbl.to_percent_encoding}/nodes")
                var nodes = new Array[NeoNode]
                for json in res.as(JsonArray) do
                        var obj = json.as(JsonObject)
@@ -286,7 +287,21 @@ class Neo4jClient
        #     assert not nodes.has(kate)
        fun nodes_with_labels(labels: Array[String]): Array[NeoNode] do
                assert not labels.is_empty
-               var res = cypher(new CypherQuery.from_string("MATCH (n:{labels.join(":")}) RETURN n"))
+
+               # Build the query.
+               var buffer = new RopeBuffer
+               buffer.append "match n where \{label_0\} in labels(n)"
+               for i in [1..labels.length[ do
+                       buffer.append " and \{label_{i}\} in labels(n)"
+               end
+               buffer.append " return n"
+               var query = new CypherQuery.from_string(buffer.write_to_string)
+               for i in [0..labels.length[ do
+                       query.params["label_{i}"] = labels[i]
+               end
+
+               # Retrieve the answer.
+               var res = cypher(query)
                var nodes = new Array[NeoNode]
                for json in res.as(JsonObject)["data"].as(JsonArray) do
                        var obj = json.as(JsonArray).first.as(JsonObject)
@@ -336,29 +351,31 @@ class Neo4jClient
        # Parse the cURL `response` as a JSON string
        private fun parse_response(response: CurlResponse): Jsonable do
                if response isa CurlResponseSuccess then
-                       if response.body_str.is_empty then
+                       var str = response.body_str
+                       if str.is_empty then return new JsonObject
+                       var res = str.parse_json
+                       if res isa JsonParseError then
+                               var e = new NeoError(res.to_s, "JsonParseError")
+                               e.cause = res
+                               return e
+                       end
+                       if res == null then
+                               # empty response wrap it in empty object
                                return new JsonObject
-                       else
-                               var str = response.body_str
-                               var res = str.to_jsonable
-                               if res == null then
-                                       # empty response wrap it in empty object
-                                       return new JsonObject
-                               else if res isa JsonObject and res.has_key("exception") then
-                                       var error = "Neo4jError::{res["exception"] or else "null"}"
-                                       var msg = ""
-                                       if res.has_key("message") then
-                                               msg = res["message"].to_s
-                                       end
-                                       return new JsonError(error, msg.to_json)
-                               else
-                                       return res
+                       else if res isa JsonObject and res.has_key("exception") then
+                               var error = "Neo4jError::{res["exception"] or else "null"}"
+                               var msg = ""
+                               if res.has_key("message") then
+                                       msg = res["message"].to_s
                                end
+                               return new NeoError(msg, error)
+                       else
+                               return res
                        end
                else if response isa CurlResponseFailed then
-                       return new JsonError("Curl error", "{response.error_msg} ({response.error_code})")
+                       return new NeoError("{response.error_msg} ({response.error_code})", "CurlError")
                else
-                       return new JsonError("Curl error", "Unexpected response '{response}'")
+                       return new NeoError("Unexpected response \"{response}\".", "CurlError")
                end
        end
 end
@@ -388,8 +405,6 @@ class CypherQuery
        # `params` to embed in the query like in prepared statements
        var params = new JsonObject
 
-       init do end
-
        # init the query from a query string
        init from_string(query: String) do
                self.query = query
@@ -524,7 +539,7 @@ abstract class NeoEntity
        private var internal_properties: nullable JsonObject = null
 
        private fun load_properties: JsonObject do
-               var obj = neo.get("{url.to_s}/properties").as(JsonObject)
+               var obj = neo.get(url.to_s / "properties").as(JsonObject)
                internal_properties = obj
                return obj
        end
@@ -609,7 +624,7 @@ class NeoNode
 
        private fun load_labels: Array[String] do
                var labels = new Array[String]
-               var res = neo.get("{url.to_s}/labels")
+               var res = neo.get(url.to_s / "labels")
                if res isa JsonArray then
                        for val in res do labels.add val.to_s
                end
@@ -624,7 +639,7 @@ class NeoNode
 
        private fun load_in_edges: List[NeoEdge] do
                var edges = new List[NeoEdge]
-               var res = neo.get("{url.to_s}/relationships/in").as(JsonArray)
+               var res = neo.get(url.to_s / "relationships/in").as(JsonArray)
                for obj in res do
                        edges.add(new NeoEdge.from_json(neo, obj.as(JsonObject)))
                end
@@ -639,7 +654,7 @@ class NeoNode
 
        private fun load_out_edges: List[NeoEdge] do
                var edges = new List[NeoEdge]
-               var res = neo.get("{url.to_s}/relationships/out")
+               var res = neo.get(url.to_s / "relationships/out")
                for obj in res.as(JsonArray) do
                        edges.add(new NeoEdge.from_json(neo, obj.as(JsonObject)))
                end
@@ -896,7 +911,7 @@ class NeoBatch
        fun save_edges(edges: Collection[NeoEdge]) do for edge in edges do save_edge(edge)
 
        # Execute the batch and update local nodes
-       fun execute: List[JsonError] do
+       fun execute: List[NeoError] do
                var request = new JsonPOST(client.batch_url, client.curl)
                # request.headers["X-Stream"] = "true"
                var json_jobs = new JsonArray
@@ -908,16 +923,16 @@ class NeoBatch
        end
 
        # Associate data from response in original nodes and edges
-       private fun finalize_batch(response: Jsonable): List[JsonError] do
-               var errors = new List[JsonError]
+       private fun finalize_batch(response: Jsonable): List[NeoError] do
+               var errors = new List[NeoError]
                if not response isa JsonArray then
-                       errors.add(new JsonError("Neo4jError", "Unexpected batch response format"))
+                       errors.add(new NeoError("Unexpected batch response format.", "Neo4jError"))
                        return errors
                end
                # print " {res.length} jobs executed"
                for res in response do
                        if not res isa JsonObject then
-                               errors.add(new JsonError("Neo4jError", "Unexpected job format in batch response"))
+                               errors.add(new NeoError("Unexpected job format in batch response.", "Neo4jError"))
                                continue
                        end
                        var id = res["id"].as(Int)