manual

The general overview gives a quick-start and how to use the common methods. This manual explains in full length the client and admin API, answers common questions, a detailed look at the security, highlights some graph analysis concepts and how to use the sample data API.

frequent questions

can I test it out?

You can download the HTML/JS client library on Github and freely use the public GraphDB. The repository contains various examples demostrating the API and also includes a diagram UI which allows you to create, edit, delete entities.

Diagram example

what is a graph database?

A graph database is storage system which in some way stores graphs (nodes, links and usually a lot of other related objects and properties). Within this broad definition there a many sub-categories and specialized types of graph databases. Strictly speaking, GraphDB is a graph database without native graph storage and it's also not an index-free adjacency system (it does rely on inner joins). GraphDB is however a schema-free NoSQL solution based on an underlying SQL repository. It's not a replacement for schema's or other NoSQL solutions, it's just one solution out of many with its pro and cons.

For more on graph database, see for example the 'Graph Databases' book by Ian Robinson, Emil Eifrem and Jim Weber

what is the typical use-case of GraphDB?

Whether GraphDB is right for you depends on many factors and a straight answer is not easily formulated. There are two main criteria; relationship structure and proliferation of schema.

When considering data storage you should distinguish between

GraphDB (and graph databases in general) fit in the tree and graph categories. If your data relationships are complex and graph-like you will likely benefit from graph storage rather than RDBMS storage.

The second criterium is related to how complex your set of data types is. For example, a typical CRM system uses a limited set of types: address, person, company and so on. When a new type of entity is necessary it means adding a new table (and related data access layer) to the system. This approach does not work well if your entity set is variable and/or indeterminate. A NoSQL (and thus graph databases) provide a way out in this situation; you do not need to specify any table structure in advance. GraphDB does not know about concrete entities, only about nodes and links containing XML blobs.

Concretely: if the following fits your (business) context:

then GraphDB is probably right for you.

what about Gremlin, SPARQL, Cypher...?

No, GraphDB does not support (currently) any of the NoSQL query languages. The GraphDB client API has simple search methods, fulltext search and graph oriented methods (traversals and graph algorithms). NoSQL query languages and LINQ are under consideration.

what's the difference between a Node and an Entity?

An Entity is the payload or data content of a Node. A Node is a generic Node object carrying an IDataEntity object. The Entity is XML serialized and stored as part of the Node in the database. In order to be able to do a minimum of processing (searching e.g.) on the nodes the Node has two obligatory properties; Id and Title. Additional (business related) content sits in the Entity. GraphDB ships with a set of predefined entities but you can easily add/define your own.

what's the difference between Link, Edge, Connection?

There is technically speaking no difference, the semantic difference resides in the context where the concept is used. For example, Link or Edge is usually used in a graph-theoretic context while Connection emphasizes the visual or semantic relation between objects. On an API and database level the actual object stored is a Link, but the method to connect two nodes is called "Connect".

Similarly, there is no real difference between Node, Vertex and Item.

GraphDB API

GraphDB API modules

What is 'me'?

In the code samples below the 'me' object refers to the current security context of a logged in user, that is

var me = DataManager.Login('You', 'Password');

or

var me = DataManager.Login('You', 'Password', token);

for a non-standard installation. See also the security guide below.

What is a 'store'?

The documentation refers to the store; this is equivalent to a GraphDB instance.

Predefined Entities

The GraphDB client libraries contain a set of predefined, common entities. You can easily extend the API with your custom entities, in the serialization guide below you can read about how custom entities are created for and added to GraphDB.

Thought

A generic entity which consists only of a Title and a Description.

var thought = new Thought("Shopping list", "Pound of sugar, fresh milk and eggs.");
Bag

An entity with a name-value collection. The entity can be used to set an arbitrary collection of values and is also used on importing JSON entities.

var bag = new Bag("My bag", "Just a Bag entity",
            new Dictionary<string, string>{
                { "Pi", "3.141592" },
                { "Euler", "2.7182" },
                { "QED", "1/137" },
            });
var json = bag.ToJson();
Person

An entity describing a person. See also the contact entity.

var john = new Person { FirstName = "John", LastName = "Field" };
var robert = new Person { FirstName = "Robert", LastName = "Nash" };
D.AddEntities(me, john, robert);
D.Connect(me, john.Id, robert.Id);
Address

An entity describing an address.

var address = new GraphDB.Entities.Address{
                    Title = "Oxydo HQ",
                    AddressLine1 = "Old Bridge Road 23b",
                    AddressLine2 = "Midsummer",
                    Zip = "4501-3"
                    Country = "United Kingdom", 
                    }
Company

An entity describing a company.

var company = new GraphDB.Entities.Company{
                            CompanyName = "Fruchtermann AD",
                            Description = "Cakes and Sweets"
                            }
Contact

A person or contact in the context of an order, service or product.

var c = new GraphDB.Entities.Contact
           {
            Address = "Greenfields 141",
            City = "Cork",
            CompanyName = "Maximum Magnets",
            ContactName = "Peter Roscoff",
            ContactTitle = "Dr.",
            ContactType = "Standard",
            Country = "Ireland",
            Extension = "",
            Fax = "",
            HomePage = "http://www.maxmagnet.ir",
            Phone = "+45-253-533-90800",
            PhotoPath = "//images//Proscoff.png",
            PostalCode = "34-533",
            Region = "Cork County",
            Title = "Dr. Peter Roscoff",
            Description = string.Format("{0} from {1}", contact.ContactName, contact.CompanyName)
            };
Order

A request to be made, supplied or served.

var order = new GraphDB.Entities.Order
                {
                 Title = "55 Alu-Ti Tubes",
                 Description = "Urgent, before 12/3/2014",
                 ShipAddress = "Greenlane 3",
                 ShipCity = "Cork",
                 ShipCountry = "Ireland",
                 ShipName = "BlueLine",
                 ShipPostalCode = "4500-3",
                 ShipRegion = "Cork County",
                };
Employee

A person employed at a company.

