Graph Databases Introduction: An Indian Restaurant
Tags: graphs, opencypher, apacheage, postgres
Published: 2023-02-20 02:02:57

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3g8v3ipvyfyvn40yhs76.jpg)

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);
```
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);
```
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](https://pastebin.com/MXDxRM9K) (along with all other queries used throughout this tutorial).

![Adding all food items](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j05rlfao3axbw1ttokh7.png)

**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
```
`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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fqmd5xslqtb4ylr4xvio.png)

**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
```
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
```

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
```

![Samosa Chai relationship](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9n7msx5azi4d7ayutbvh.png)

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
```

![Bread-Curry goes with](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h79f4nlq58tdjwhnjgi.png)

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
```

![Rice with Dal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tvfcveojtd8txrqdhagv.png)


**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
```

![Updating DDal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lztrjwvh72gsto99s4p9.png)



**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
```

![Deleting non vegetarian](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bi32se5qy5eggasztrqb.png)

**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
```

![Vegan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6h8ea3ke3rq9dq2se4bp.png)

- 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
```

![Goes with naan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jcv9a16lrscanobkzah3.png)



- Customer who wants a Vegan Curry

```
MATCH (vc:CURRY {type: "Vegan"})
RETURN vc
```

![Vegan curry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snmio86npohfay1ogd0l.png)

Thank you!
Our restaurant is a great success!

All the Apache AGE queries used throughout the post are available [here](https://pastebin.com/MXDxRM9K).