DEV Community

Cover image for Getting started with Cypher and RedisGraph / Part II
Gianluca Fabrizi
Gianluca Fabrizi

Posted on • Edited on

Getting started with Cypher and RedisGraph / Part II

In the first post of this series we looked at what a Graph DB is and why should you use one.
Today we'll learn what is Cypher start querying the graph!

What is Cypher

Cypher it's a declarative query language, developed internally by Neo4j since 2011.
There is an ongoing standardization process (OpenCypher) to make Cypher an open standard.
Cypher let's you easily retrieve data from the graph. It's one of the simplest query language to learn; its syntax is constructed in a way that helps visualize relationship between nodes

RedisInsight

We will be running queries in RedisInsight. If you've read the previous post of this series, you should know how to run a docker based installation of Redis.
After launching docker compose up -d, point your browser to http://localhost:8001 , RedisInsight should welcome you. Accept the EULA and you will be redirected to RedisInsight dashboard.
Next click on the "Workbench" tab (the third icon in the side menu on the left).
The upper right pane of this new screen is where you'll write all the queries.

Adding nodes and edges

Now that both Redis and RedisInsight are up & running, let's create a new graph and add one node to it:

GRAPH.QUERY Social "CREATE (:Person {name: 'Laura Phillips', age: 32})"
Enter fullscreen mode Exit fullscreen mode

Every query is prepended by GRAPH.QUERY and the graph name.
Now click the green arrow to the right or press Ctrl + Enter.

The output should be similar to this:

after first CREATE

The first time we create e node in a non-existent graph, RedisGraph will create the graph for us.
So we've just created a new graph called "Social" and added one node of type "Person".
name and age are properties that we add to the node. RedisGrap automatically add another property to each node: the unique id.

We can add more nodes in bulk:

GRAPH.QUERY Social "CREATE (:Person {name: 'Diana Hendrickson', age: 31}), (:Person {name: 'Susan Hendrickson', age: 29}), (:Person {name: 'Peter Steinmetz', age: 30}), (:Person {name: 'Louise Rosol', age: 29}), (:Person {name: 'Bryce Fett', age: 30})"
Enter fullscreen mode Exit fullscreen mode

Now you should have 6 nodes of type Person in the graph. Let's check them out!

Basic querying

GRAPH.QUERY Social "MATCH (p:Person) RETURN p"
Enter fullscreen mode Exit fullscreen mode

Let's analyze this query: MATCH is the keyword that describes the relationship between queried entities. It's used to 'match' something in the graph, based on some parameters. In this case we are asking for Person, and we alias our results with a p (the alias name is not important, you can choose any letter or word).
Nodes are always specified in round brackets.
The RETURN keyword returns every p found.
So this query returns every Person it finds in the graph Social.

The resulting graph should be something like this:

MATCH-ing all nodes

The previous query could also be written as:

GRAPH.QUERY Social "MATCH (a) RETURN a"
Enter fullscreen mode Exit fullscreen mode

This is the generic way to view all nodes in the graph, you'll see this a lot of times. Here we are using an alias a without asking for a specific node type.

Ok, so let's say we want to retrieve all Person(s) who are exactly 30 years old:

GRAPH.QUERY Social "MATCH (p:Person {age: 30}) RETURN p"
Enter fullscreen mode Exit fullscreen mode

this query introduces another feature: match for node's attribute. Here we are matching only nodes of type Person and with an attribute age equal to 30.
You can query for multiple attributes, by separating attribute: value couples with comma between the curly brackets.

Sometimes visualizing a graph of this kind isn't much helpful, a textual result might be more useful.
So click on the </> icon just above the last graph and choose Text; the output of the last query will be shown as text:

MATCH results as text

Connecting nodes and creating edges

Now we add one relation between two nodes:

GRAPH.QUERY Social "MATCH (a:Person {name: 'Diana Hendrickson'}), (b:Person {name: 'Susan Hendrickson'}) CREATE (a)-[:KNOWS {relation: 'sister'}]->(b)"
Enter fullscreen mode Exit fullscreen mode

The first part of the query is a MATCH to find 2 Person ('Diana Hendrickson' and 'Susan Hendrickson'); we alias them with a and b.

Then we create the relation:

CREATE (a)-[:KNOWS {relation: 'sister'}]->(b)
Enter fullscreen mode Exit fullscreen mode

relationships are always written in square brackets. We are binding node a and node b with a relation of type KNOWS. The KNOWS relation has an attribute relation with value sister.

Edge creation

Redis is informing us that it has created one relationship with one properties.

NOTE: The correct way to add a relation between existent nodes is by matching them first; if we try to directly create the relationship, like so:
CREATE (a:Person {name: 'Diana Hendrickson'})-[:KNOWS {relation: 'sister'}]->(b:Person {name: 'Susan Hendrickson'})
RedisGraph would create 2 new nodes and add the relation between them

