grim - brings graphs to Nim
grim is a graph data structure for the Nim language, written in the Nim language, and inspired by the Neo4j database. Data is stored in Nodes and Edges. Each Node and Edge has a label for grouping data, and can store an arbitrary of (Json-like) properties.
Getting up and running
grim is packaged with the nimble, the Nim package manager and should be easy to use with a recent Nim distribution.
You need the Nim compiler, obviously. See the nim-lang homepage for easy installation instructions.
grim is easily used with the nimble package manager. To install on your local computer use
nimble install grim
To use it in your own project add as requirement to the .nimble file:
grim is hopefully quite easy to use. Some use cases can be found in the tests/ folder. Here are some other examples. The auto-generated documentation can be found here.
A graph consists of nodes and edges. Both nodes and edges carry a label for categorization, and both nodes and edges can store data attributes in key/value pairs. First, create a new graph:
import grim var g = newGraph("my graph") echo g.name # "my graph" doAssert g.numberOfNodes == 0 and g.numberOfEdges == 0
The graph variable must be mutable. Second, add some data to the graph. First, nodes:
let c1 = g.addNode("Country", %(name: "Sweden", GDP_per_capita: 53208.0)) c2 = g.addNode("Country", %(name: "Norway", GDP_per_capita: 74536.0)) c3 = g.addNode("Country", %(name: "Germany", GDP_per_capita: 52559.0)) o1 = g.addNode("Organization", %(name: "NATO", founded: 1949, active: true)) o2 = g.addNode("Organization", %(name: "EU", founded: 1993, active: true)) o3 = g.addNode("Organization", %(name: "Nordic Council", founded: 1952))
%operator can be used on tuples of key/value pairs to store properties in heterogeneous tables.
oidfor the node/edge. The
oidis a unique string that is either specified with the
oid=argument when creating the node or else auto-generated. If the
oidis not desired, it must be explicitly discarded.
Read data from the graph:
doAssert g.getNode(c1).label == "Country" doAssert g.getNode(c1).properties["name"] == "Sweden"
Now, add edges to the graph:
let e1 = g.addEdge(c1, o2, "MEMBER_OF", %(since: 1995)) e2 = g.addEdge(c1, o3, "MEMBER_OF", %(since: 1952)) e3 = g.addEdge(c2, o3, "MEMBER_OF", %(since: 1952, membership: "full"))
oidsof the nodes are used to create a new edge (with label "MEMBER_OF") with properties. Some information of the graph:
# Iterator over all nodes for node in g.nodes: echo node.label, ": ", node.properties # Iterator over all edges for edge in g.edges: echo edge.label, ": ", edge.properties # Iterator over all edges between two nodes for edge in g.getEdges(c1, o3): echo edge.label, ": ", edge.properties # Iterator over neighbor nodes for node in g.neighbors(c1): echo node.label, ": ", node.properties
Loading and saving graphs
Graph structures can be loaded and saved in YAML format. grim uses the NimYAML library for this. The two procs
saveYaml are available.
import grim var g = loadYaml("example.yaml") # Load graph from YAML file g.saveYaml("example2.yaml") # Save a copy of the file
DSL for specifying graphs
A small DSL is provided to construct graphs. Consider this toy example:
import grim graph g "Some people": nodes: Person: "a nice guy": name: "Santa Claus" age: 108 "a smart girl": name: "Jane Austen" wealth: 10304.3 edges: "a nice guy" -> "a smart girl": DELIVERS: category: "writing material" value: 204
This will make the graph structure available to the rest of the code as a mutable variable
g. This example shows how to access the graph properties.
let p1 = g.getNode("a nice guy") p2 = g.getNode("a smart girl") doAssert p1.label == "Character" and p2.label == "Character" doAssert p1.properties["name"].getStr == "Santa Claus" doAssert p1.properties["age"].getInt == 108 doAssert p2.properties["name"].getStr == "Jane Austen" doAssert p2.properties["wealth"].getFloat == 10304.3 for e in g.getEdges("a nice guy", "a smart girl"): doAssert e.label == "DELIVERS" doAssert e.properties["category"].getStr == "writing material" doAssert e.properties["value"].getInt == 204
Running the tests
The tests can be run with nimble, they test basic usage of the package such as creating and modifying graphs, the DSL, and loading/saving graphs as YAML.
I'll be happy to accept and review PRs and discuss other things on submitted to Issues.
- Erik G. Brandt - Original author - ebran
This project is licensed under the MIT License.