Graph Databases Introduction: An Indian Restaurant

capnspek

Bhaskar Sharma

Posted on February 20, 2023

Graph Databases Introduction: An Indian Restaurant

INTRODUCTION
This blog post is intended to introduce the readers to Graph databases and Open-Cypher graph query language. It takes example of an imaginary Indian restaurant menu, and the tutorial revolves around it.
The reader is expected to have set-up some kind of graph database management system before starting out with this tutorial.
Apache-AGE setup on PostgreSQL has been used to implement Graph database throughout this tutorial.
Read about Apache-AGE here: https://age.apache.org/
GitHub here: https://github.com/apache/age

THE PROBLEM
Suppose that you and I together start an Indian restaurant in Budapest with a modest investment. You are in-charge of management of the restaurant and I am in-charge of food and cooking.
Given our initial investment, we come up with a small and suiting menu for our little cozy restaurant. We go with 1 starter, 4 main curries, 2 types of breads, 1 beverage, and 1 dessert.

Sasta Achha Restaurant Menu

We decide to store our menu in the form of a graph database.

How can we achieve it?

Creating nodes
Well, for starters we can store each food item as a node in our Graph database. We can use the label to identify whether it is a starter, curry, beverage, bread or dessert. We can also store some additional properties along with the item such as: -

  • name: Name of the food item.
  • type: Whether the food is vegan, vegetarian, or non vegetarian.
  • price: Price of the food item.

How do we create a node in Open Cypher?

We can create a node in Open Cypher by using the following query syntax: -
CREATE (:LABEL {property1: "VALUE1", property2: "VALUE2"})
To do it in Apache AGE, first create a graph: -
SELECT * FROM ag_catalog.create_graph('menu');
In Apache AGE, the Open Cypher query is inputted like this: -

SELECT * FROM cypher('graph_name', $$ 
/* Cypher Query Here */ 
$$) AS (result1 agtype, result2 agtype);
Enter fullscreen mode Exit fullscreen mode

Hence, if we wish to add a node for Samosa in our graph named 'menu', it will look like this: -

SELECT * FROM cypher('menu', $$
CREATE (:STARTER {name: "Samosa", type: "Vegan", price: 30})
$$) AS (result agtype);
Enter fullscreen mode Exit fullscreen mode

Different food items can have different labels and properties. You can experiment with them on your own. For the sake of this tutorial, I have prepared a query to add all the menu items with suitable description to our graph. The query can be found here (along with all other queries used throughout this tutorial).

Adding all food items

Query for all nodes
We can confirm that our food items were correctly added by executing a MATCH query in Open Cypher.

MATCH (n) RETURN n
Enter fullscreen mode Exit fullscreen mode

MATCH is used to select nodes in Open Cypher. n is a variable name we have given here. We have not specified any filters so by default all the nodes are selected under variable n. Return n outputs those nodes, hence all the nodes are outputted.
Reviewing all food items

Creating relationships
Since we have opened our restaurant in a foreign city, our guests might not be aware of what is supposed to be order with what, or what goes with what. To aid their confusion, we can add relationship 'GOES_WITH' in between food items that are generally ordered with one another. For example: -

  • Chai goes well with Samosa.
  • Any bread goes well with any curry.
  • Rice goes well with Dal or other lentils.

To create relationships between nodes, we will first query for them using MATCH clause, and then use CREATE to create a relationship.
The general syntax of querying for items satisfying a certain label and/or properties using MATCH clause is as follows: -

MATCH (variable_name:label {property_name1: "value1", property_name2: "value2"...})
RETURN variable_name
Enter fullscreen mode Exit fullscreen mode

It is important to note that only those details around which we seek to filter nodes are to be provided.

To create a relationship between 2 nodes, CREATE clause can be inserted between MATCH clause and RETURN clause: -

MATCH (variable_name1:label1 {property_name1: "value1"}), (variable_name2:label2 {property_name2: "value2"})
CREATE (variable_name1)-[:relationship_name {relationship_property:"value"}]->(variable_name2)
RETURN variable_name1, variable_name2
Enter fullscreen mode Exit fullscreen mode

To create a relationship named 'GOES_WITH' in between Chai and Samosa, the following Open Cypher query can be executed: -

MATCH (samosa:STARTER {name: "Samosa"}), (chai:BEVERAGE {name: "Chai"})
CREATE (samosa)-[:GOES_WITH]->(chai)
RETURN samosa, chai
Enter fullscreen mode Exit fullscreen mode

Samosa Chai relationship

Now, let us create a GOES_WITH relationship between all BREADs and all CURRYs. To do so, we may use the following query: -

MATCH (bread:BREAD), (curry:CURRY)
CREATE (bread)-[:GOES_WITH]->(curry)
RETURN bread, curry
Enter fullscreen mode Exit fullscreen mode

Bread-Curry goes with

What does rice go with? Rice goes with Dal. We can create a GOES_WITH relationship between rice and dal using the following query: -

MATCH (rice:RICE), (dal:CURRY {name: "Dal Makhani"})
CREATE (rice)-[:GOES_WITH]->(dal)
RETURN rice, dal
Enter fullscreen mode Exit fullscreen mode

Rice with Dal

Updating nodes
Snap! We just realized that we may have made an error. Given that Dal Makhani has butter in it, it is incorrectly marked as vegan in its properties. We need to update its 'type' property to 'vegetarian' from vegan.
To update property of a node, first we need to select the node in a variable using MATCH clause, then update its property using SET clause. The usage has been demonstrated by the following query: -

MATCH (dal:CURRY {name: "Dal Makhani"})
SET dal.type = "Vegetarian"
RETURN dal
Enter fullscreen mode Exit fullscreen mode

Updating DDal

Deleting nodes
Let's say in our further discussions, since almost all our food is vegetarian anyway, we can go for a completely vegetarian restaurant. For that, we'd be needed to delete all the 'Non Vegetarian' food items from our menu.
Again, to delete a node, first select it using MATCH clause, then we can delete it using DELETE clause. In case the node is part of some relationship, we will be required to DETACH it first from its existing relationships. So, a query to delete all the nodes of the type 'Non Vegetarian' would look something this: -

MATCH (nv {type:"Non Vegetarian"})
DETACH DELETE nv
RETURN nv
Enter fullscreen mode Exit fullscreen mode

Deleting non vegetarian

Querying data
Finally, we think our menu is prepared and solid.
Let's test our database out against some typical kinds of customers:

  • The Vegans. Lets try to output all Vegan food items on the menu using MATCH clause.
MATCH (v {type: "Vegan"})
RETURN v
Enter fullscreen mode Exit fullscreen mode

Vegan

  • Customer who wants to eat Naan but does not know what to eat it with
MATCH (n:BREAD {name: "Butter Naan"})-[GOES_WITH]-(f)
RETURN f
Enter fullscreen mode Exit fullscreen mode

Goes with naan

  • Customer who wants a Vegan Curry
MATCH (vc:CURRY {type: "Vegan"})
RETURN vc
Enter fullscreen mode Exit fullscreen mode

Vegan curry

Thank you!
Our restaurant is a great success!

All the Apache AGE queries used throughout the post are available here.

💖 💪 🙅 🚩
capnspek
Bhaskar Sharma

Posted on February 20, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related