var e = new GraphDB.Entities.Employee
            {
             Address = "St-Andrew lane 34",
             City = "Little Wallop",
             Country = "United Kingdom",
             Extension = "12",
             PostalCode = "23001",
             Region = "Sussex",
             Title = "John Field",
             LastName = "Field",
             FirstName = "John Richard",
             EmployeeTitle = "CTO",
             HomePhone = "+44-234-533-09834",
             Notes = ""
             };
Customer

A customer in the context of a CRM system, ERP system or order. See also the person and contact entities.

var customer = new GraphDB.Entities.Customer
{
    Address = "Abbey Road 12",
    City = "Manchester",
    Country = "United Kingdom",
    PostalCode = "D-47852",
    Region = "Greater Manchester",
    Title = "John Field",
    Description = string.Format("{0} from {1}", customer.ContactName, customer.CompanyName),
    CompanyName = "Pleyel Ltd.",
    ContactName = "John Field",
    ContactTitle = "Sir",
    Fax = "None",
    Phone = "+44-478-453900"
};
EmailAddress

An Email address.

var email = new GraphDB.Entities.EmailAddress
{
    Id = Guid.NewGuid(),
    Description = "Sales contact point",
    Title = "Pleyel Sales",
    Address = "sales@PleyelInc.net"
};
Product

A generic product entity.

var e = new GraphDB.Entities.Product
        {
            Title = "Mandibula AZ-23",
            Description = string.Format("Product {0}", product.ProductName),
            ProductName = "Mandibula v2",
            Discontinued = False,
            QuantityPerUnit = 2,
        };

GraphDB Client API

The client API allows you to manage data, edit and create workspace, collaborate and search across the store. It does not allow you to instantiate a store or the manage the users within an instance. Of course, if you reference in your solution both the client and admin assemblies you can access the full breadth of the GraphDB API.

Note that the sample data is a separate assembly and depends on the client assemblies. It's separate from the main API because it contains a large amount of static resources which are not necessary if you ship the assemblies with your application. The sample data is only at your

entities

FindNode

Searches for a node on the basis of its Title and, optionally, its Type. You can use a '*' or '%' wildcard in the Title search and keeping the Type null will consider all data types.

Searching for anything which starts with 'John':

DataManager.FindNode('John*', null, me);

while constraining this search to the Person type would be

DataManager.FindNode('John*', 'Person', me);

Anything (within your security scope) containing the string 'abc' would be

DataManager.FindNode("%abc%", null, me);

where the '%' can also be replaced with '*'.

TakeRandomEntity

Takes a random entity within the current active workspace.

DataManager.TakeRandomEntity(me);
UpdateEntity

This method either updates or adds the given entity to the current workspace.

var thought = new Thought("Polarized marketing", "The cumulative, mass-driven advantage.");
DataManager.UpdateEntity(thought, me);

thought.Title = "Critical M-dimensions";
DataManager.UpdateEntity(thought, me);
AddAssemblies

Scans the entities in the given assemblies and add them to the serialization cache. See also the custom serialization guidelines

DataManager.AddAssemblies(typeof(Scanner).Assembly);
AddType

Adds the given type to the known serialization entities (serialization cache).

DataManager.AddType(typeof(SalesReport));
AddValue

Value nodes don't have a DataType and can store any (string) value. This method adds a value to the store.

var id = DataManager.AddValue("3.141592", me);
GetValue

Gets an existing value from the store.

var id = DataManager.AddValue("3.141592", me);
var value = DataManager.GetValue(id, me);
UpdateValue

Updates an existing value.

var id = DataManager.AddValue("3.141592", me);
DataManager.UpdateValue(id, "2.7182", me);
var value = DataManager.GetValue(id, me);
UpdateEntities

Similar to UpdateEntity but accepts a series of entities rather than just a single one.

var john = new Person("John", "Conway");
var pixar = new Company("Pixar");
DataManager.UpdateEntities(me, john, pixar);
AddEntities

An alias for UpdateEntities.

AddEntity

An alias for UpdateEntity .

AddNode

Adds the given Node to the store.

You can instantiate a Node and define its properties but be careful to keep it consistent. For example, the payload can be set to an arbitrary bit of XML which, if not matching the DataType specification, lead to errors in the deserialization.

var node = new Node{Id= Guid.NewId(), Title= "My stuff"};
DataManager.AddNode(node, me);
MoveEntity

Moves an entity into another workspace. Note that this can induce security changes for people who have share access. For instance, moving an entity from a shared workspace to a non-shared one will prohibit other users to access the moved entity.

var address = new Address("Tottenham Court Road 121", "London", "UK");
DataManager.UpdateEntity(address, me);
var newSpace = DataManager.AddWorkspace("Aside", me);
DataManager.MoveEntity(address, newSpace.Id, me);
GetNode

Returns a raw Node (not an entity) with the specified identifier.

var node = DataManager.GetNode(id, me);
if(!string.IsNullOrEmpty(node.DataType)) 
{
    node.DataEntity = node.Xob.Deserialize();
}
GetEntity

Returns the entity (an IDataEntity instance) with the specified identifier. If the underlying Node is a value node, null is returned.

var entity = DataManager.GetEntity(id, me);
if(entity is Person)
{
    var person = entity as Person;
}
else
{
    Console.WriteLine("The entity is a " + entity.DataType);
}
GetEntityWorkspaceName

Gets the name of the workspace with the specified identifier.

var space = DataManager.GetEntityWorkspaceName(id, me);
Console.WriteLine("The entity sits in the '" + space + "' workspace.");
EntityExists

Returns whether the workspace with he specified identifier exists.

var exists = DataManager.EntityExists(id, me);
Console.WriteLine("The entity " + exists? "exists.": "does not exist." );
DeleteEntity

Removes the entity/node with the specified identifier from the store. All connections attached to the entity are deleted and all statistics are removed as part of the deletion.

DataManager.DeleteEntity(id, me);
DeleteNode

Alias for DeleteEntity. Not that not every Node is an Entity; a value-node or tagging node does not carry an entity.

