RedisGraph is a super-fast graph database, and like others of its kind (such as Neo4j), it is useful to represent networks of entities and their relationships. Examples include social networks, family trees, and organisation charts.
Getting Started
The easiest way to try RedisGraph is using Docker. Use the following command, which is based on what the Quickstart recommends but instead uses the edge
tag, which would have the latest features and fixes:
sudo docker run -p 6379:6379 -it --rm redislabs/redisgraph:edge
You will also need the redis-cli
tool to run the example queries. On Ubuntu (or similar), you can get this by installing the redis-tools
package.
Tom Loves Judy
We’ll start by representing something really simple: Tom Loves Judy.
We can create this graph using a single command:
GRAPH.QUERY TomLovesJudy "CREATE (tom:Person {name: 'Tom'})-[:loves]->(judy:Person {name: 'Judy'})"
When using redis-cli
, queries will also follow the format of GRAPH.QUERY <key> "<cypher_query>"
. In RedisGraph, a graph is stored in a Redis key (in this case called “TomLovesJudy
“) with the special type graphdata
, thus this must always be specified in queries. The query itself is the part between double quotes, and uses a language called Cypher. Cypher is also used by Neo4j among other software, and RedisGraph implements a subset of it.
Cypher represents nodes and relationships using a sort of ASCII art. Nodes are represented by round brackets (parentheses), and relationships are represented by square brackets. The arrow indicates the direction of the relationship. RedisGraph at present does not support undirected relationships. When you run the above command, Redis should provide some output indicating what happened:
Since our graph has been created, we can start running queries against it. For this, we use the MATCH
keyword:
GRAPH.QUERY TomLovesJudy "MATCH (x) RETURN x"
Since round brackets represent a node, here we’re saying that we want the query to match any node, which we’ll call x
, and then return it. The output for this is quite verbose:
1) 1) "x"
2) 1) 1) 1) 1) "id"
2) (integer) 0
2) 1) "labels"
2) 1) "Person"
3) 1) "properties"
2) 1) 1) "name"
2) "Tom"
2) 1) 1) 1) "id"
2) (integer) 1
2) 1) "labels"
2) 1) "Person"
3) 1) "properties"
2) 1) 1) "name"
2) "Judy"
3) 1) "Query internal execution time: 61.509847 milliseconds"
As you can see, this has given us the whole structure of each node. If we just want to get something specific, such as the name, then we can specify it in the RETURN
clause:
GRAPH.QUERY TomLovesJudy "MATCH (x) RETURN x.name"
1) 1) "x.name"
2) 1) 1) "Tom"
2) 1) "Judy"
3) 1) "Query internal execution time: 0.638126 milliseconds"
We can also query based on relationships. Let’s see who loves who:
GRAPH.QUERY TomLovesJudy "MATCH (x)-[:loves]->(y) RETURN x.name, y.name"
1) 1) "x.name"
2) "y.name"
2) 1) 1) "Tom"
2) "Judy"
3) 1) "Query internal execution time: 54.642536 milliseconds"
It seems like Tom Loves Judy. Unfortunately, Judy does not love Tom back.
Company Shareholding
Let’s take a look at a slightly more interesting example.
In this graph, we have companies (blue nodes) which are owned by multiple individuals (red nodes). We can’t create this as a single command as we did before. We also can’t simply issue a series of CREATE
commands, because we may end up creating multiple nodes with the same name.
Instead, let’s create all the nodes separately first:
GRAPH.QUERY Companies "CREATE (:Individual {name: 'X'})"
GRAPH.QUERY Companies "CREATE (:Individual {name: 'Y'})"
GRAPH.QUERY Companies "CREATE (:Individual {name: 'Z'})"
GRAPH.QUERY Companies "CREATE (:Company {name: 'A'})"
GRAPH.QUERY Companies "CREATE (:Company {name: 'B'})"
You’ll notice here that the way we are defining nodes is a little different. A node follows the structure (alias:type {properties})
. The alias is not much use in such CREATE
commands, but on the other hand, the type now (unlike in the earlier example) gives us a way to distinguish between different kinds of nodes.
Now that we have the nodes, we can create the relationships:
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'X' }), (c:Company { name : 'A' }) CREATE (x)-[:owns {percentage: 85}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Y' }), (c:Company { name : 'A' }) CREATE (x)-[:owns {percentage: 15}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Y' }), (c:Company { name : 'B' }) CREATE (x)-[:owns {percentage: 55}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Z' }), (c:Company { name : 'B' }) CREATE (x)-[:owns {percentage: 45}]->(c)"
In order to make sure we apply the relationships to existing nodes (as opposed to creating new ones), we first find the nodes we want with a MATCH
clause, and then CREATE
the relationship between them. You’ll notice that our relationships now also have properties.
Now that our graph is set up, we can start querying it! Here are a few things we can do with it.
Return the names of all the nodes:
GRAPH.QUERY Companies "MATCH (x) RETURN x.name"
1) 1) "x.name"
2) 1) 1) "X"
2) 1) "Y"
3) 1) "Z"
4) 1) "A"
5) 1) "B"
3) 1) "Query internal execution time: 0.606600 milliseconds"
Return the names only of the companies:
GRAPH.QUERY Companies "MATCH (c:Company) RETURN c.name"
1) 1) "c.name"
2) 1) 1) "A"
2) 1) "B"
3) 1) "Query internal execution time: 0.515959 milliseconds"
Return individual ownership in each company (separate fields):
GRAPH.QUERY Companies "MATCH (i)-[s]->(c) RETURN i.name, s.percentage, c.name"
1) 1) "i.name"
2) "s.percentage"
3) "c.name"
2) 1) 1) "X"
2) (integer) 85
3) "A"
2) 1) "Y"
2) (integer) 15
3) "A"
3) 1) "Y"
2) (integer) 55
3) "B"
4) 1) "Z"
2) (integer) 45
3) "B"
3) 1) "Query internal execution time: 1.627741 milliseconds"
Return individual ownership in each company (concatenated strings):
GRAPH.QUERY Companies "MATCH (i)-[s]->(c) RETURN i.name + ' owns ' + round(s.percentage) + '% of ' + c.name"
1) 1) "i.name + ' owns ' + round(s.percentage) + '% of ' + c.name"
2) 1) 1) "X owns 85% of A"
2) 1) "Y owns 15% of A"
3) 1) "Y owns 55% of B"
4) 1) "Z owns 45% of B"
3) 1) "Query internal execution time: 1.281184 milliseconds"
Find out who owns at least 50% of the shares in Company A:
GRAPH.QUERY Companies "MATCH (i)-[s]->(c) WHERE s.percentage >= 50 AND c.name = 'A' RETURN i.name"
1) 1) "i.name"
2) 1) 1) "X"
3) 1) "Query internal execution time: 1.321579 milliseconds"
Wrapping Up
In this article, we’ve seen how to:
- get up and running with RedisGraph
- create simple graphs
- perform basic queries
We’ve obviously scratched the surface of RedisGraph and Cypher, but hopefully these examples will help others who, like me, are new to this space.
3 thoughts on “First Steps with RedisGraph”