Skip to main content

How to use query builder

Through this guide, you will learn how to use GQLAlchemy query builder to:

Hopefully, this guide will teach you how to properly use GQLAlchemy query builder. If you have any more questions, join our community and ping us on Discord.

info

To test the above features, you must install GQLAlchemy and have a running Memgraph instance. If you're unsure how to run Memgraph, check out the Memgraph Quick start.

Create nodes and relationships

Methods create(), merge(), match(), node(), to() and from_() are most often used when building a query to create or merge nodes and relationships.

Create a node

To create a node with label Person and a property name of value "Ron", run the following code:

from gqlalchemy import create

query = create().node(labels="Person", name="Ron").execute()

Create a relationship

To create a relationship of type FRIENDS_WITH with property since from one Person node to another, run the following code:

from gqlalchemy import create

query = (
create()
.node(labels="Person", name="Leslie")
.to(relationship_type="FRIENDS_WITH", since="2023-02-16")
.node(labels="Person", name="Ron")
.execute()
)

Since you are creating a relationship between two nodes, without first matching the existing nodes or merging the relationships, the nodes will be created too.

To create a relationship of type FRIENDS_WITH from one Person node to another in an opposite direction, run the following code:

from gqlalchemy import create

query = (
create()
.node(labels="Person", name="Leslie")
.from(relationship_type="FRIENDS_WITH")
.node(labels="Person", name="Ron")
.execute()
)

Again, since you are creating a relationship between two nodes, without first matching the existing nodes or merging the relationships, the nodes will be created too.

To create a relationship between existing nodes, first match the existing nodes and then create a relationship by running the following code:

from gqlalchemy import create, match

query = (
match()
.node(labels="Person", name="Leslie", variable="leslie")
.match()
.node(labels="Person", name="Ron", variable="ron")
create()
.node(variable="leslie")
.to(relationship_type="FRIENDS_WITH")
.node(variable="ron")
.execute()
)

Read more about CREATE clause in the Cypher manual.

Merge nodes and relationships

Merge a node

To merge a node, run the following code:

from gqlalchemy import merge

query = merge().node(labels="Person", name="Leslie").execute()

Merge a relationship

To merge a relationship, first match the existing nodes and then merge the relationship by running the following code:

from gqlalchemy import match, merge

query = (
match()
.node(labels="Person", name="Leslie", variable="leslie")
.match()
.node(labels="Person", name="Ron", variable="ron")
.merge()
.node(variable="leslie")
.to(relationship_type="FRIENDS_WITH")
.node(variable="ron")
.execute()
)

Read more about MERGE clause in the Cypher manual.

Set or update properties and labels

The set_() method is used to set labels on nodes, and properties on nodes and relationships. When being set, labels and properties can be updated or created, depending on the operator used as the argument of set_() method.

Set a property

To set a property of a graph object use the assignment operator from the query builder or a simple equals sign as a string - "=".

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = (
create()
.node(labels="Country", variable="c", name="Germany")
.set_(item="c.population", operator=Operator.ASSIGNMENT, literal=83000001)
.execute()
)
info

Operator is an enumeration class defined in the declarative_base.py. It can be imported from gqlalchemy.query_builders.memgraph_query_builder.

If you don't want to import it, you can use strings "=", ">=", ">", "<>", ":", "<", "<=", "!=" or "+=" instead.

To set a property of already existing node, first match the node and then set its property.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = (
match()
.node(labels="Country", variable="c", name="Germany")
.set_(item="c.population", operator=Operator.ASSIGNMENT, literal=10000)
.execute()
)

To set multiple properties of a node, run the following code:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = (
match()
.node(variable="n")
.where(item="n.name", operator="=", literal="Germany")
.set_(item="n.population", operator=Operator.ASSIGNMENT, literal=83000001)
.set_(item="n.capital", operator=Operator.ASSIGNMENT, literal="Berlin")
.execute()
)

If a node already has the properties we are setting, they will be updated to a new value. Otherwise, the properties will be created and their value will be set.

Set a label

To set a label of a node, run the following code:

from gqlalchemy import Match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = Match()
.node(variable="c", name="Germany")
.set_(item="c", operator=Operator.LABEL_FILTER, expression="Land")
.return_()
.execute()

If a node already has a label, then it will have both old and new label.

Replace all properties

With Cypher, it is possible to replace all properties using a map within a SET clause. Here is how to do it with query builder:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = (
match()
.node(variable="c", labels="Country")
.where(item="c.name", operator="=", literal="Germany")
.set_(
item="c",
operator=Operator.ASSIGNMENT,
literal={"country_name": "Germany", "population": 85000000},
)
.execute()
)

The properties that are not a part of the graph objects, but are in the map, will be set. The properties that are not in the map, but are a part of the graph objects, will be removed. If a property is both in map and a graph object property, it will be updated to a new value set in map.

