How to Use GQLAlchemy Query Builder?

katelatte

Katarina Supe

Posted on May 24, 2023

How to Use GQLAlchemy Query Builder?

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.

Set everything up

If you want to try the code from this blog post, make sure to install
Docker and
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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Hint: You may need to install CMake before
installing GQLAlchemy.

from gqlalchemy import Memgraph

memgraph = Memgraph("127.0.0.1", 7687)
Enter fullscreen mode Exit fullscreen mode

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.

memgraph.drop_database()
memgraph.drop_indexes()
Enter fullscreen mode Exit fullscreen mode

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.

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
Enter fullscreen mode Exit fullscreen mode

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:

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)
Enter fullscreen mode Exit fullscreen mode

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'}>}
Enter fullscreen mode Exit fullscreen mode

Next, create a couple of movies:

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)
Enter fullscreen mode Exit fullscreen mode

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'}>}
Enter fullscreen mode Exit fullscreen mode

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

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()
Enter fullscreen mode Exit fullscreen mode

To check whether the data is correctly imported into Memgraph, you can open
Memgraph Lab on localhost:3000.
In the query editor, run:

MATCH (u)-[r]->(m)
RETURN u, r, m;
Enter fullscreen mode Exit fullscreen mode

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

memgraph-lab-visualization

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.

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"])
Enter fullscreen mode Exit fullscreen mode

Output:

Maria's id: 2
LOTR id: 0
Enter fullscreen mode Exit fullscreen mode

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.

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"])
Enter fullscreen mode Exit fullscreen mode

Output:

Ron
John
Ian
Enter fullscreen mode Exit fullscreen mode

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:

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"])
Enter fullscreen mode Exit fullscreen mode

Output:

Ron
John
Jenny
Ian
Enter fullscreen mode Exit fullscreen mode

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.

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

for result in results:
    print(result)
Enter fullscreen mode Exit fullscreen mode

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'}>}
Enter fullscreen mode Exit fullscreen mode

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.

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"])
Enter fullscreen mode Exit fullscreen mode

Output:

LOTR 0.26900584795321636
Hobbit 0.1
Matrix 0.1
Home Alone 0.09005847953216374
Star Wars 0.09005847953216374
Enter fullscreen mode Exit fullscreen mode

Delete and remove objects

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

match().node(labels="User", id=4, variable="u").delete(variable_expressions=["u"], detach=True).execute()
Enter fullscreen mode Exit fullscreen mode

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:

match().node(labels="User", id=0).to(edge_label="RATED", variable="r").node(labels="Movie", id=0).delete(variable_expressions=["r"]).execute()
Enter fullscreen mode Exit fullscreen mode

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:

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

for result in results:
    print(result)
Enter fullscreen mode Exit fullscreen mode

Output:

{'u': <User id=2 labels={'User'} properties={'id': 2, 'name': None}>}
Enter fullscreen mode Exit fullscreen mode

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
for some cool use cases and examples. If you need some help with GQLAlchemy,
join our Discord server and have a chat with
us.

Read more about Python on memgraph.com

๐Ÿ’– ๐Ÿ’ช ๐Ÿ™… ๐Ÿšฉ
katelatte
Katarina Supe

Posted on May 24, 2023

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

Sign up to receive the latest update from our blog.

Related

ยฉ TheLazy.dev

About