using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace OrigoDB.Core.Types
{
[Serializable]
public class GraphModel : Model
{
///
/// Unique id generator, shared by nodes and edges
///
private long _lastId;
//Nodes and edges
private readonly SortedDictionary _nodesById;
private readonly SortedDictionary _edgesById;
[NonSerialized]
private SortedDictionary> _nodesByLabel;
[NonSerialized]
private SortedDictionary> _edgesByLabel;
public IEnumerable Nodes
{
get { return _nodesById.Values; }
}
public IEnumerable Edges
{
get { return _edgesById.Values; }
}
public GraphModel()
{
var ignoreCase = StringComparer.OrdinalIgnoreCase;
_edgesById = new SortedDictionary();
_edgesByLabel = new SortedDictionary>(ignoreCase);
_nodesById = new SortedDictionary();
_nodesByLabel = new SortedDictionary>(ignoreCase);
}
[Serializable]
public abstract class Item : IComparable-
{
public readonly long Id;
public readonly string Label;
public readonly SortedDictionary Props;
protected Item(long id, string label)
{
Id = id;
Label = label;
Props = new SortedDictionary(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();
}
}
[Command]
public long CreateNode(string label)
{
var id = ++_lastId;
var node = new Node(id, label);
_nodesById[id] = node;
AddByLabel(_nodesByLabel, node, label);
return id;
}
[Command]
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(Expression> 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(IDictionary items, long id)
{
T item;
if (items.TryGetValue(id, out item)) return item;
throw new CommandAbortedException("No such node: " + id);
}
private static void AddByLabel(IDictionary> index, T item, string label)
{
SortedSet set;
if (!index.TryGetValue(label, out set))
{
set = new SortedSet();
index[label] = set;
}
set.Add(item);
}
protected internal override void SnapshotRestored()
{
var ignoreCase = StringComparer.InvariantCultureIgnoreCase;
_edgesByLabel = new SortedDictionary>(ignoreCase);
_nodesByLabel = new SortedDictionary>(ignoreCase);
foreach(var node in _nodesById.Values) AddByLabel(_nodesByLabel, node, node.Label);
foreach(var edge in _edgesById.Values) AddByLabel(_edgesByLabel, edge, edge.Label);
}
[Serializable]
public class Node : Item
{
public Node(long id, string label) : base(id,label){}
internal ISet Out = new SortedSet();
internal ISet In = new SortedSet();
}
[Serializable]
public class Edge : Item
{
public Edge(long id, string label) : base(id,label){}
internal Node From;
internal Node To;
}
}
}