Update all properties

With Cypher, it is also possible to update all properties using a map within a SET clause by using the increment operator (+=). Here is how to do it with query builder:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

query = (
match()
.node(variable="c", labels="Country")
.where(item="c.country_name", operator="=", literal="Germany")
.set_(
item="c",
operator=Operator.INCREMENT,
literal={"population": "85000000"},
)
.execute()
)

All the properties in the map (value of the literal argument) that are on a graph object will be updated. The properties that are not on a graph object but are in the map will be added. Properties that are not present in the map will be left as is.

Filter data

You can use the methods where(), where_not(), or_where(), or_where_not(), and_where(), and_where_not(), xor_where() and xor_where_not() to construct queries that will filter data.

Filter data by property comparison

To filter data by comparing properties of two nodes, run the following code:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where(item="p1.name", operator=Operator.LESS_THAN, expression="p2.name")
.return_()
.execute()
)

print(results)

Keyword arguments that can be used in filtering methods are literal and expression. Usually we use literal for property values and expression for property names and labels. That is because property names and labels shouldn't be quoted in Cypher statements.

info

You will probably see the GQLAlchemySubclassNotFoundWarning warning. This happens if you did not define a Python class which maps to a graph object in the database. To do that, check the object graph mapper how-to guide. To ignore such warnings, you can do the following before query execution:

from gqlalchemy import models

models.IGNORE_SUBCLASSNOTFOUNDWARNING = True

Standard boolean operators like NOT, AND, OR and XOR are used in the Cypher query language. To have NOT within WHERE clause, you need to use where_not() method.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where_not(item="p1.name", operator=Operator.LESS_THAN, expression="p2.name")
.return_()
.execute()
)

print(results)

In a similar way, you can use AND and AND NOT clauses which correspond to the methods and_where() and and_not_where(). Using the query below you can find all persons with the same address and last_name, but different name.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where(item="p1.address", operator=Operator.EQUAL, expression="p2.address")
.and_where(item="p1.last_name", operator=Operator.EQUAL, expression="p2.last_name")
.and_not_where(item="p1.name", operator=Operator.EQUAL, expression="p2.name")
.return_()
.execute()
)

print(results)

The same goes for the OR, OR NOT, XOR and XOR NOT clauses, which correspond to the methods or_where(), or_not_where(), xor_where() and xor_not_where().

Filter data by property value

You can filter data by comparing the property of a graph object to some value (a literal). Below you can see how to compare age property of a node to the integer.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(labels="Person", variable="p")
.where(item="p.age", operator=Operator.GREATER_THAN, literal=18)
.return_()
.execute()
)

The third keyword argument is literal since we wanted the property age to be saved as an integer. If we used expression keyword argument instead of literal, then the age property would be a string (it would be quoted in Cypher query). Instead of Operator.GREATER_THAN, a simple string of value ">" can be used.

Just like in property comparison, it is possible to use different boolean operators to further filter the data.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(labels="Person", variable="p")
.where(item="p.age", operator=Operator.GREATER_THAN, literal=18)
.or_where(item="p.name", operator=Operator.EQUAL, literal="John")
.return_()
.execute()
)

The literal keyword is used again since you want John to be quoted in the Cypher query (to be saved as a string in the database).

Filter data by label

Nodes can be filtered by their label using the WHERE clause instead of specifying it directly in the MATCH clause. You have to use expression as the third keyword argument again since you don't want the quotes surrounding the label in the Cypher clause.

To filter data by label use the following code:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Operator

results = list(
match()
.node(variable="p")
.where(item="p", operator=Operator.LABEL_FILTER, expression="Person")
.return_()
.execute()
)

print(results)

Just like in property comparison, it is possible to use different boolean operators to further filter the data.

Return results

You can use the methods return_(), limit(), skip() and order_by() to construct queries that will return data from the database.

Return all variables from a query

To return all the variables from a query, use the return_() method at the end of the query:

from gqlalchemy import match

results = list(match().node(labels="Person", variable="p").return_().execute())
print(results)

Return specific variables from a query

To return only a subset of variables from a query, specify them in the return_() method:

from gqlalchemy import match

results = list(
match()
.node(labels="Person", variable="p1")
.to()
.node(labels="Person", variable="p2")
.return_(results=[("p1", "first"), "p2"])
.execute()
)

for result in results:
print("Here is one pair:")
print(result["first"])
print(result["p2"])

Limit the number of returned results

To limit the number of returned results, use the limit() method after the return_() method:

from gqlalchemy import match

results = list(match().node(labels="Person", variable="p").return_().limit(3).execute())
print(results)

Order the returned results

