After my initial implementation of some BrainExpanded-related ideas on top of dgraph using its GraphQL interface, I realized that the GraphQL data model was a bit restrictive for what I needed. It’s a long story so I won’t bore you.
So, I started thinking of Neo4j. Jim pinged me and said something along the lines of “how dare you not use Neo4j?” Well… he didn’t really say that but I know that’s what he meant 🙂
The first thing I started was to change the implementation of my Web API (implemented in .NET) to interact with Neo4j. I very quickly realized that having to map my domain data model to/from Neo4j’s data model was going to be a pain, whether I used the Neo4j’s .NET driver or the Neo4j Web API. Why worrying about serializing/deserializing objects when middleware can do that automatically? Granted, I may not be able to do absolutely everything that Neo4j has to offer but the middleware should be able to handle most things for me such serialization/deserialization, mapping between .NET and Neo4j data types, representation of nodes and relationships, simple graph queries, navigational queries with projects/filtering, graph traversals, etc.
So, with the help of Github’s copilot and its various models, I built the “Graph Model” abstraction and an implementation of it for Neo4j.
This project is just a tool for what I am doing with my main project so I decided to open source the code in case others are interested. There is a ton more to be done and a lot of cleaning up to do. The coding models tend to write a lot of code which isn’t necessarily beautiful and well-organized. I collaborated with the models by writing tests. I wrote the Graph.Model
interfaces, most of the Neo4jGraphProvider
class, and most of the tests. The coding AIs wrote most of the LINQ expression → Cypher logic.
GraphModel is a .NET library that provides a clean abstraction over graph databases, particularly Neo4j. It offers a set of interfaces and tools to model, query, and manage graph data effectively. Think of it as your trusty compass in the vast forest of nodes and relationships.
Ready to dive in? Here’s how you can get started.
1. Install the NuGet Package:
> dotnet new console
> dotnet add package Cvoya.Graph.Model
> dotnet add package Cvoya.Graph.Provider.Neo4j
2. Define your application domain entities
using Cvoya.Graph.Model;
public class Person : Node
{
public string Name { get; set; }
}
public class Knows : Relationship
{
public DateTime Since { get; set; }
}
3. Initialize a provider
Providers implement the Cvoya.Graph.Model.IGraph
interface. The Cvoya.Graph.Provider.Neo4j
is such a provider.
var provider = new Neo4jGraphProvider("bolt://localhost:7687", "neo4j", "password");
4. Ready to partyyyyy
var alice = new Person { Name = "Alice" };
var bob = new Person { Name = "Bob" };
var knows = new Knows { Since = DateTime.Now };
context.CreateRelationship(alice, knows, bob);
And voilà! You’ve just established a relationship between Alice and Bob.
GraphModel requires providers to implement LINQ queries. The Neo4j provider offers extensive support for queries over the graph database. Check out the tests in the graphmodel
repo to get ideas.
var friendsOfAlice = context.Match<Person>()
.Where(p => p.Name == "Alice")
.Traverse<Knows>()
.To<Person>();
Need to perform multiple operations atomically? GraphModel has you covered:
using var tx = context.BeginTransaction())
var charlie = new Person { Name = "Charlie" };
context.CreateNode(charlie);
tx.Commit();
Customize your graph entities using attributes:
[Node(Label = "User")]
public class Person : Node
{
[Property(Name = "full_name")]
public string Name { get; set; }
}
var savas = new Person { Id = "savas", Name = "Savas Parastatidis" }
await provider.CreateNode(savas)
This maps the Person
class to a User
node in Neo4j, with the Name
property stored as full_name
. It is now possible to retrieve the same node using a completely different class. In other words, there is no tight-coupling between the Person
class and the Neo4j representation’s of the graph node.
[Node(Label = "User")]
public class SomeOtherPersonClass : Node
{
[Property(Name = "full_name")]
public string FullName { get; set; }
}
var greekDude = await provider.GetNode<SomeOtherPersonClass>("savas")
There is so much more but this entry is a good start.
I am sure there are many issues with the project. Please feel free to send me feedback via email or submit an issue. Pull requests are also very much welcomed 🙂
Say hello to the Graph Model Domain Specific Language (GMDSL), created with the help of…
As I wrote in previous posts, the manual recording of memories for BrainExpanded is just…
Imagine a world where your memory is enhanced by a team of intelligent agents, working…
As part of the BrainExpanded project, I’m building an iOS app that lets users easily…
Artificial Intelligence (AI) has rapidly evolved over the past few decades, becoming an integral part…
Happy New Year everyone! I was planning for my next BrainExpanded post to be a…