Azure Cosmos DB: Develop with the Graph API in .NET

Azure Cosmos DB is Microsoft's globally distributed multi-model database service. You can quickly create and query document, key/value, and graph databases, all of which benefit from the global distribution and horizontal scale capabilities at the core of Azure Cosmos DB.

This tutorial demonstrates how to create an Azure Cosmos DB account using the Azure portal and how to create a graph database and container. The application then creates a simple social network with four people using the Graph API (preview), then traverses and queries the graph using Gremlin.

This tutorial covers the following tasks:

  • Create an Azure Cosmos DB account
  • Create a graph database and container
  • Serialize vertices and edges to .NET objects
  • Add vertices and edges
  • Query the graph using Gremlin

Graphs in Azure Cosmos DB

You can use Azure Cosmos DB to create, update, and query graphs using the Microsoft.Azure.Graphs library. The Microsoft.Azure.Graph library provides a single extension method CreateGremlinQuery<T> on top of the DocumentClient class to execute Gremlin queries.

Gremlin is a functional programming language that supports write operations (DML) and query and traversal operations. We cover a few examples in this article to get your started with Gremlin. See Gremlin queries for a detailed walkthrough of Gremlin capabilities available in Azure Cosmos DB.

Prerequisites

Please make sure you have the following:

Create database account

Let's start by creating an Azure Cosmos DB account in the Azure portal.

Tip

  1. In a new browser window, sign in to the Azure portal.

  2. Click New > Databases > Azure Cosmos DB.

    Azure portal "Databases" pane

  3. In the New account page, enter the settings for the new Azure Cosmos DB account.

    Setting Suggested value Description
    ID Enter a unique name Enter a unique name to identify this Azure Cosmos DB account. Because documents.azure.com is appended to the ID that you provide to create your URI, use a unique but identifiable ID.

    The ID can contain only lowercase letters, numbers, and the hyphen (-) character, and it must contain 3 to 50 characters.
    API Gremlin (graph) The API determines the type of account to create. Azure Cosmos DB provides five APIs to suits the needs of your application: SQL (document database), Gremlin (graph database), MongoDB (document database), Azure Table, and Cassandra, each which currently require a separate account.

    Select Gremlin (graph) because in this quickstart you are creating a graph that is queryable using Gremlin syntax.

    Learn more about the Graph API
    Subscription Your subscription Select Azure subscription that you want to use for this Azure Cosmos DB account.
    Resource group Enter the same unique name as provided above in ID Enter a new resource-group name for your account. For simplicity, you can use the same name as your ID.
    Location Select the region closest to your users Select geographic location in which to host your Azure Cosmos DB account. Use the location that's closest to your users to give them the fastest access to the data.
    Enable geo-redundancy Leave blank This creates a replicated version of your database in a second (paired) region. Leave this blank.
    Pin to dashboard Select Select this box so that your new database account is added to your portal dashboard for easy access.

    Then click Create.

    The new account blade for Azure Cosmos DB

  4. The account creation takes a few minutes. During account creation the portal displays the Deploying Azure Cosmos DB tile.

    The Azure portal Notifications pane

    Once the account is created, the Congratulations! Your Azure Cosmos DB account was created page is displayed.

Set up your Visual Studio solution

  1. Open Visual Studio on your computer.
  2. On the File menu, select New, and then choose Project.
  3. In the New Project dialog, select Templates / Visual C# / Console App (.NET Framework), name your project, and then click OK.
  4. In the Solution Explorer, right click on your new console application, which is under your Visual Studio solution, and then click Manage NuGet Packages...
  5. In the NuGet tab, click Browse, and type Microsoft.Azure.Graphs in the search box, and check the Include prerelease versions.
  6. Within the results, find Microsoft.Azure.Graphs and click Install.

    If you get a message about reviewing changes to the solution, click OK. If you get a message about license acceptance, click I accept.

    The Microsoft.Azure.Graphs library provides a single extension method CreateGremlinQuery<T> for executing Gremlin operations. Gremlin is a functional programming language that supports write operations (DML) and query and traversal operations. We cover a few examples in this article to get your started with Gremlin. Gremlin queries has a detailed walkthrough of Gremlin capabilities in Azure Cosmos DB.

Connect your app

Add these two constants and your client variable in your application.

string endpoint = ConfigurationManager.AppSettings["Endpoint"]; 
string authKey = ConfigurationManager.AppSettings["AuthKey"]; 

Next, head back to the Azure portal to retrieve your endpoint URL and primary key. The endpoint URL and primary key are necessary for your application to understand where to connect to, and for Azure Cosmos DB to trust your application's connection.

In the Azure portal, navigate to your Azure Cosmos DB account, click Keys, and then click Read-write Keys.