DataManager.DeleteNode(id, me);
DeleteValue

Deletes the value-node with the specified identifier.

DataManager.DeleteValue(id, me);
UpdateConnection

Updates or adds the given Link to the store.

var link = DataManager.GetConnections(fromId, toId, me).Single(c => c.Color == 3);
link.Title = "Shipping via A'dam";
DataManager.UpdateConnection(link, me);
UpdateConnections

Analog to the UpdateConnection method but this one allows a series of connections.

DeleteAllDataInWorkspace

Deletes all nodes within a workspace. You need to be the owner of the workspace to perform this action, other shares/roles will trigger an exception when calling this method.

IsInCurrentWorkspace

Returns whether the given entity sits in the current active workspace.

security

User authentication is configured using the Admin API and from the login/client point of view this is a transparent setting; whether the store is set up for internal or external authentication does have any side-effect on the client API. If the authentication is external you do need a configuration file however, which is part of the standard setup of the ASP.Net Membership mechanism.

A typical web.config or app.config would look like the following;

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
    <clear/>
    <add name="MembershipDB"
      connectionString="Some Bogus String here that gets overrided in the custom class"
      providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
    <authentication mode="Forms"/>
    <authorization>
        <deny users="?"/>
    </authorization>
    <membership>
        <providers>
            <remove name="AspNetSqlMembershipProvider"/>
            <add name="AspNetSqlMembershipProvider"
              connectionStringName="MembershipDB"
              applicationName="GraphDB"
              type="GraphDB.Shared.GraphDBMembershipProvider, GraphDB.Shared"
              requiresUniqueEmail="false"
              requiresQuestionAndAnswer="false"/>
        </providers>
    </membership>
</system.web>
<startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>

It's important to note that the standard provider assembly is replaced by a GraphDB implementation here. The reason for this is purely related to security. A user could configure an arbitrary local membership authentication store, gain access by authenticating on this instance and proceed to the store. This security issue is forbidden by this GraphDB implementation which enforces the user to authenticate on an instance defined during the setup of a GraphDB store. It is, hence, the admin (i.e. the person who sets up a GraphDB store and defines the authentication mode) who defines how and where users get authenticated.

A GraphDB instance with internal authentication does obviously not require any configuration file related to the Membership Provider.

Whether you should use internal or external authentication really depends on your actual (business) context and has not influence on the internals of GraphDB. Future versions of GraphDB will have additional authentication channels in order to ease the integration scenarios.

Using the client API you can consume multiple GraphDB instances at the same time and within the same application domain. You can acquire multiple user security contexts (the 'me' used in this documentation) and pass it to any API method. Alternatively, you can have multiple user accounts within a single GraphDB instance if needed.

The GraphDB API defines both a static and a standard (instance) DataManager. This means that you can either keep an instance throughout your application session;

var token = InstanceToken.Default; // or custom token
var dataManager = new DataManager(token);

dataManager.AddEntity(new Person());

without having to specify any security context, or you can use the static methods with a specified context

var me = DataManager.Login("Allen","A-Password");
DataManager.AddEntity(new Person(), me);

User collaboration is unrelated to the authentication described above and works with all authentication setups. Collaboration, in the context of GraphDB, is about sharing 'knowledge' by granting access to your workspaces (with a certain role) to other users.

Granting access can be done on a per-user basis or on a workspace level. For example, you can declare one of your workspaces as public

 DataManager.SetPublicWorkspace(mySpaceId, true, me);

and it will allow any user to access (read, search, create links to) the entities defined in that workspace. Switching it off is just one line as well;

DataManager.SetPublicWorkspace(mySpaceId, false, me);

Alternatively, you can grant read access to user with identifier 'userId' to your workspace using

DataManager.GrantAccess(userId, mySpaceId, WorkspaceAccessRole.Member);

GraphDB defines only three roles:

The collarboration in GraphDB has, hence, three dimensions:

The combinations and the resulting permission are summarized in the table below.

Security overview.

Most of the results are straightforward but do mind the singular cases. The client API has various method which allow you to query your own permission on a workspace, see the sharing state of a workspace, query the roles of a user, see the access you have on a specific entity and more.

Workspace level shares are set using specific methods (discusses below) and can be one or combinations of the following:

The correspond methods are SetWorkspacePublic, SetWorkspaceInternal and SetWorkspaceReadOnly. The boolean parameter switches the state on and off.

GetAllUsers

Returns all the users in the store.

var john = DataManager.GetAllUsers(me).SingleOrDefault(u => u.FriendlyName=="John");
IsSystemAdmin

Returns whether the specified identifier is the system administrator, i.e. the user who installed the store and manages the users.

GrantAccess

Grants access to one of your workspaces with a role, see above for more details.

RevokeAccess

Revokes access to a user. This method has no effect if the user has not been granted access before.

DataManager.RevokeAccess(mySpaceId, userId, me);
HasWorkspaceAccess

Returns whether the given user has access to the current active workspace.

var currentWorkspaceId = me.CurrentWorkspaceId;
var workspace = DataManager.GetWorkspace(currenWorkspceId, me);