The default ordering in the Cypher query language is ascending (ASC or ASCENDING), and if you want the descending order, you need to add the DESC or DESCENDING keyword to the ORDER BY clause.

To order the return results by one value, use the order_by(properties) method, where properties can be a string (a property) or a tuple of two strings (a property and an order).

The following query will order the results in an ascending (default) order by the property name of a node.

from gqlalchemy import match

results = list(
match().node(variable="n").return_().order_by(properties="n.name").execute()
)
print(results)

You can also emphasize that you want an ascending order:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Order

results = list(
match()
.node(variable="n")
.return_()
.order_by(properties=("n.name", Order.ASC))
.execute()
)
print(results)

The same can be done with the keyword ASCENDING:

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Order

results = list(
match()
.node(variable="n")
.return_()
.order_by(properties=("n.name", Order.ASCENDING))
.execute()
)
print(results)
info

Order is an enumeration class defined in the declarative_base.py. It can be imported from gqlalchemy.query_builders.memgraph_query_builder.

If you don't want to import it, you can use strings "ASC", "ASCENDING", "DESC" or "DESCENDING" instead.

To order the query results in descending order, you need to specify the DESC or DESCENDING keyword. Hence, the argument of the order_by() method must be a tuple.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Order

results = list(
match()
.node(variable="n")
.return_()
.order_by(properties=("n.name", Order.DESC))
.execute()
)

print(results)

Similarly, you can use Order.DESCENDING to get DESCENDING keyword in ORDER BY clause.

Order by a list of values

To order the returned results by more than one value, use the order_by(properties) method, where properties can be a list of strings or tuples of strings (list of properties with or without order).

The following query will order the results in ascending order by the property id, then again in ascending (default) order by the property name of a node. After that, it will order the results in descending order by the property last_name, then in ascending order by the property age of a node. Lastly, the query will order the results in descending order by the node property middle_name.

from gqlalchemy import match
from gqlalchemy.query_builders.memgraph_query_builder import Order

results = list(
match()
.node(variable="n")
.return_()
.order_by(
properties=[
("n.id", Order.ASC),
"n.name",
("n.last_name", Order.DESC),
("n.age", Order.ASCENDING),
("n.middle_name", Order.DESCENDING),
]
)
.execute()
)

print(results)

Delete and remove objects

You can use the methods delete() and remove() to construct queries that will remove nodes and relationships or properties and labels.

Delete a node

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

from gqlalchemy import match

match().node(labels="Person", name="Harry", variable="p").delete(
variable_expressions="p"
).execute()

Delete a relationship

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

from gqlalchemy import match

match().node(labels="Person", name="Leslie").to(
relationship_type="FRIENDS_WITH", variable="f"
).node(labels="Person").delete(variable_expressions="f").execute()

Remove properties

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

from gqlalchemy import match

match().node(labels="Person", name="Jane", variable="p").remove(
items=["p.name", "p.last_name"]
).execute()

Call procedures

You can use the methods call() and yield_() to construct queries that will call procedure and return results from them.

Call procedure with no arguments

To call a procedure with no arguments, don't specify the arguments in the call() method:

from gqlalchemy import call

results = list(call("pagerank.get").yield_().return_().execute())
print(results)

Call procedure with arguments

To call a procedure with arguments, specify the arguments as a string in the call() method:

from gqlalchemy import call

results = list(
call(
"json_util.load_from_url",
"'https://download.memgraph.com/asset/mage/data.json'",
)
.yield_("objects")
.return_(results="objects")
.execute()
)

print("Load from URL with argument:", results, "\n")

Full code example

from gqlalchemy import create, merge, Memgraph, match, models, call
from gqlalchemy.query_builders.memgraph_query_builder import Operator, Order


db = Memgraph()
# clean database
db.drop_database()

# create nodes and a relationship between them

create().node(labels="Person", name="Leslie").to(relationship_type="FRIENDS_WITH").node(
labels="Person", name="Ron"
).execute()


# merge a node
merge().node(labels="Person", name="Leslie").execute()

# create nodes and a relationship between them
create().node(
labels="Person", name="Jane", last_name="James", address="street", age=19
).from_(relationship_type="FRIENDS_WITH", since="2023-02-16").node(
labels="Person", name="John", last_name="James", address="street", age=8
).execute()


# merge a relationship between existing nodes

match().node(labels="Person", name="Leslie", variable="leslie").match().node(
labels="Person", name="Ron", variable="ron"
).merge().node(variable="leslie").to(relationship_type="FRIENDS_WITH").node(
variable="ron"
).execute()


# set a property
create().node(labels="Country", variable="c", name="Germany").set_(
item="c.population", operator=Operator.ASSIGNMENT, literal=83000001
).execute()

# update a property
match().node(labels="Country", variable="c", name="Germany").set_(
item="c.population", operator=Operator.ASSIGNMENT, literal=10000
).execute()


