1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
11 # Provides JSON as a mean to store graphs.
12 module neo4j
::graph
::json_graph_store
16 # Save or load a graph using a JSON document.
18 # The graph (or the specified part of it) is stored as a JSON object with the
19 # following properties:
21 # * `"nodes"`: An array with all nodes. Each node is an object with the
22 # following properties:
23 # * `"labels"`: An array of all applied labels.
24 # * `"properties"`: An object mapping each defined property to its value.
25 # * `"edges"`: An array with all relationships. Each relationship is an object
26 # with the following properties:
27 # * `"type"`: The type (`String`) of the relationship.
28 # * `"properties"`: An object mapping each defined property to its value.
29 # * `"from"`: The local ID of the source node.
30 # * `"to"`: The local ID of the destination node.
33 # import neo4j::graph::sequential_id
35 # var graph = new NeoGraph(new SequentialNodeCollection("nid"))
39 # a["Ultimate question of"] = new JsonArray.from(["life",
40 # "the Universe", "and Everything."])
41 # graph.nodes.register a
42 # var b = graph.create_node
45 # graph.edges.add new NeoEdge(a, "BAZ", b)
47 # var ostream = new StringWriter
48 # var store = new JsonGraphStore(graph)
49 # store.ostream = ostream
51 # assert ostream.to_s == """{"nodes":[""" + """
52 # {"labels":["Foo"],"properties":{"answer":42,""" + """
53 # "Ultimate question of":["life","the Universe","and Everything."],""" + """
55 # {"labels":["Foo","Bar"],"properties":{"nid":2}}],""" + """
56 # "edges":[{"type":"BAZ","properties":{},"from":1,"to":2}]}"""
60 # store.istream = new StringReader(ostream.to_s)
62 # assert 1 == graph.edges.length
63 # for edge in graph.edges do
64 # assert "BAZ" == edge.rel_type
65 # assert a.labels == edge.from.labels
66 # for k, v in a.properties do assert v == edge.from.properties[k]
67 # assert b.labels == edge.to.labels
68 # for k, v in b.properties do assert v == edge.to.properties[k]
70 # assert 2 == graph.nodes.length
75 # The stream to use for `load`.
76 var istream
: nullable Reader = null is writable
78 # The stream to use for `save` and `save_part`.
79 var ostream
: nullable Writer = null is writable
81 # Use the specified `Duplex`.
82 init from_io
(graph
: NeoGraph, iostream
: Duplex) do
88 # Use the specified string to load the graph.
89 init from_string
(graph
: NeoGraph, string
: String) do
91 istream
= new StringReader(string
)
94 redef fun isolated_save
do return true
97 var istream
= self.istream
98 assert istream
isa Reader
100 graph
.load_json
(istream
.read_all
)
104 redef fun save_part
(nodes
, edges
) do
105 var ostream
= self.ostream
106 assert ostream
isa Writer
108 ostream
.write
(graph
.to_json
)
116 # Retrieve the graph from the specified JSON document.
118 # For the expected format, see `JsonGraphStore`.
121 # import neo4j::graph::sequential_id
123 # var graph = new NeoGraph(new SequentialNodeCollection("node_id"))
124 # var a = new NeoNode
127 # a["Ultimate question of"] = new JsonArray.from(["life",
128 # "the Universe", "and Everything."])
129 # graph.nodes.register a
130 # var b = graph.create_node
133 # graph.edges.add new NeoEdge(a, "BAZ", b)
135 # graph = new NeoGraph.from_json(
136 # new SequentialNodeCollection("node_id"), graph.to_json)
137 # assert 1 == graph.edges.length
138 # for edge in graph.edges do
139 # assert "BAZ" == edge.rel_type
140 # assert a.labels == edge.from.labels
141 # for k, v in a.properties do assert v == edge.from.properties[k]
142 # assert b.labels == edge.to.labels
143 # for k, v in b.properties do assert v == edge.to.properties[k]
145 # assert 2 == graph.nodes.length
147 init from_json
(nodes
: NeoNodeCollection, t
: Text) do
148 from_json_object
(nodes
, t
.parse_json
.as(JsonObject))
151 # Retrieve the graph from the specified JSON object.
153 # For the expected format, see `JsonGraphStore`.
154 init from_json_object
(nodes
: NeoNodeCollection, o
: JsonObject) do
159 # Retrieve a part of the graph from the specified JSON document.
161 # For the expected format, see `JsonGraphStore`.
162 fun load_json
(t
: Text) do
163 load_json_object
(t
.parse_json
.as(JsonObject))
166 # Retrieve a part of the graph from the specified JSON object.
168 # For the expected format, see `JsonGraphStore`.
169 fun load_json_object
(o
: JsonObject) do
170 var json_nodes
= o
["nodes"].as(JsonArray)
171 var nodes
= self.nodes
172 nodes
.enlarge
(nodes
.length
)
173 for json_node
in json_nodes
do
174 assert json_node
isa JsonObject
175 var node
= new NeoNode.from_json_object
(json_node
)
179 var json_edges
= o
["edges"].as(JsonArray)
180 var edges
= self.edges
181 if edges
isa AbstractArray[NeoEdge] then edges
.enlarge
(edges
.length
)
182 for json_edge
in json_edges
do
183 assert json_edge
isa JsonObject
184 var from
= nodes
[nodes
.id_from_jsonable
(json_edge
["from"])]
185 var to
= nodes
[nodes
.id_from_jsonable
(json_edge
["to"])]
186 var rel_type
= json_edge
["type"].as(String)
187 var json_properties
= json_edge
["properties"].as(JsonObject)
188 var edge
= new NeoEdge(from
, rel_type
, to
)
189 edge
.properties
.recover_with
(json_properties
)
194 redef fun to_json
do return to_json_by_append
196 # Append the JSON representation of `self` to the specified buffer.
198 # For a description of the format, see `JsonGraphStore`.
201 redef fun append_json
(b
) do
202 b
.append
"\{\"nodes\
":["
203 append_entities_json
(nodes
, b
)
204 b
.append
"],\"edges\
":["
205 append_entities_json
(edges
, b
)
209 # Encode `self` in JSON.
211 # For a description of the format, see `JsonGraphStore`.
214 private fun append_entities_json
(entities
: Collection[NeoEntity],
216 var i
= entities
.iterator
218 i
.item
.append_json_for
(self, b
)
222 entity
.append_json_for
(self, b
)
228 redef class NeoNodeCollection
229 # Convert the specified JSON value into a local ID.
230 fun id_from_jsonable
(id
: nullable Jsonable): ID_TYPE do return id
.as(ID_TYPE)
233 redef class NeoEntity
235 # Append the JSON representation of the entity to the specified buffer.
236 fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) is abstract
239 # Make `NeoNode` `Jsonable`.
243 # Retrieve the node from the specified JSON value.
245 # Note: Here, the `"id"` is optional and ignored.
249 # var node = new NeoNode.from_json("""
251 # "labels": ["foo", "Bar"],
257 # assert ["foo", "Bar"] == node.labels
258 # assert 42 == node["baz"]
259 init from_json
(t
: Text) do
260 from_json_object
(t
.parse_json
.as(JsonObject))
263 # Retrieve the node from the specified JSON value.
265 # Note: Here, the `"id"` is optional and ignored.
268 init from_json_object
(o
: JsonObject) do
270 var labels
= o
["labels"].as(JsonArray)
271 for lab
in labels
do self.labels
.add
(lab
.as(String))
272 var json_properties
= o
["properties"].as(JsonObject)
273 properties
.recover_with
(json_properties
)
276 redef fun to_json
do return to_json_by_append
278 # Append the JSON representation of the node to the specified buffer.
281 redef fun append_json
(b
) do
282 b
.append
"\{\"labels\
":["
283 var i
= labels
.iterator
285 i
.item
.append_json
(b
)
292 b
.append
"],\"properties\
":"
293 properties
.append_json
(b
)
297 redef fun to_s
do return to_json
299 # Append the JSON representation of the node to the specified buffer.
300 redef fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) do
307 # Append the JSON representation of the relationship to the specified buffer.
309 # Use the IDs specfied by `graph.nodes`.
310 redef fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) do
311 buffer
.append
"\{\"type\
":"
312 rel_type
.append_json
(buffer
)
313 buffer
.append
",\"properties\
":"
314 properties
.append_json
(buffer
)
315 buffer
.append
",\"from\
":"
316 graph
.nodes
.id_of
(from
).append_json
(buffer
)
317 buffer
.append
",\"to\
":"
318 graph
.nodes
.id_of
(to
).append_json
(buffer
)