-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Advanced query operations ; add interlinks
- Loading branch information
1 parent
f66c653
commit 3da6102
Showing
7 changed files
with
144 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
.. _Advanced query operations: | ||
|
||
========================= | ||
Advanced query operations | ||
========================= | ||
|
||
neomodel provides ways to enhance your queries beyond filtering and traversals. | ||
|
||
Annotate - Aliasing | ||
------------------- | ||
|
||
The `annotate` method allows you to add transformations to your elements. To learn more about the available transformations, keep reading this section. | ||
|
||
Aggregations | ||
------------ | ||
|
||
neomodel implements some of the aggregation methods available in Cypher: | ||
|
||
- Collect | ||
- Last | ||
|
||
These are usable in this way:: | ||
|
||
from neomodel.sync_match import Collect, Last | ||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
# distinct is optional, and defaults to False. When true, objects are deduplicated | ||
Supplier.nodes.traverse_relations(available_species="coffees__species") | ||
.annotate(Collect("available_species", distinct=True)) | ||
.all() | ||
|
||
# Last is used to get the last element of a list | ||
Supplier.nodes.traverse_relations(available_species="coffees__species") | ||
.annotate(Last(Collect("last_species"))) | ||
.all() | ||
|
||
.. note:: | ||
Using the Last() method right after a Collect() without having set an ordering will return the last element in the list as it was returned by the database. | ||
|
||
This is probably not what you want ; which means you must provide an explicit ordering. To do so, you cannot neomodel's order_by method, but need an intermediate transformation step (see below). | ||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
This is because the order_by method adds ordering as the very last step of the Cypher query ; whereas in the present example, you want to first order Species, then get the last one, and then finally return your results. In other words, you need an intermediate WITH Cypher clause. | ||
|
||
Intermediate transformations | ||
---------------------------- | ||
|
||
The `intermediate_transform` method basically allows you to add a WITH clause to your query. This is useful when you need to perform some operations on your results before returning them. | ||
|
||
As discussed in the note above, this is for example useful when you need to order your results before applying an aggregation method, like so:: | ||
|
||
from neomodel.sync_match import Collect, Last | ||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
# This will return all Coffee nodes, with their most expensive supplier | ||
Coffee.nodes.traverse_relations(suppliers="suppliers") | ||
.intermediate_transform( | ||
{"suppliers": "suppliers"}, ordering=["suppliers.delivery_cost"] | ||
) | ||
.annotate(supps=Last(Collect("suppliers"))) | ||
|
||
Subqueries | ||
---------- | ||
|
||
The `subquery` method allows you to perform a `Cypher subquery <https://neo4j.com/docs/cypher-manual/current/subqueries/call-subquery/>`_ inside your query. This allows you to perform operations in isolation to the rest of your query:: | ||
|
||
from neomodel.sync_match import Collect, Last | ||
# This will create a CALL{} subquery | ||
# And return a variable named supps usable in the rest of your query | ||
Coffee.nodes.subquery( | ||
Coffee.nodes.traverse_relations(suppliers="suppliers") | ||
.intermediate_transform( | ||
{"suppliers": "suppliers"}, ordering=["suppliers.delivery_cost"] | ||
) | ||
.annotate(supps=Last(Collect("suppliers"))), | ||
["supps"], | ||
) | ||
|
||
Helpers | ||
------- | ||
|
||
Reading the sections above, you may have noticed that we used explicit aliasing in the examples, as in:: | ||
|
||
traverse_relations(suppliers="suppliers") | ||
|
||
This allows you to reference the generated Cypher variables in your transformation steps, for example:: | ||
|
||
traverse_relations(suppliers="suppliers").annotate(Collect("suppliers")) | ||
|
||
In some cases though, it is not possible to set explicit aliases, for example when using `fetch_relations`. In these cases, neomodel provides `resolver` methods, so you do not have to guess the name of the variable in the generated Cypher. Those are `NodeNameResolver` and `RelationshipNameResolver`. For example:: | ||
|
||
from neomodel.sync_match import Collect, NodeNameResolver, RelationshipNameResolver | ||
|
||
Supplier.nodes.fetch_relations("coffees__species") | ||
.annotate( | ||
all_species=Collect(NodeNameResolver("coffees__species"), distinct=True), | ||
all_species_rels=Collect( | ||
RelationNameResolver("coffees__species"), distinct=True | ||
), | ||
) | ||
.all() | ||
|
||
.. note:: | ||
|
||
When using the resolvers in combination with a traversal as in the example above, it will resolve the variable name of the last element in the traversal - the Species node for NodeNameResolver, and Coffee--Species relationship for RelationshipNameResolver. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
neomodel.sync_.match