var access = DataManager.HasWorkspaceAccess(someUserId, me);
Console.WriteLine("User '" + someUserId + "' has " + (access? "access.":"no access.");
GetWorkspaceRole

Returns the role a certain user has on one of your workspace. If you attempt to fetch the role of a user on a space you don't own an exception will be thrown.

var john = DataManager.GetAllUsers(me).SingleOrDefault(u => u.FriendlyName=="John");
var currentWorkspaceId = me.CurrentWorkspaceId;
// the role of John in your current workspace
var role = DataManager.GetWorkspaceRole(john.UserId, currentWorkspaceId, me);
IsWorkspaceAdmin

Returns whether the specified user is an owner (admin) of the workspace. Note that this is not the same as the IsSystemAdmin method which relates to the user who installed the store, though the could eventually be the same.

GetEntityPermissions

Returns an overview of the permissions you have on a specified entity. The overview consists of:

IsInScope

Whether an Entity is accessible under your user context depends on various parameters; this method returns whether the given entity is accessible to you.

GetWorkspacePermissions

Gets the permissions you have on the given workspace under the current (security user context.

GetWorkspaceAccessOverview

Returns an overview of the shares and the users with their role for the current workspace.

ChangePassword

Changes your password. If the store has an external authentication set it will perform the change through the ASP.Net Memnership API.

workspace

A workspace is both a semantic boundary and a collaboration unit. You can create workspaces to collect nodes belonging to the same semantic context but workspaces are also the object which can be shared with other users. Yet another way to organize nodes is by using tags which do not belong to one spaces but can span all your workspaces. You can add as many workspaces as you wish and share them in in various ways, the only constraint is the

AddWorkspace

Adds a new workspace to the store. By adding a new workspace it does not automatically becomes the current or active workspace, you need to set it explicitly through the SetActiveWorkspace method.

Note that the name of a workspace doesn't have to be unique, rather the identifier uniquely identifies the space. To fetch the workspace (and change through this its properties) you can use the GetWorkspace or GetWorkspaces methods.

var spaceId = DataManager.AddWorkspace("Accounting and billing workspace", me);
var workspace = DataManager.GetWorkspace(spaceId, me);
GetWorkspaces

Returns the list of workspaces you own (have created) as well as the ones that have been shared with you. Use the GetWorkspacesInfo method to return a similar result but with role information in addition.

var allMySpaces = DataManager.GetWorkspaces(me);
SetActiveWorkspace

Sets the active or current workspace. At any moment you can have only one active workspace and you cannot delete all your workspace since one has to be active. When a new user is created a default (and current) workspace is created. A user can have an unlimited amount of workspaces.

The store also has some system workspaces which are not accessible to any users. In addition, each user also has a (unaccessible) system workspaces where e.g. tags and favorites (pointers) are stored.

DeleteWorkspace

Removes the specified workspace. Note that this will delete all nodes and links attached to the workspace. In addition it will disable all shares and thus impacts the users who have a role in the workspace.

WorkspaceExists

Returns whether the workspace with the given identifier exists. The method will return true whatever your role or ownership is, i.e. the value returned does not imply anything about the access you have to the workspace.

UpdateWorkspace

Update the properties of the specified workspace. Note that the complete list of workspaces contains also the shared spaces. While you can alter the properties of the object you need to be an owner of the space to be able to update it (i.e. commit the changes).

var mySpace = DataManager.GetWorkspaces().First();
mySpace.Name = "The first space";
// you need to be an admin of the space or an exception will be thrown
DataManager.UpdateWorkspace(mySpace); 
SetWorkspacePublic

By making a workspace public you allow every user to have access (search, read) to the nodes defined within it. You remain the owner of the workspace and other users do not have a specific role assigned.

SetWorkspaceInternal

By making a workspace internal you restrict the links to be only between nodes in the workspace. This setting does not affect sharing or access.

SetWorkspaceReadOnly

By making a workspace read-only you turn it into an immutable set of nodes and links. Even if you own the workspace you'll need to switch off the read-only mode to alter something. This setting does not affect sharing or access.

GetWorkspace

Returns the workspace with the specified identifier. See also the #getworkspacesinfo for a complete list of workspace and additional information.

GetWorkspacesInfo

Returns all your workspace (including the shared ones), your roles and some basic workspace statistics.

var allMySpaces = DataManager.GetWorkspacesInfo(me);

foreach(var space in allMySpaces)
{
    Console.WriteLine(string.Format("Space '{0}' with role '{1}'; {2} links and {3} nodes."), 
                        space.Workspace.Name, 
                        space.Role,
                        space.WorkspaceStats.LinkCount,
                        space.WorkspaceStats.NodeCount);

}
IsWorkspacePublic

Returns whether the workspace with the specified identifier is public.

IsWorkspaceReadOnly

A read-only workspace does not allow changes on any level (nodes, properties, links). It does not affect the sharing or access.

IsWorkspaceInternal

An internal workspace does not allow links going outside the workspace. In graph terminology this is an isolated component siting within the workspace. It does not affect the sharing or access.

GetWorkspaceInfo

Returns an overview of various things about the workspace.

GetCurrentWorkspace

Gets the current or active workspace.

GetAllNodesInWorkspace

Gets an enumerable collection of the nodes in the current workspace.

GetAllLinksInWorkspace

Gets an enumerable collection of the links in the current workspace.

graphs

The API defines a rich set of graph related methods which are executed on the server, i.e. they are effectively stored procedures. You should be careful to distinguish between analog methods defined on the Graph structure which are executed on the client. For example, a depth-first traversal can be performed both server-side and client-side.

Graphs in GraphDB are directed, i.e. a link has a direction and the 'from' entity is called 'source' while the 'to' entity is called 'target'.

Graphs can be cyclic, i.e. can have loops. The server-side graph algorithms deal with this and will not overflow. The side-effect is however that because graphs can also be multi-edged (multiple links between nodes) they are consolidated as one in the algorithms. For example, whether you have one or hundred links from one to another entity is equivalent in the topological sorting and traversals.

Wherever possible the graph-related methods do not return so-called hydrated results; only lists of nodes or identifiers are returned. Some methods have overloads or options to return hydrated sets, i.e. the entities are deserialized as part of the process. The reason that entities are not automatically deserialized is to increase performance, keep the memory low and also assumes that the if you fetch a huge graph you don't necessarily want all entities.

By convention, a parent of a node is a node whenever there is at least one connection with the parent as source and the target is the child of the parent. A child of a node is another node with at least one connection flowing downstream to the child.

The graph traversals easily process thousands of nodes (100K nodes ~ 2s) but no security checks or deserializations are performed. This means that you can fetch a large graph or spanning tree with ease but that not all identifiers are necessarily within your security scope.

BiConnect

Creates a bidirectional link between the specified entities.

var order = new Order{Title = "Order BG64-GT7", Quantity = 12};
var customer = new Customer{ContactName = "Ken F. Price"};
DataManager.BiConnect(order.Id, customer.Id, me);
Connect

Creates a link between the given entities.

var peter = new Person{FirstName = "Peter", LastName = "Sidorov"};
var address = new Address{Title = "State Tretyakov Gallery", 
                            AddressLine1 = " 10, Lavrunshkensky Pereulok",
                            Zip = "119017",
                            City = "Moscow",
                            Country = "Russia" 
                         };
var connectionId = DataManager.Connect(peter.Id, address.Id, Title: "Address", Weight: 150);
GetConnections

Returns the connections between the given entities. You can in addition filter them out on the basis of various connection properties (weight, color, title...).

GetConnection

Returns the link with the specified identity.

Disconnect

Removes all connections between the given entities.

FullTextSearch

Performs a full text search on the entities/nodes. This methods directly uses the underlying SQL Server full-text search engine and its integration with XML.

GetRelated

Returns the parents and children of the specified entity. This is equivalent to the neighbor nodes which are directly connected (as a source or as a target) to the given entity.

This method is equivalent to the GetNeighbors method.

SimpleSearchWithResolution

Similar to SimpleSearch but also deserializes the entities returned.

SimpleSearch

The simple search searches at the Title of the node and the DataType only. Wildcards '%' and '*' can be used for the Title (not the data type) and a 'null' DataType corresponds to 'all data types'.

Searching for any entity with a title starting with 'Ford':

var results = DataManager.SimpleSearch("Ford*", null, me);

Searching all Person entities ending with 's':

 var results = DataManager.SimpleSearch("*s", "Person", me);

Attempting to search for everything will result in an exception:

var results = DataManager.SimpleSearch("*", null, me); // throws exception

The search only looks at the entities in within your scope, only accessible entities are returned. The permissions/constraints related to sharing are implied.

GetChildren

Returns the children of the given entity. A node is a child if there is a link flowing towards it.

BFT

Performs a breadth-first traversal of the graph within the workspace and starting at the specified node identifier.

DFT

Performs a depth-first traversal of the graph within the workspace and starting at the specified node identifier.

GetParents

Gets the parents of the given node.

GetNeighbors

Gets the collection of parents and children of the given node.

AreConnected

Returns whether the specified nodes are connected, i.e. there at least one connection between the nodes, independently of the direction.

UpdateGraph

Updates the nodes and links which define the graph. Note that the graph itself is not saved in the store, i.e. the subset of nodes and links are not stored.

import

The native format for everything serialization is XML because SQL Server has an optimized XML data type and API (contrary to the JSON format). You can however import data into GraphDB defined in other formats.

ImportJsonGraph

Imports a graph defined in JSON format into the store. The JSON can be structured in two different ways:

The following example will import the JSON defined in tree-format in the current workspace. Since there is no data specification the supplied JSON entries will be mapped to Bag entities, which accepts a dictionary of name-value pairs.

(double quotes only necessary if inside a C# string, you can omit if in a text file)

var json = @"
            {
                ""title"":""Customer Service"",
                ""children"":[
                    {
                        ""title"":""Billing"",
                        ""children"":[
                        {
                            ""title"":""Ellen Finn""
                        },
                        {
                            ""title"":""Tannya Andoula""
                        }
                        ]
                    },
                    {
                        ""title"":""New division""
                    }
                ]
            }
        ";
var specs = new JsonGraphSpecs
            {
                Format = JsonFormat.Tree
            };
DataManager.ImportJsonGraph(json, ctx, specs);

If you define nodes and links as properties you don't need to specify the format since it's considered the default;

(double quotes only necessary if inside a C# string, you can omit if in a text file)

const string json = @" 
        {
        ""nodes"":[
            {
                ""title"":""Australia"",
                ""id"":""20aeb159-3998-4f67-84f0-2d41ca750696""
            },
            {
                ""title"":""Portugal"",
                ""id"":""b08797f8-a2e1-4bd1-9f4f-47789407fc62""
            },
            {
                ""title"":""Canada"",
                ""id"":""2ce28317-4b4e-4a3e-88d4-4aa151803e98""
            },
            {
                ""title"":""Nigeria"",
                ""id"":""086c8c9f-b66b-4f1c-b8ac-f060df2ae89a""
            },
            {
                ""title"":""Bulgaria"",
                ""id"":""1cd18b53-02b6-475e-afba-9350293e78fd""
            }
        ],
        ""links"":[
            {
                ""from"":""20aeb159-3998-4f67-84f0-2d41ca750696"",
                ""to"":""b08797f8-a2e1-4bd1-9f4f-47789407fc62""
            },
            {
                ""from"":""20aeb159-3998-4f67-84f0-2d41ca750696"",
                ""to"":""2ce28317-4b4e-4a3e-88d4-4aa151803e98""
            },
            {
                ""from"":""2ce28317-4b4e-4a3e-88d4-4aa151803e98"",
                ""to"":""086c8c9f-b66b-4f1c-b8ac-f060df2ae89a""
            },
            {
                ""from"":""2ce28317-4b4e-4a3e-88d4-4aa151803e98"",
                ""to"":""1cd18b53-02b6-475e-afba-9350293e78fd""
            }
        ]
        }
        ";

DataManager.ImportJsonGraph(json, ctx);

The client API uses JSON.Net to (de)serialize to JSON and many extensions or customizations are possible through this.

ImportShortFormat

The short format is an easy way to define (large) graphs with a minimum of input; you only specify the titles of the nodes and how they are linked. The drawback of this format is that you cannot define additional properties or isolated nodes.

The following imports the graph defined as short format in the current workspace

var shortFormat = "Spain -> Belgium, Tunesia -> South-Africa, Tunesia -> France, Belgium -> Bulgaria";
DataManager.ImportShortFormat(shortFormat, me);

You can also convert the short format to a graph and then use the UpdateGraph method:

var graph = GraphDB.Shared.Serialization.FromShortFormat("A->B, B->C, Anna->Lucas, A->Lucas");
DataManager.UpdateGraph(graph, me);
ImportJsonWorkspace

A workspace can be exported in JSON format which is basically as graph together with some metadata about the workspace. This method will import a workspace in JSON format.

workspace and graph export

Graphs can be fetched from the store, manipulated on the client and finally committed back to the store using the UpdateGraph method. You can also export a Graph object to JSON and XML in order to process is further in your application or to send it over a service (WCF, Web API, WebService...). A complete workspace can also be exported in JSON or XML and re-imported. This also offers the possibility to define your whole workspace and batch add it to the store.

Export

This export method exports the given graph object to XML or JSON (the default option). A graph can be created manually client-side or fetched from the store through one of the many graph-oriented methods.

var graph = GraphDB.Shared.Serialization.FromShortFormat("Jane->Francis, Francis->Mortimer, Anna->Jane, Myriam->Anna");
var json = DataManager.Export(graph, ExportFormat.JSON);
var xml = DataManager.Export(graph, ExportFormat.XML);

Alternatively, you can use the extension

var json = graph.ToJson();
ExportWorkspace

Exporting a workspace is is effectively the same as a Graph export with some metadata about the workspace added. When you import an existing export you have the option to recreate the workspace with the exported name or to import the data in the current workspace.

To export the current (active) workspace to JSON:

var currentId = me.CurrentWorkspaceId;
DataManager.ExportWorkspace(currentId, me);

To export the workspace to XML you only need to specify the format in addition:

var currentId = me.CurrentWorkspaceId;
DataManager.ExportWorkspace(currentId, me, ExportFormat.XML);

If you wish to import the workspace in another store you can do so by using the ImportJSONWorkspace method. The XML format is not a supported import format however.

tags and favorites

Tags (aka labels) are a way of organizing nodes into semantically related groups across workspaces. You can look at workspaces as chapters in a textbook while the tags are the index. Some tags are predefined and cannot be renamed or removed, they are also automatically associated on the basis of various criteria. For example, the 'People' tag is automatically associated with Person entities.

A special type of tag is the Favorites tag and a series of API methods have been specifically defined to make it easy to manage favorites.

Of course, you can define custom tags. Neither the nodes attached to the tags nor the tags themselves are visible to other users even if the space is shared. The tags reside in a system workspace (one per user) which cannot be accessed through the API like other workspaces.

AddToFavorites

Adds the specified entity to the favorites.

ChangeTagName

Changes the name of the custom tag.

RemoveFromFavorites

Removes the specified entity from the favorites.

GetTagItemsWithoutResolution

Gets the nodes tagged by the specified tag but without deserializing the entities£.

GetTagEntities
GetTagNodes
GetTasks

Gets the Task entities (tagged with the 'Tasks' tag).

GetTags

Gets all the tags of the current user.

GetTagName

Gets the name of the tag with the specified identifier.

GetPeople

Get the Person entities (tagged with the 'Person' tag).

GetItemTags

Gets all the tags defined/attached to the specified entity.

GetAllTags

Gets all the tag of the current user.

AddToTag

Adds the given entity to the specified tag.

AddTag

Adds a custom tag with the specified name. Note that tag names are not case-sensitive and that predefined tag names cannot be reused.

TagExists

Returns whether the tag with the given name exists.

DeleteTag

Deletes the specified tag. The predefined tags (People, Favorites...) cannot be deleted or renamed.

RemoveFromTag

Removes the specified entity from the tag.

AddToFavorites

Adds the given entity to the favorites collection.

GetFavoritesWithResolution

Gets the favorite nodes with deserialization of the entities.

GetFavorites

Gets the favorites nodes (without deserialization of the entities).

Admin API

Sample Data

The sample data sits in an optional assembly 'GraphDB.Samples' and contains both concrete data sets and network generators.

The install methods allow you to specify an optional workspace name in which the data will be added. If omitted, the data is added in the current workspace.

Worldcup 1988

Small network of 35 nodes and 118 links. Describe the 22 soccer teams which participated in the World Cup in Paris, 1998. The adjacency structure can be seen in the picture below.

The third parameter in the method is an optional name for a new Workspace where the data will be put. If omitted the data will be added in the current workspace.

System.Threading.Tasks.Task.WaitAll(GraphDB.Samples.Install(GraphDB.SampleData.Worldcup1988, me, "Worldcup"));

With the await keyword and in the current space this would be:

await GraphDB.Samples.Install(GraphDB.SampleData.Worldcup1988, me)

Worldcup 1988 sample network.

Northwind

The Northwind database is a well-known SQL Server database which is about a company named "Northwind Traders" and captures all the sales transactions that occurs between this company and its customers, shipper, suppliers and more. The database itself and more info can be found on Azure and CodePlex. This database is replicated in this sample as a graph with the proper entities replacing the corresponding table records. In this perspective, an Order record with a foreign-key to a Supplier corresponds to an Order entity with a connection (i.e. GraphDB Link) to a Supplier entity.

await GraphDB.Samples.Install(GraphDB.SampleData.NorthWind, me, "Northwind_Import");

Random network

This creates a random network with around 100K Thought entities and around 250K connections. There is no guarantee that the network will be connected or (much less) tree-like. The algorithm does however restrict the degree of the nodes to four, i.e. nodes do not have more than four connections attached. This creates a medium large workspace where you can experiment with the graph algorithms (traversal, shortest paths and so on).

The Thought entities have Title 'Item *' and the links have Title 'Random', which makes it easier to delete the nodes if merged into a workspace with other nodes.

await GraphDB.Samples.Install(GraphDB.SampleData.RandomNetwork, me, "Large network");

Tree network (aka hierarchy)

This creates a large tree or around 5000 nodes and the same amount of links.

The Thought entities have Title 'Tree Item *' and the links have Title 'Random', which makes it easier to delete the nodes if merged into a workspace with other nodes.

await GraphDB.Samples.Install(GraphDB.SampleData.RandomTree, me, "Large tree");

Serialization and custom entities

Custom entities are just ordinary classes which implement the IDataEntity interface:

/// <summary>
/// Describes an entity which represent some unit of information of a particular type (person, document etc.).
/// In more generic terms this represents anything which can be stored as a <see cref="Node"/> in the GraphDB database.
/// </summary>
/// <remarks>This is stored in the Node table.</remarks>
public interface IDataEntity : ICloneable
{
    /// <summary>
    /// Gets or sets the title of the entity.
    /// </summary>
    /// <value>The title.</value>
    string Title { get; set; }

    /// <summary>
    /// Gets or sets the description of the entity.
    /// </summary>
    /// <value>The description.</value>
    string Description { get; set; }

    /// <summary>
    /// Gets or sets the unique identifier of the entity.
    /// </summary>
    /// <value>The Id.</value>
    Guid Id { get; set; }
}

which is the minimum you need to store an object in the GraphDB store. The serialization is done automatically using .Net's WCF data serializer. You do have, however, plenty of options to customize how things are store and serialized:

The most basic example of the IDataEntity implementation is the following:

public class MostBasicEntity : IDataEntity
{     
    public string Title { get; set; }
    public string Description { get; set; }
    public Guid Id { get; set; }
    public object Clone()
    {
        return new MostBasicEntity
        {
            Id = Guid.NewGuid(),
            Title = this.Title,
            Description = this.Description
        };
    }
}

Internally GraphDB will ensure that if no Id is set when storing it in SQL Server one is created on the fly. The entity above can be added to the store by simply adding the type:

DataManager.AddType(typeof(MostBasicEntity));

and from this point on you can use it any way you want; linking, storing, graphing and so on.

The XML namespace when serializing will correspond to the namespace of the C# entity. You alter this by adding a DataContract attribute on the class:

[DataContract(Namespace = "Company.MyStuff")]
public class MostBasicEntity : IDataEntity
{...}

as well as changing the name of the XML element:

[DataContract(Namespace = "Company.MyStuff", Name = "MySpecialEntity")]
public class MostBasicEntity : IDataEntity
{...}

All of this works as well with nested classes and GraphDB will take care of instantiating types as needed.

If you wish to have more control over how entities are (de)serialized you can implement the IXmlSerializable interface:

public interface IXmlSerializable
{
    XElement Serialize(XElement root);  
    void Deserialize(XElement data);
}

For example;

public class MyTruck : IDataEntity, IXmlSerializable
{
    public string Title { get; set; }   
    public string Description { get; set; } 
    public Guid Id { get; set; }

    public XElement Serialize(XElement root)
    {
        root.Add(new XAttribute("Title", this.Title), new XAttribute("Description", this.Description), new XAttribute("Id", this.Id.ToString()));
        return root;
    }

    public void Deserialize(XElement data)
    {
        if (data.Attribute("Id") != null) this.Id = Guid.Parse(data.Attribute("Id").Value);
        if (data.Attribute("Title") != null) this.Title = data.Attribute("Title").Value;
        if (data.Attribute("Description") != null) this.Description = data.Attribute("Description").Value;
    }
    public object Clone()
    {
        return new MyTruck
        {
            Id = Guid.NewGuid(),
            Title = this.Title,
            Description = this.Description
        };
    }
}

GraphDB will bypass the default serialization and fully rely on the interface in this case. Note that whatever your customization is, you need to add the type or the assembly in which the type resides to the know GraphDB types.

Web API

The web API is an (ASP.Net MVC) RESTful service which wraps around the client API and serves a wide variety of form factors; browsers, mobile devices, JavaScript, XAML and so on. It does not replace the client or admin API but allows one to articulate some types of solutions. The signatures of the methods is in most cases identical to the underlying .Net API.

To access the web API one needs an API Key which one can get by logging into the site. The authentication can be configured to allow the usual social networks (Facebook, Twitter), integration with a backend system (ActiveDirectory, Azure AD) or a local database.

A typical use of the API key in a JavaScript setting (jQuery) looks like the following.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Get Entity Example</title>
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

    <script>
        $.ajax({
            url: "https://api.company.com/GetEntity/8a066845-a533-457d-b384-a2788a68ac33",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("ApiKey", "YPeXQkIa4N");
            },
            xhrFields: {
                withCredentials: true
            },
            type:"GET",
            error: function(result) {
                $("#result").html(result);
            },
            statusCode: {
                200: function (result) {
                    $("#result").html("Found: " + result.Title); 
                },
                403: function (result) {
                    $("#result").html(result);
                }
            }
        });

    </script>
</head>
<body>
    <div id="result"></div>

</body>
</html> 

This fetches an entity with the specified identifier and passes the API key along with the request. If no key or an incorrect key is supplied an error wrapped in JSON format will be returned.

Other actions are very similar. For example, to update an entity one would code something like the following:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Update Entity Example</title>
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

    <script>
        var person = {
            FirstName: 'Francis',
            LastName: 'Crick',
            Type: "Person",
            Title: "Molecular biologist",
            "Id": "8a066845-a533-457d-b384-a2788a68ac33",
            $typekey:"Person"
        };
        $.ajax({
            url: "https://api.company.com/UpdateEntity",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("ApiKey", "YPeXQkIa4N");
            },
            xhrFields: {
                withCredentials: true
            },
            type: "POST",
            data: JSON.stringify(person),
            contentType: "application/json;charset=utf-8",
            error: function(result) {
                $("#result").html(result);
            },
            statusCode: {
                200: function (result) {
                    $("#result").html(result); 
                },
                403: function (result) {
                    $("#result").html(result);
                }
            }
        });

    </script>
</head>
<body>
    <div id="result"></div> 
</body>
</html> 

A XAML client can send requests to the web API as follows:

private HttpClient client;
public MainWindow()
{
    InitializeComponent();
    client = new HttpClient
                 {
                     BaseAddress = new Uri("https://api.company.com")
                 };
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    this.Loaded += MainWindow_Loaded;
}

async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    client.DefaultRequestHeaders.Add("ApiKey", "YPeXQkIa4N");
    var response = await client.GetAsync("GetEntity/8a066845-a533-457d-b384-a2788a68ac33");
    response.EnsureSuccessStatusCode(); 
    var content = response.Content;
    var result = await content.ReadAsStringAsync();
}

The necessary referenced libraries and details can be found in the ASP.Net MVC documentation.

The advantage of having a graph-like NoSQL backend is that you can fetch an indefinite series of data types in one go. In the following example the Telerik Kendo UI data grid is used to display the results of a search. Note that due to URL encoding specifics the wildcard used normally (i.e. '*' or '%') is replaced by the '$' symbol.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>GraphDB | Data Grid</title>
    <script src="scripts/jquery.min.js"></script>
    <script src="scripts/kendo.all.min.js"></script>
    <link href="styles/kendo.common.min.css" rel="stylesheet" />
    <link href="styles/kendo.default.min.css" rel="stylesheet" />
    <script>

        $(document).ready(function () {

            $("#grid").kendoGrid({
                sortable: true,
                columns: [{
                    field: "Title",
                    title: "Title",
                    width: 140
                }, {
                    field: "DataType",
                    title: "Type",
                    width: 190
                }]
            });

            $.ajax({
                url: "https://api.company.com/Search/$Is$",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("ApiKey", "YPeXQkIa4N");
                },
                xhrFields: {
                    withCredentials: true
                },
                type: "GET",
                error: function (result) {
                    $("#result").html(result);
                },
                statusCode: {
                    200: function (result) {
                        $("#grid").data("kendoGrid").dataSource.data(result);
                    },
                    403: function (result) {
                        $("#result").html(result);
                    }
                }
            });

        });
    </script>
