How to Use GQLAlchemy Query Builder?
Published: 2023-05-24 13:59:18

**GQLAlchemy query builder** will help you build Cypher queries in no time! With
this blog post, you will learn how to use different query builder methods to
create, change, get, set, and remove data from Memgraph.

You can find the original Jupyter Notebook in our open-source [GitHub repository](https://github.com/memgraph/jupyter-memgraph-tutorials).

## Set everything up

If you want to try the code from this blog post, make sure to install
[Docker](https://docs.docker.com/get-docker/) and
[GQLAlchemy](https://pypi.org/project/gqlalchemy/). Docker is used because
**Memgraph** is a native Linux application and can't be installed on Windows and
macOS. After you install Docker, you can set up Memgraph by running:

```
docker run -it -p 7687:7687 -p 7444:7444 -p 3000:3000 memgraph/memgraph-platform:2.2.6
```

This command will start the download and after it finishes, run the Memgraph
container.

We will be using the **GQLAlchemy** object graph mapper (OGM) to connect to
Memgraph and execute **Cypher** queries easily. GQLAlchemy also serves as a
Python driver/client for Memgraph. You can install it using:

```
pip install gqlalchemy==1.2.0
```

> **Hint**: You may need to install [CMake](https://cmake.org/download/) before
> installing GQLAlchemy.

```python
from gqlalchemy import Memgraph

memgraph = Memgraph("127.0.0.1", 7687)
```

Make sure that Memgraph is empty before starting with anything else. Also, you
should drop all indexes, if there are any, since you'll be creating new ones.

```python
memgraph.drop_database()
memgraph.drop_indexes()
```

## Define graph schema

You will create **Python classes** that will represent the graph schema. This
way, all the objects that are returned from Memgraph will be of the correct type
if the class definition can be found. That is not a part of the query builder,
but it will help you work with the nodes and relationships.

```python
from typing import Optional
from gqlalchemy import Node, Relationship, Field

class Movie(Node):
    id: int = Field(index=True, unique=True, exists=True, db=memgraph)
    title: Optional[str]

class User(Node):
    id: int = Field(index=True, unique=True, exists=True, db=memgraph)
    name: Optional[str]

class Rated(Relationship, type="RATED"):
    rating: float
```

## Create nodes and relationships

To return all the variables from a query, just use the `return_()` method at the
end of your query. Don't forget to `execute()` each query. First, you can create
some users in the database:

```python
from gqlalchemy import create


ron = next(create().node(labels="User", id=0, name="Ron", variable="ron").return_().execute())
john = next(create().node(labels="User", id=1, name="John", variable="john").return_().execute())
maria = next(create().node(labels="User", id=2, name="Maria", variable="maria").return_().execute())
jenny = next(create().node(labels="User", id=3, name="Jenny", variable="jenny").return_().execute())
ian = next(create().node(labels="User", id=4, name="Ian", variable="ian").return_().execute())

users = [ron, john, maria, jenny, ian]

for user in users:
    print(user)
```

Output: 
```
{'ron': <User id=0 labels={'User'} properties={'id': 0, 'name': 'Ron'}>}
{'john': <User id=1 labels={'User'} properties={'id': 1, 'name': 'John'}>}
{'maria': <User id=2 labels={'User'} properties={'id': 2, 'name': 'Maria'}>}
{'jenny': <User id=3 labels={'User'} properties={'id': 3, 'name': 'Jenny'}>}
{'ian': <User id=4 labels={'User'} properties={'id': 4, 'name': 'Ian'}>}
```

Next, create a couple of movies:

```python
lotr = next(create().node(labels="Movie", id=0, title="LOTR", variable="lotr").return_().execute())
home_alone = next(create().node(labels="Movie", id=1, title="Home Alone", variable="home_alone").return_().execute())
star_wars = next(create().node(labels="Movie", id=2, title="Star Wars", variable="star_wars").return_().execute())
hobbit = next(create().node(labels="Movie", id=3, title="Hobbit", variable="hobbit").return_().execute())
matrix = next(create().node(labels="Movie", id=4, title="Matrix", variable="matrix").return_().execute())

movies = [lotr, home_alone, star_wars, hobbit, matrix]
for movie in movies:
    print(movie)
```

Output: 
```
{'lotr': <Movie id=5 labels={'Movie'} properties={'id': 0, 'title': 'LOTR'}>}
{'home_alone': <Movie id=6 labels={'Movie'} properties={'id': 1, 'title': 'Home Alone'}>}
{'star_wars': <Movie id=7 labels={'Movie'} properties={'id': 2, 'title': 'Star Wars'}>}
{'hobbit': <Movie id=8 labels={'Movie'} properties={'id': 3, 'title': 'Hobbit'}>}
{'matrix': <Movie id=9 labels={'Movie'} properties={'id': 4, 'title': 'Matrix'}>}
```

Now you need to connect users and movies with the `RATED` relationship:

```python
from gqlalchemy import match

match().node(labels="User", id=0, variable="u").match().node(labels="Movie", id=0, variable="m").create().node(variable="u").to(edge_label="RATED", rating=5.0).node(variable="m").execute()
match().node(labels="User", id=0, variable="u").match().node(labels="Movie", id=1, variable="m").create().node(variable="u").to(edge_label="RATED", rating=3.2).node(variable="m").execute()
match().node(labels="User", id=0, variable="u").match().node(labels="Movie", id=2, variable="m").create().node(variable="u").to(edge_label="RATED", rating=4.3).node(variable="m").execute()
match().node(labels="User", id=1, variable="u").match().node(labels="Movie", id=0, variable="m").create().node(variable="u").to(edge_label="RATED", rating=5.0).node(variable="m").execute()
match().node(labels="User", id=2, variable="u").match().node(labels="Movie", id=0, variable="m").create().node(variable="u").to(edge_label="RATED", rating=4.9).node(variable="m").execute()
match().node(labels="User", id=3, variable="u").match().node(labels="Movie", id=3, variable="m").create().node(variable="u").to(edge_label="RATED", rating=2.3).node(variable="m").execute()
match().node(labels="User", id=3, variable="u").match().node(labels="Movie", id=4, variable="m").create().node(variable="u").to(edge_label="RATED", rating=4.8).node(variable="m").execute()
match().node(labels="User", id=4, variable="u").match().node(labels="Movie", id=0, variable="m").create().node(variable="u").to(edge_label="RATED", rating=4.0).node(variable="m").execute()
```

To check whether the data is correctly imported into Memgraph, you can open
[**Memgraph Lab**](https://memgraph.com/docs/memgraph-lab/) on `localhost:3000`.
In the query editor, run:

```
MATCH (u)-[r]->(m)
RETURN u, r, m;
```

You'll see the imported data visualized as a graph below:

![memgraph-lab-visualization](https://raw.githubusercontent.com/memgraph/jupyter-memgraph-tutorials/main/gqlalchemy/query-builder/memgraph-lab-visualization.png)

## Filter results

You can **filter data by comparing the property of a graph object to a value**
(a literal). To return only a subset of variables from a query, specify them in
the `return()` method. Below you can see how to compare `name` and `title`
properties of nodes to a string value.

```python
results = list(match()
            .node(labels="User", variable="u")
            .to(edge_label="RATED")
            .node(labels="Movie", variable="m")
            .where(item="u.name", operator="=", literal="Maria")
            .and_where(item="m.title", operator="=", literal="LOTR")
            .return_({"u.id": "user_id", "m.id": "movie_id"})
            .execute()
        )

for result in results:
    print("Maria's id:", result["user_id"])
    print("LOTR id:", result["movie_id"])
```

Output: 

```
Maria's id: 2
LOTR id: 0
```

You filtered the data by finding a user with `name` Maria, who rated the movie
with `title` LOTR.

You can also negate the expression in `WHERE` clause, using the `where_not()`
method. Similarly, you have `and_not_where()`, `or_not_where()` and
`xor_not_where()` methods.

```python
results = list(match()
            .node(labels="User", variable="u")
            .to(edge_label="RATED")
            .node(labels="Movie", variable="m")
            .where_not(item="u.name", operator="=", literal="Maria")
            .and_where(item="m.title", operator="=", literal="LOTR")
            .return_({"u.name": "user_name"})
            .execute()
        )

for result in results:
    print(result["user_name"])
```

Output: 

```
Ron
John
Ian
```

What happened above? You filtered the data by finding the names of all users who
are not Maria and who rated the movie LOTR.

You can also **filter data by comparing properties** of graph objects:

```python
results = list(match()
            .node(labels="User", name="Maria", variable="u")
            .match()
            .node(labels="User", variable="v")
            .where(item="u.name", operator="!=", expression="v.name")
            .return_({"v.name": "user_name"})
            .execute()
        )

for result in results:
    print(result["user_name"])
```

Output: 

```
Ron
John
Jenny
Ian
```

You found all the users that don't have the property `name` set to `Maria`.

Nodes can also be **filtered by their label** using the `where()` method instead
of specifying it directly in the `node()` method of the query builder. You have
to use `expression` as the third keyword argument since you want to escape the
quotes surrounding the label in the Cypher clause.

```python
results = list(match()
            .node(variable="u")
            .where(item="u", operator=":", expression="Movie")
            .return_()
            .execute()
        )

for result in results:
    print(result)
```

Output: 

```
{'u': <Movie id=5 labels={'Movie'} properties={'id': 0, 'title': 'LOTR'}>}
{'u': <Movie id=6 labels={'Movie'} properties={'id': 1, 'title': 'Home Alone'}>}
{'u': <Movie id=7 labels={'Movie'} properties={'id': 2, 'title': 'Star Wars'}>}
{'u': <Movie id=8 labels={'Movie'} properties={'id': 3, 'title': 'Hobbit'}>}
{'u': <Movie id=9 labels={'Movie'} properties={'id': 4, 'title': 'Matrix'}>}
```

Above you filtered all nodes in the database by the `Movie` label.

## Call procedures

Let's see which movie is the most important in the network. You can suppose that
the most important movie will be the one that the largest number of users rated.
To order the returned results, use the `order_by()` method after the `return_()`
method.

```python
from gqlalchemy import call
from gqlalchemy.query_builder import Order

results = list(call("pagerank.get")
            .yield_()
            .with_({"node": "node", "rank": "rank"})
            .where(item="node", operator=":", expression="Movie")
            .return_({"node.title": "movie_title", "rank": "rank"})
            .order_by(properties=("rank", Order.DESC))
            .execute()
        )

for result in results:
    print(result["movie_title"], result["rank"])
```

Output: 

```
LOTR 0.26900584795321636
Hobbit 0.1
Matrix 0.1
Home Alone 0.09005847953216374
Star Wars 0.09005847953216374
```

## Delete and remove objects

To **delete a node** from the database, use the `delete()` method:

```python
match().node(labels="User", id=4, variable="u").delete(variable_expressions=["u"], detach=True).execute()
```

You deleted Ian from the database and the relationships he was connected with.

To **delete a relationship** from the database, again use the `delete()` method:

```python
match().node(labels="User", id=0).to(edge_label="RATED", variable="r").node(labels="Movie", id=0).delete(variable_expressions=["r"]).execute()
```

Above you deleted the relationship between Ron and LOTR, that is, it seems like
Ron hasn't rated LOTR.

To remove a property from the database, use the `remove()` method:

```python
results = list(match()
            .node(labels="User", name="Maria", variable="u")
            .remove(items=["u.name"])
            .return_()
            .execute()
        )

for result in results:
    print(result)
```

Output: 

```
{'u': <User id=2 labels={'User'} properties={'id': 2, 'name': None}>}
```

Now Maria's `name` is set to `None`, so you can only recognize her by the `id`
in the database.

## What's next?

Now it's time for you to use Memgraph on a graph problem!

You can always check out [Memgraph Playground](https://playground.memgraph.com/)
for some cool use cases and examples. If you need some help with GQLAlchemy,
join our [Discord](https://www.discord.gg/memgraph) server and have a chat with
us.

[![Read more about Python on memgraph.com](https://public-assets.memgraph.com/external/memgraph-read-more-gradient-1200.png)](https://memgraph.com/blog?topics=Python&utm_source=devto&utm_medium=referral&utm_campaign=blog_repost&utm_content=banner#list)