# update multiple properties
match().node(variable="n").where(item="n.name", operator="=", literal="Germany").set_(
item="n.population", operator=Operator.ASSIGNMENT, literal=83000001
).set_(item="n.capital", operator=Operator.ASSIGNMENT, literal="Berlin").execute()


# replace all properties
match().node(variable="c", labels="Country").where(
item="c.name", operator="=", literal="Germany"
).set_(
item="c",
operator=Operator.ASSIGNMENT,
literal={"country_name": "Germany", "population": 85000000},
).execute()


# update multiple properties

match().node(variable="c", labels="Country").where(
item="c.country_name", operator="=", literal="Germany"
).set_(
item="c",
operator=Operator.INCREMENT,
literal={"population": "85000000"},
).execute()


models.IGNORE_SUBCLASSNOTFOUNDWARNING = True

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where(item="p1.name", operator=Operator.LESS_THAN, expression="p2.name")
.return_()
.execute()
)

print("Filter by property comparison:", results, "\n")

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where_not(item="p1.name", operator=Operator.LESS_THAN, expression="p2.name")
.return_()
.execute()
)

print("Filter by property comparison (negation):", results, "\n")

results = list(
match()
.node(labels="Person", variable="p1")
.to(relationship_type="FRIENDS_WITH")
.node(labels="Person", variable="p2")
.where(item="p1.address", operator=Operator.EQUAL, expression="p2.address")
.and_where(item="p1.last_name", operator=Operator.EQUAL, expression="p2.last_name")
.and_not_where(item="p1.name", operator=Operator.EQUAL, expression="p2.name")
.return_()
.execute()
)

print("Filter by property comparison + logical operators:", results, "\n")

results = list(
match()
.node(labels="Person", variable="p")
.where(item="p.age", operator=Operator.GREATER_THAN, literal=18)
.return_()
.execute()
)

print("Filter by property value:", results, "\n")

results = list(
match()
.node(labels="Person", variable="p")
.where(item="p.age", operator=Operator.GREATER_THAN, literal=18)
.or_where(item="p.name", operator=Operator.EQUAL, literal="John")
.return_()
.execute()
)

print("Filter by property value + logical operators:", results, "\n")

results = list(
match()
.node(variable="p")
.where(item="p", operator=Operator.LABEL_FILTER, expression="Person")
.return_()
.execute()
)

print("Filter by label:", results, "\n")


results = list(match().node(labels="Person", variable="p").return_().execute())
print("Return all:", results, "\n")

results = list(
match()
.node(labels="Person", variable="p1")
.to()
.node(labels="Person", variable="p2")
.return_(results=[("p1", "first"), "p2"])
.execute()
)

for result in results:
print("Here is one pair:")
print(result["first"])
print(result["p2"])

print()

results = list(match().node(labels="Person", variable="p").return_().limit(3).execute())
print("Limit results:", results, "\n")


results = list(
match().node(variable="n").return_().order_by(properties="n.name").execute()
)
print("Order descending:", results, "\n")

results = list(
match()
.node(variable="n")
.return_()
.order_by(properties=("n.name", Order.ASCENDING))
.execute()
)
print("Order ascending:", results, "\n")

results = list(
match()
.node(variable="n")
.return_()
.order_by(properties=("n.name", Order.DESC))
.execute()
)

print("Order descending with ordering:", results, "\n")

results = list(
match()
.node(variable="n")
.return_()
.order_by(
properties=[
("n.id", Order.ASC),
"n.name",
("n.last_name", Order.DESC),
("n.age", Order.ASCENDING),
("n.middle_name", Order.DESCENDING),
]
)
.execute()
)

print("Mix of ordering:", results, "\n")


# create a node to delete
create().node(labels="Person", name="Harry").execute()

# delete a node
match().node(labels="Person", name="Harry", variable="p").delete(
variable_expressions="p"
).execute()

# delete a relationship between Leslie and her friends
match().node(labels="Person", name="Leslie").to(
relationship_type="FRIENDS_WITH", variable="f"
).node(labels="Person").delete(variable_expressions="f").execute()

# remove name and last_name properties from Jane
match().node(labels="Person", name="Jane", variable="p").remove(
items=["p.name", "p.last_name"]
).execute()

# calculate PageRank
results = list(call("pagerank.get").yield_().return_().execute())
print("PageRank:", results, "\n")

# Load JSON from URL with arguments
results = list(
call(
"json_util.load_from_url",
"'https://download.memgraph.com/asset/mage/data.json'",
)
.yield_("objects")
.return_(results="objects")
.execute()
)

print("Load from URL with argument:", results, "\n")

Hopefully, this guide has taught you how to properly use GQLAlchemy query builder. If you have any more questions, join our community and ping us on Discord.