opportunity: Added Makefile and README
[nit.git] / lib / neo4j / neo4j.nit
index 9b7ad1c..385b966 100644 (file)
@@ -246,16 +246,55 @@ class Neo4jClient
        end
 
        # Retrieve all nodes with specified `lbl`
+       #
+       #     var client = new Neo4jClient("http://localhost:7474")
+       #     #
+       #     var andres = new NeoNode
+       #     andres.labels.add_all(["Human", "Male"])
+       #     client.save_node(andres)
+       #     var kate = new NeoNode
+       #     kate.labels.add_all(["Human", "Female"])
+       #     client.save_node(kate)
+       #     #
+       #     var nodes = client.nodes_with_label("Human")
+       #     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 nodes = new Array[NeoNode]
-               var batch = new NeoBatch(self)
-               for obj in res.as(JsonArray) do
-                       var node = new NeoNode.from_json(self, obj.as(JsonObject))
-                       batch.load_node(node)
+               for json in res.as(JsonArray) do
+                       var obj = json.as(JsonObject)
+                       var node = load_node(obj["self"].to_s)
+                       node.internal_properties = obj["data"].as(JsonObject)
+                       nodes.add node
+               end
+               return nodes
+       end
+
+       # Retrieve nodes belonging to all the specified `labels`.
+       #
+       #     var client = new Neo4jClient("http://localhost:7474")
+       #     #
+       #     var andres = new NeoNode
+       #     andres.labels.add_all(["Human", "Male"])
+       #     client.save_node(andres)
+       #     var kate = new NeoNode
+       #     kate.labels.add_all(["Human", "Female"])
+       #     client.save_node(kate)
+       #     #
+       #     var nodes = client.nodes_with_labels(["Human", "Male"])
+       #     assert nodes.has(andres)
+       #     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"))
+               var nodes = new Array[NeoNode]
+               for json in res.as(JsonObject)["data"].as(JsonArray) do
+                       var obj = json.as(JsonArray).first.as(JsonObject)
+                       var node = load_node(obj["self"].to_s)
+                       node.internal_properties = obj["data"].as(JsonObject)
                        nodes.add node
                end
-               batch.execute
                return nodes
        end
 
@@ -345,14 +384,12 @@ end
 # For more details, see: http://docs.neo4j.org/chunked/milestone/rest-api-cypher.html
 class CypherQuery
        # Query string to perform
-       private var query: String
+       private var query: String = ""
 
        # `params` to embed in the query like in prepared statements
        var params = new JsonObject
 
-       init do
-               self.query = ""
-       end
+       init do end
 
        # init the query from a query string
        init from_string(query: String) do
@@ -449,10 +486,10 @@ end
 #     assert node["name"] == "Andres"  # loaded lazily from base
 abstract class NeoEntity
        # Neo4j client connector
-       private var neo: Neo4jClient
+       private var neo: Neo4jClient is noinit
 
        # Entity unique URL in Neo4j REST API
-       var url: nullable String
+       var url: nullable String = null
 
        # Temp id used in batch mode to update the entity
        private var batch_id: nullable Int = null
@@ -690,7 +727,7 @@ class NeoEdge
        fun from: NeoNode do return internal_from or else load_from
 
        private fun load_from: NeoNode do
-               var node = new NeoNode.from_neo(neo, internal_from_url.to_s)
+               var node = neo.load_node(internal_from_url.to_s)
                internal_from = node
                return node
        end
@@ -699,7 +736,7 @@ class NeoEdge
        fun to: NeoNode do return internal_to or else load_to
 
        private fun load_to: NeoNode do
-               var node = new NeoNode.from_neo(neo, internal_to_url.to_s)
+               var node = neo.load_node(internal_to_url.to_s)
                internal_to = node
                return node
        end
@@ -768,13 +805,6 @@ class NeoBatch
 
        # Load a node in batch mode also load labels, data and edges
        fun load_node(node: NeoNode) do
-               load_node_data(node)
-               load_node_labels(node)
-               load_node_out_edges(node)
-       end
-
-       # Load data into node
-       private fun load_node_data(node: NeoNode) do
                var job = new_job(node)
                job.action = load_node_data_action
                job.method = "GET"
@@ -783,11 +813,7 @@ class NeoBatch
                else
                        job.to = "\{{node.batch_id.to_s}\}"
                end
-       end
-
-       # Load labels into node
-       private fun load_node_labels(node: NeoNode) do
-               var job = new_job(node)
+               job = new_job(node)
                job.action = load_node_labels_action
                job.method = "GET"
                if node.id != null then
@@ -797,9 +823,17 @@ class NeoBatch
                end
        end
 
-       # Load out edges into node
-       private fun load_node_out_edges(node: NeoNode) do
+       # Load in and out edges into node
+       fun load_node_edges(node: NeoNode) do
                var job = new_job(node)
+               job.action = load_node_in_edges_action
+               job.method = "GET"
+               if node.id != null then
+                       job.to = "/node/{node.id.to_s}/relationships/in"
+               else
+                       job.to = "\{{node.batch_id.to_s}\}/relationships/in"
+               end
+               job = new_job(node)
                job.action = load_node_out_edges_action
                job.method = "GET"
                if node.id != null then
@@ -809,6 +843,15 @@ class NeoBatch
                end
        end
 
+       # Create a `NeoNode` or a `NeoEdge` in batch mode.
+       fun save_entity(nentity: NeoEntity) do
+               if nentity isa NeoNode then
+                       save_node(nentity)
+               else if nentity isa NeoEdge then
+                       save_edge(nentity)
+               else abort
+       end
+
        # Create a node in batch mode also create labels and edges
        fun save_node(node: NeoNode) do
                if node.id != null or node.batch_id != null then return
@@ -825,7 +868,7 @@ class NeoBatch
                job.to = "\{{node.batch_id.to_s}\}/labels"
                job.body = new JsonArray.from(node.labels)
                # add edges
-               save_edges(node.out_edges)
+               #save_edges(node.out_edges)
        end
 
        # Create multiple nodes
@@ -898,12 +941,19 @@ class NeoBatch
                                var labels = new Array[String]
                                for l in res["body"].as(JsonArray) do labels.add l.to_s
                                node.internal_labels = labels
+                       else if job.action == load_node_in_edges_action then
+                               var node = job.entity.as(NeoNode)
+                               var edges = res["body"].as(JsonArray)
+                               node.internal_in_edges = new List[NeoEdge]
+                               for edge in edges do
+                                       node.internal_in_edges.add client.load_edge(edge.as(JsonObject)["self"].to_s)
+                               end
                        else if job.action == load_node_out_edges_action then
                                var node = job.entity.as(NeoNode)
                                var edges = res["body"].as(JsonArray)
                                node.internal_out_edges = new List[NeoEdge]
                                for edge in edges do
-                                       node.internal_out_edges.add new NeoEdge.from_json(client, edge.as(JsonObject))
+                                       node.internal_out_edges.add client.load_edge(edge.as(JsonObject)["self"].to_s)
                                end
                        end
                end
@@ -917,7 +967,8 @@ class NeoBatch
        private fun create_edge_action: Int do return 2
        private fun load_node_data_action: Int do return 3
        private fun load_node_labels_action: Int do return 4
-       private fun load_node_out_edges_action: Int do return 5
+       private fun load_node_in_edges_action: Int do return 5
+       private fun load_node_out_edges_action: Int do return 6
 end
 
 # A job that can be executed in a `NeoBatch`