Copy the URI from the portal and paste it over Endpoint in the endpoint property above. Then copy the PRIMARY KEY from the portal and paste it into the AuthKey property above.

Screen shot of the Azure portal used by the tutorial to create a C# application. Shows an Azure Cosmos DB account the KEYS button highlighted on the Azure Cosmos DB navigation , and the URI and PRIMARY KEY values highlighted on the Keys blade

Instantiate the DocumentClient

Next, create a new instance of the DocumentClient.

DocumentClient client = new DocumentClient(new Uri(endpoint), authKey); 

Create a database

Now, create an Azure Cosmos DB database by using the CreateDatabaseAsync method or CreateDatabaseIfNotExistsAsync method of the DocumentClient class from the DocumentDB .NET SDK.

Database database = await client.CreateDatabaseIfNotExistsAsync(new Database { Id = "graphdb" }); 

Create a graph

Next, create a graph container by using the using the CreateDocumentCollectionAsync method or CreateDocumentCollectionIfNotExistsAsync method of the DocumentClient class. A collection is a container of graph entities.

DocumentCollection graph = await client.CreateDocumentCollectionIfNotExistsAsync( 
    UriFactory.CreateDatabaseUri("graphdb"), 
    new DocumentCollection { Id = "graphcollz" }, 
    new RequestOptions { OfferThroughput = 1000 }); 

Serialize vertices and edges to .NET objects

Azure Cosmos DB uses the GraphSON wire format, which defines a JSON schema for vertices, edges, and properties. The Azure Cosmos DB .NET SDK includes JSON.NET as a dependency, and this allows us to serialize/deserialize GraphSON into .NET objects that we can work with in code.

As an example, let's work with a simple social network with four people. We look at how to create Person vertices, add Knows relationships between them, then query and traverse the graph to find "friend of friend" relationships.

The Microsoft.Azure.Graphs.Elements namespace provides Vertex, Edge, Property and VertexProperty classes for deserializing GraphSON responses to well-defined .NET objects.

Run Gremlin using CreateGremlinQuery