</head>
<body>
    <div id="grid" style="width:1200px; height:500px;"></div>   
</body>
</html>

The result of this is something like the screenshot below, showing only the Title and the DataType of the Nodes being fetched. Because JavaScript does not have any knowledge of the returned .Net data types it would require explicit extraction of the type-specific properties from the acquired JSON array (and the appended serialized data entities).

DataGrid sample output.

Obviously, if one wishes to display the graph-like result (i.e. the connections between the entities) it requires a clientside diagramming library or a different approach (parent-children grid display for example).

query language

QL is based on a message processing engine which can be easily embedded and extended but it's outside the scope of this manual to go deeper into it. The sample output shown in what follows was created by embedding the QL engine in a simple command-line application. You can, however, also create similar interactions by embedding it in a web form or standard GUI.

QL is based on an hierarchy of commands which gradually specify the action or request. Commands always end with ':' and followed by a set of parameters. For example, to add a Person you'd write:

add:Person: Johann Sebastian Bach

which could be more specific:

add:Person: title=Great composer/FirstName=Johann Sebastian/LastName=Bach

The fields of the data entity are separated by a '/' and fields which cannot be matched will be ignored. The 'Person' keyword is any type of registered entity in the system (using Register or RegisterAssembly method). So, if you have defined an entity type 'DentalCast' you can write

