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 StringOStream
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 StringIStream(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 IStream = null is writable
78 # The stream to use for `save` and `save_part`.
79 var ostream
: nullable OStream = null is writable
81 # Use the specified `IOStream`.
82 init from_io
(graph
: NeoGraph, iostream
: IOStream) do
88 # Use the specified string to load the graph.
89 init from_string
(graph
: NeoGraph, string
: String) do
91 istream
= new StringIStream(string
)
94 redef fun isolated_save
do return true
97 var istream
= self.istream
98 assert istream
isa IStream
100 graph
.load_json
(istream
.read_all
)
104 redef fun save_part
(nodes
, edges
) do
105 var ostream
= self.ostream
106 assert ostream
isa OStream
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 for json_node
in json_nodes
do
172 assert json_node
isa JsonObject
173 var node
= new NeoNode.from_json_object
(json_node
)
177 var json_edges
= o
["edges"].as(JsonArray)
178 for json_edge
in json_edges
do
179 assert json_edge
isa JsonObject
180 var from
= nodes
[nodes
.id_from_jsonable
(json_edge
["from"])]
181 var to
= nodes
[nodes
.id_from_jsonable
(json_edge
["to"])]
182 var rel_type
= json_edge
["type"].as(String)
183 var json_properties
= json_edge
["properties"].as(JsonObject)
184 var edge
= new NeoEdge(from
, rel_type
, to
)
185 edge
.properties
.recover_with
(json_properties
)
190 redef fun to_json
do return to_json_by_append
192 # Append the JSON representation of `self` to the specified buffer.
194 # For a description of the format, see `JsonGraphStore`.
197 redef fun append_json
(b
) do
198 b
.append
"\{\"nodes\
":["
199 append_entities_json
(nodes
, b
)
200 b
.append
"],\"edges\
":["
201 append_entities_json
(edges
, b
)
205 # Encode `self` in JSON.
207 # For a description of the format, see `JsonGraphStore`.
210 private fun append_entities_json
(entities
: Collection[NeoEntity],
212 var i
= entities
.iterator
214 i
.item
.append_json_for
(self, b
)
218 entity
.append_json_for
(self, b
)
224 redef class NeoNodeCollection
225 # Convert the specified JSON value into a local ID.
226 fun id_from_jsonable
(id
: nullable Jsonable): ID_TYPE do return id
.as(ID_TYPE)
229 redef class NeoEntity
231 # Append the JSON representation of the entity to the specified buffer.
232 fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) is abstract
235 # Make `NeoNode` `Jsonable`.
239 # Retrieve the node from the specified JSON value.
241 # Note: Here, the `"id"` is optional and ignored.
245 # var node = new NeoNode.from_json("""
247 # "labels": ["foo", "Bar"],
253 # assert ["foo", "Bar"] == node.labels
254 # assert 42 == node["baz"]
255 init from_json
(t
: Text) do
256 from_json_object
(t
.parse_json
.as(JsonObject))
259 # Retrieve the node from the specified JSON value.
261 # Note: Here, the `"id"` is optional and ignored.
264 init from_json_object
(o
: JsonObject) do
266 var labels
= o
["labels"].as(JsonArray)
267 for lab
in labels
do self.labels
.add
(lab
.as(String))
268 var json_properties
= o
["properties"].as(JsonObject)
269 properties
.recover_with
(json_properties
)
272 redef fun to_json
do return to_json_by_append
274 # Append the JSON representation of the node to the specified buffer.
277 redef fun append_json
(b
) do
278 b
.append
"\{\"labels\
":["
279 var i
= labels
.iterator
281 i
.item
.append_json
(b
)
288 b
.append
"],\"properties\
":"
289 properties
.append_json
(b
)
293 redef fun to_s
do return to_json
295 # Append the JSON representation of the node to the specified buffer.
296 redef fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) do
303 # Append the JSON representation of the relationship to the specified buffer.
305 # Use the IDs specfied by `graph.nodes`.
306 redef fun append_json_for
(graph
: NeoGraph, buffer
: Buffer) do
307 buffer
.append
"\{\"type\
":"
308 rel_type
.append_json
(buffer
)
309 buffer
.append
",\"properties\
":"
310 properties
.append_json
(buffer
)
311 buffer
.append
",\"from\
":"
312 graph
.nodes
.id_of
(from
).append_json
(buffer
)
313 buffer
.append
",\"to\
":"
314 graph
.nodes
.id_of
(to
).append_json
(buffer
)