Gremlin, like SQL, supports read, write, and query operations. For example, the following snippet shows how to create vertices, edges, perform some sample queries using CreateGremlinQuery<T>, and asynchronously iterate through these results using ExecuteNextAsync and `HasMoreResults.

Dictionary<string, string> gremlinQueries = new Dictionary<string, string>
{
    { "Cleanup",        "g.V().drop()" },
    { "AddVertex 1",    "g.addV('person').property('id', 'thomas').property('firstName', 'Thomas').property('age', 44)" },
    { "AddVertex 2",    "g.addV('person').property('id', 'mary').property('firstName', 'Mary').property('lastName', 'Andersen').property('age', 39)" },
    { "AddVertex 3",    "g.addV('person').property('id', 'ben').property('firstName', 'Ben').property('lastName', 'Miller')" },
    { "AddVertex 4",    "g.addV('person').property('id', 'robin').property('firstName', 'Robin').property('lastName', 'Wakefield')" },
    { "AddEdge 1",      "g.V('thomas').addE('knows').to(g.V('mary'))" },
    { "AddEdge 2",      "g.V('thomas').addE('knows').to(g.V('ben'))" },
    { "AddEdge 3",      "g.V('ben').addE('knows').to(g.V('robin'))" },
    { "UpdateVertex",   "g.V('thomas').property('age', 44)" },
    { "CountVertices",  "g.V().count()" },
    { "Filter Range",   "g.V().hasLabel('person').has('age', gt(40))" },
    { "Project",        "g.V().hasLabel('person').values('firstName')" },
    { "Sort",           "g.V().hasLabel('person').order().by('firstName', decr)" },
    { "Traverse",       "g.V('thomas').outE('knows').inV().hasLabel('person')" },
    { "Traverse 2x",    "g.V('thomas').outE('knows').inV().hasLabel('person').outE('knows').inV().hasLabel('person')" },
    { "Loop",           "g.V('thomas').repeat(out()).until(has('id', 'robin')).path()" },
    { "DropEdge",       "g.V('thomas').outE('knows').where(inV().has('id', 'mary')).drop()" },
    { "CountEdges",     "g.E().count()" },
    { "DropVertex",     "g.V('thomas').drop()" },
};

foreach (KeyValuePair<string, string> gremlinQuery in gremlinQueries)
{
    Console.WriteLine($"Running {gremlinQuery.Key}: {gremlinQuery.Value}");

    // The CreateGremlinQuery method extensions allow you to execute Gremlin queries and iterate
    // results asychronously
    IDocumentQuery<dynamic> query = client.CreateGremlinQuery<dynamic>(graph, gremlinQuery.Value);
    while (query.HasMoreResults)
    {
        foreach (dynamic result in await query.ExecuteNextAsync())
        {
            Console.WriteLine($"\t {JsonConvert.SerializeObject(result)}");
        }
    }

    Console.WriteLine();
}

Add vertices and edges

Let's look at the Gremlin statements shown in the preceding section more detail. First we some vertices using Gremlin's addV method. For example, the following snippet creates a "Thomas Andersen" vertex of type "Person", with properties for first name, last name, and age.

// Create a vertex
IDocumentQuery<Vertex> createVertexQuery = client.CreateGremlinQuery<Vertex>(
    graphCollection, 
    "g.addV('person').property('firstName', 'Thomas')");

while (createVertexQuery.HasMoreResults)
{
    Vertex thomas = (await create.ExecuteNextAsync<Vertex>()).First();
}

Then we create some edges between these vertices using Gremlin's addE method.

// Add a "knows" edge
IDocumentQuery<Edge> createEdgeQuery = client.CreateGremlinQuery<Edge>(
    graphCollection, 
    "g.V('thomas').addE('knows').to(g.V('mary'))");

while (create.HasMoreResults)
{
    Edge thomasKnowsMaryEdge = (await create.ExecuteNextAsync<Edge>()).First();
}

We can update an existing vertex by using properties step in Gremlin. We skip the call to execute the query via HasMoreResults and ExecuteNextAsync for the rest of the examples.

// Update a vertex
client.CreateGremlinQuery<Vertex>(
    graphCollection, 
    "g.V('thomas').property('age', 45)");

You can drop edges and vertices using Gremlin's drop step. Here's a snippet that shows how to delete a vertex and an edge. Note that dropping a vertex performs a cascading delete of the associated edges.

// Drop an edge
client.CreateGremlinQuery(graphCollection, "g.E('thomasKnowsRobin').drop()");

// Drop a vertex
client.CreateGremlinQuery(graphCollection, "g.V('robin').drop()");

Query the graph

You can perform queries and traversals also using Gremlin. For example, the following snippet shows how to count the number of vertices in the graph:

// Run a query to count vertices
IDocumentQuery<int> countQuery = client.CreateGremlinQuery<int>(graphCollection, "g.V().count()");

You can perform filters using Gremlin's has and hasLabel steps, and combine them using and, or, and not to build more complex filters:

// Run a query with filter
IDocumentQuery<Vertex> personsByAge = client.CreateGremlinQuery<Vertex>(
  graphCollection, 
  "g.V().hasLabel('person').has('age', gt(40))");

You can project certain properties in the query results using the values step:

// Run a query with projection
IDocumentQuery<string> firstNames = client.CreateGremlinQuery<string>(
  graphCollection, 
  $"g.V().hasLabel('person').values('firstName')");

So far, we've only seen query operators that work in any database. Graphs are fast and efficient for traversal operations when you need to navigate to related edges and vertices. Let's find all friends of Thomas. We do this by using Gremlin's outE step to find all the out-edges from Thomas, then traversing to the in-vertices from those edges using Gremlin's inV step:

// Run a traversal (find friends of Thomas)
IDocumentQuery<Vertex> friendsOfThomas = client.CreateGremlinQuery<Vertex>(
  graphCollection,
  "g.V('thomas').outE('knows').inV().hasLabel('person')");

The next query performs two hops to find all of Thomas' "friends of friends", by calling outE and inV two times.

// Run a traversal (find friends of friends of Thomas)
IDocumentQuery<Vertex> friendsOfFriendsOfThomas = client.CreateGremlinQuery<Vertex>(
  graphCollection,
  "g.V('thomas').outE('knows').inV().hasLabel('person').outE('knows').inV().hasLabel('person')");

You can build more complex queries and implement powerful graph traversal logic using Gremlin, including mixing filter expressions, performing looping using the loop step, and implementing conditional navigation using the choose step. Learn more about what you can do with Gremlin support!

That's it, this Azure Cosmos DB tutorial is complete!

Clean up resources

If you're not going to continue to use this app, use the following steps to delete all resources created by this tutorial in the Azure portal.

  1. From the left-hand menu in the Azure portal, click Resource groups and then click the name of the resource you created.
  2. On your resource group page, click Delete, type the name of the resource to delete in the text box, and then click Delete.

Next steps

In this tutorial, you've done the following:

  • Created an Azure Cosmos DB account
  • Created a graph database and container
  • Serialized vertices and edges to .NET objects
  • Added vertices and edges
  • Queried the graph using Gremlin

You can now build more complex queries and implement powerful graph traversal logic using Gremlin.