add:dentalCast: weight=532gr/ABO=ADE47C

and you should note that all things are case insensitive.

In order to update or delete an entity you need a reference to one, i.e. read or get one. This can be done in various ways, for example by means of a search:

search:%Bach%

which will return the entities containing 'Bach' in their title:

Search

In command line mode the processing engine keeps track of the interactions, hence the '[3]' in the upper-left corner, referring to the output corresponding to the third input. This number is also a number you can 'get'. By requesting

get:3

the same table will be returned. However, if you further specify the row number like so

get:3.2

you will get the entity:

Get

This can be done not just with search results but also with other queries. As another example, if you wish to see the registered users and then get a reference to one in particular:

users:

you will see something like

Get users

You can also use memory pointers which can be handy to recall entities. These pointers live within the scope of a session. If you wish to have more permanent pointers you can use tags and favorites (see below). To set a pointer to 'J.S.Bach' with the pointer name 'Bach' you would write:

set:3.2/Bach

and recall it with this name instead of a numeric reference

get:Bach

A typical output in along this line would be

Get/Set things

To update or delete an entity you can use a numerical reference or a memory pointer:

// updating the first name  
update:3.2/FirstName=J.S.
// delete it
delete: Bach

To get tags you simply use

tags:

The favorites, being a special kind of tag, works similarly