The generic query for relationship has this form:

(NodeA)-[:Relationship]->(NodeB)
Enter fullscreen mode Exit fullscreen mode

take one minute to appreciate how eloquent and visually clear the syntax of Cypher can be😯👏.

Ok, now we can add some more relationships in the graph:

GRAPH.QUERY Social "MATCH (a:Person {name: 'Susan Hendrickson'}), (b:Person {name: 'Peter Steinmetz'}) CREATE (a)-[:KNOWS {relation: 'married'}]->(b)"
GRAPH.QUERY Social "MATCH (a:Person {name: 'Susan Hendrickson'}), (b:Person {name: 'Louise Rosol'}) CREATE (a)-[:KNOWS {relation: 'friend'}]->(b)"
GRAPH.QUERY Social "MATCH (a:Person {name: 'Peter Steinmetz'}), (b:Person {name: 'Bryce Fett'}) CREATE (a)-[:KNOWS {relation: 'friend'}]->(b)"
GRAPH.QUERY Social "MATCH (a:Person {name: 'Laura Phillips'}), (b:Person {name: 'Louise Rosol'}) CREATE (a)-[:KNOWS {relation: 'coworker'}]->(b)"
GRAPH.QUERY Social "MATCH (a:Person {name: 'Laura Phillips'}), (b:Person {name: 'Diana Hendrickson'}) CREATE (a)-[:KNOWS {relation: 'coworker'}]->(b)"
GRAPH.QUERY Social "MATCH (a:Person {name: 'Louise Rosol'}), (b:Person {name: 'Diana Hendrickson'}) CREATE (a)-[:KNOWS {relation: 'coworker'}]->(b)"
Enter fullscreen mode Exit fullscreen mode

Let's see what we just did:

GRAPH.QUERY Social "MATCH (p:Person) RETURN p"
Enter fullscreen mode Exit fullscreen mode

the graph is exactly the same as before... why?🤔

The reason is that RedisInshight hides relationships by default; if you enable the "All relationship" slide in the upper right side of the graph, RedisInsight will also show you all the relationships between nodes:

All edges created

Now try to create a connection between an existent node and a non-existent node:

GRAPH.QUERY Social "MATCH (p:Person {name: 'Laura Phillips'}) CREATE (p)-[:KNOWS {relation: 'married'}]->(:Person {name: 'William Stultz', age:33})"
Enter fullscreen mode Exit fullscreen mode

RedisGraph creates for us the unknown node (Person 'William Stultz') and add the relationship between the known node and the new one.

Querying for relationships

Now that we've created some "connections" between nodes, we can query the graph for relation between nodes.
Let's say we want to show every married Person:

GRAPH.QUERY Social "MATCH (p:Person)-[:KNOWS {relation:'married'}]->(o:Person) RETURN p,o"
Enter fullscreen mode Exit fullscreen mode

Or we need the list of every Person that knows 'Susan Hendrickson':

GRAPH.QUERY Social "MATCH (p:Person)-[:KNOWS]-(:Person {name: 'Susan Hendrickson'}) RETURN p"
Enter fullscreen mode Exit fullscreen mode

here we made some subtle but important changes to the query: first of all, we don't need the Person 'Susan Hendrickson' so we haven't added an alias to it.
Second: in RedisGraph every relation has a direction (starting from Node A ending on Node B). There's no point in giving the :KNOWS relation a direction in the last query (two friends are friends to each other, it's a bi-directional relationship).
So we query for the relation without specifying the direction; the syntax (a)-[:KNOWS]->(b) became (a)-[:KNOWS]-(b).

What if instead of the list of acquaintances of 'Susan Hendrickson' we want just the count?
We can use the COUNT aggregation:

GRAPH.QUERY Social "MATCH (p:Person)-[:KNOWS]-(:Person {name: 'Susan Hendrickson'}) RETURN count(p)"
Enter fullscreen mode Exit fullscreen mode


There is no graph to see here, so RedisGraph only shows the textual output of the COUNT:

COUNT nodes

Delete nodes and edges

The last query we'll look at today is the DELETE:

GRAPH.QUERY Social "MATCH (p:Person {name: 'William Stultz'}) DELETE p"
Enter fullscreen mode Exit fullscreen mode

again, we first MATCH the node, then we DELETE it.

As you may remember, we created a relationship between this node and another Person ('Laura Phillips'). When we delete a node, all its relationships are deleted too. This may seems obvious, but it's something to keep in mind.

RECAP

In this post we learned the basics of Cypher and how to query RedisGraph. The topic is vast, we haveve covered a small part of it just to get you started.

In the next (and last) post, I'll show you a more practical example of RedisGraph in action.

Top comments (0)