Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added GraphModel #67

Merged
merged 2 commits into from Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Memstate.Core/Models/Graph/Edge.cs
@@ -0,0 +1,13 @@
namespace Memstate.Models.Graph
{
public partial class GraphModel
{
public class Edge : Item
{
public Edge(long id, string label) : base(id, label) { }

public Node From;
public Node To;
}
}
}
114 changes: 114 additions & 0 deletions src/Memstate.Core/Models/Graph/GraphModel.cs
@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Memstate.Models.Graph
{
public partial class GraphModel
{
/// <summary>
/// Unique id generator, shared by nodes and edges
/// </summary>
private long _lastId;

//Nodes and edges
private readonly SortedDictionary<long, Node> _nodesById;
private readonly SortedDictionary<long, Edge> _edgesById;

private SortedDictionary<string, SortedSet<Node>> _nodesByLabel;
private SortedDictionary<string, SortedSet<Edge>> _edgesByLabel;


public IEnumerable<Node> Nodes
{
get { return _nodesById.Values; }
}

public IEnumerable<Edge> Edges
{
get { return _edgesById.Values; }
}

public GraphModel()
{
var ignoreCase = StringComparer.OrdinalIgnoreCase;
_edgesById = new SortedDictionary<long, Edge>();
_edgesByLabel = new SortedDictionary<string, SortedSet<Edge>>(ignoreCase);
_nodesById = new SortedDictionary<long, Node>();
_nodesByLabel = new SortedDictionary<string, SortedSet<Node>>(ignoreCase);
}

public long CreateNode(string label)
{
var id = ++_lastId;
var node = new Node(id, label);
_nodesById[id] = node;
AddByLabel(_nodesByLabel, node, label);
return id;
}

public long CreateEdge(long fromId, long toId, string label)
{
Node from = NodeById(fromId);
Node to = NodeById(toId);
var id = ++_lastId;
var edge = new Edge(id, label) { From = from, To = to };
_edgesById[id] = edge;
AddByLabel(_edgesByLabel, edge, label);
from.Out.Add(edge);
to.In.Add(edge);
return id;
}

public void RemoveEdge(long id)
{
var edge = EdgeById(id);
_edgesById.Remove(id);
edge.From.Out.Remove(edge);
edge.To.In.Remove(edge);
_edgesByLabel[edge.Label].Remove(edge);
}

public void RemoveNode(long id)
{
var node = NodeById(id);
foreach (var edge in node.Out) RemoveEdge(edge.Id);
foreach (var edge in node.In) RemoveEdge(edge.Id);
_nodesById.Remove(id);
_nodesByLabel[node.Label].Remove(node);
}

public T Query<T>(Expression<Func<GraphModel, T>> query)
{
return query.Compile().Invoke(this);
}

private Node NodeById(long id)
{
return GetById(_nodesById, id);
}

private Edge EdgeById(long id)
{
return GetById(_edgesById, id);
}

private T GetById<T>(IDictionary<long, T> items, long id)
{
if (items.TryGetValue(id, out var item)) return item;
throw new ArgumentException("No such node: " + id);
}

private static void AddByLabel<T>(IDictionary<string, SortedSet<T>> index, T item, string label)
{
SortedSet<T> set;
if (!index.TryGetValue(label, out set))
{
set = new SortedSet<T>();
index[label] = set;
}
set.Add(item);
}

}
}
53 changes: 53 additions & 0 deletions src/Memstate.Core/Models/Graph/Item.cs
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;

namespace Memstate.Models.Graph
{
public partial class GraphModel
{
[Serializable]
public abstract class Item : IComparable<Item>
{
public readonly long Id;
public readonly string Label;
public readonly SortedDictionary<string, object> Props;

protected Item(long id, string label)
{
Id = id;
Label = label;
Props = new SortedDictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}

public object Get(string key)
{
object result;
Props.TryGetValue(key, out result);
return result;
}

public void Set(string key, object value)
{
Props[key] = value;
}

public int CompareTo(Item other)
{
return Math.Sign(Id - other.Id);
}

public override bool Equals(object obj)
{
return obj != null
&& obj.GetType() == GetType() //Edge == Node should always be false
&& ((Item)obj).Id == Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

}
}
15 changes: 15 additions & 0 deletions src/Memstate.Core/Models/Graph/Node.cs
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace Memstate.Models.Graph
{
public partial class GraphModel
{
public class Node : Item
{
public Node(long id, string label) : base(id, label) { }

public ISet<Edge> Out = new SortedSet<Edge>();
public ISet<Edge> In = new SortedSet<Edge>();
}
}
}
@@ -0,0 +1,26 @@
using Memstate.Models.Graph;
using System.Linq;
using static Memstate.Models.Graph.GraphModel;
Simcon marked this conversation as resolved.
Show resolved Hide resolved

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Commands
{
public class CreateEdge : Command<GraphModel, Edge>
{
public long From { get; private set; }
public long To { get; private set; }
public string Label { get; private set; }

public CreateEdge(long from, long to, string label)
{
From = from;
To = to;
Label = label;
}

public override Edge Execute(GraphModel model)
{
var edgeId = model.CreateEdge(From, To, Label);
return model.Edges.Single(e => e.Id == edgeId);
}
}
}
@@ -0,0 +1,22 @@
using Memstate.Models.Graph;
using System.Linq;
using static Memstate.Models.Graph.GraphModel;

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Commands
{
public class CreateNode : Command<GraphModel, Node>
{
public string Label { get; private set; }

public CreateNode(string label)
{
Label = label;
}

public override Node Execute(GraphModel model)
{
var nodeId = model.CreateNode(Label);
return model.Nodes.Single(n => n.Id == nodeId);
}
}
}
@@ -0,0 +1,27 @@
using Memstate.Models.Graph;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static Memstate.Models.Graph.GraphModel;

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Commands
{
public class IncrementLikes : Command<GraphModel, Node>
{
public long TweetId { get; private set; }

public IncrementLikes(long id)
{
TweetId = id;
}

public override Node Execute(GraphModel model)
{
var tweet = model.Nodes.SingleOrDefault(n => n.Id == TweetId);
object likes = tweet.Get("likes") ?? (long)0;
tweet.Set("likes", (long)likes + 1);
return tweet;
}
}
}
@@ -0,0 +1,14 @@
using Memstate.Models.Graph;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static Memstate.Models.Graph.GraphModel;

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Queries
{
public class GetEdges : Query<GraphModel, IEnumerable<Edge>>
{
public override IEnumerable<Edge> Execute(GraphModel db) => new List<Edge>(db.Edges);
}
}
@@ -0,0 +1,11 @@
using Memstate.Models.Graph;
using System.Collections.Generic;
using static Memstate.Models.Graph.GraphModel;

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Queries
{
public class GetNodes : Query<GraphModel, IEnumerable<Node>>
{
public override IEnumerable<Node> Execute(GraphModel db) => new List<Node>(db.Nodes);
}
}
@@ -0,0 +1,18 @@
using Memstate.Models.Graph;
using System.Collections.Generic;
using System.Linq;
using static Memstate.Models.Graph.GraphModel;

namespace Memstate.Docs.GettingStarted.QuickStartGraph.Queries
{
public class GetUsersWithTweets : Query<GraphModel, IEnumerable<Node>>
{
public override IEnumerable<Node> Execute(GraphModel db)
{
var users = db.Nodes.Where(n => n.Label == "user");
var tweetEdges = users.SelectMany(e => e.Out.Where(r => r.Label == "tweeted"));
var tweets = tweetEdges.Select(e => e.To);
return tweets;
}
}
}