favorites: 

Get tags and favorites

If you wish to tag an entity you can use the tag command followed by a reference and the name of the tag:

Tag things

If the tag doesn't exist it will be automatically created. Note also that GraphDB automatically generates certain tags, like the 'People' tag for 'Person' entities.In case you are unsure about the syntax the processing engine also automatically collects help information from the QL addins which can be fetch through

help:

Help command

Workspaces can be added by means of

add:workspace: My new workspace

and you can always ask which workspace you are in by

current:

Workspaces

Of course, you can also link entities and thus create a graph structure. This is done by the 'link' command, which has various optional parameters

link: Bach/His son/Family

Once you have a graph structure you can query children, neighbors, parents and so one:

children: Bach

which will return something like the following

Children

If you have embedded the QL engine with admin commands enabled it's also possible to execute user management commands, like

add: user: Maria/herPa$sw0rd/AdminPaaaswooord

or

delete: Maria

The engine also ensures that you confirm the deletion and that all objects are cleaned up which are linked to the user.

Sharing spaces does not required the admin level and can be easily done. For example, to share the current workspace with user 'John' and role 'Member' you would write

grant: John 

or if you need to assign an 'Editor' role to him

grant: John/Editor

To see with whom the workspace is shared you can use the 'shares' command

shares:

Shares

If at some point you wish to revoke access you can simply use:

revoke:John

and all access to the workspace will be denied.

Of course, all of these commands rely on the underlying client and admin API. This means that all security is checked and that all potential constraints raised either by the logic in the stored procedures or by the business logic will bubble up to the QL output.

QL is not a replacement for the API or does not claim to be a full query language like SPARQL or SQL. It simply is a fast-forward way to interact with GraphDB and is very easy to extend and/or embed. The full potential of the underlying message processing engine will be discussed elsewhere and is a large topic on its own.