-
Notifications
You must be signed in to change notification settings - Fork 4
Querying
This library introduces a new way to query which can be unfamiliar to C# developers, specially when ef core
is used before. This library implements Eloquent
similar to Laravel
to perform queries with some tweaks to adopt the idea to C# and querying a graph database such as neo4j.
Any queries in Neo4j must start with a match. The Match()
method under a NodeSet<>
is exactly that.
graphContext.Movies.Match();
//OR
graphContext.Movies.Match(x => x.Where(y => y.Id, "<some-id>"));
The code above will be immediately translated to it's equivalent cypher code.
MATCH(l0:Movie)
//OR
MATCH(l0:Movie WHERE l0.Id = '<some-id>')
But this still needs some work. You can run the above code as it is to get your result by calling ToListAsync
or FirstOrDefaultAsync
, or you can extend your query. Let's look a bit closer to the NodeQuery
object which is returned by calling the Match
method.
This filtering method is used on each match to filter out the matching nodes. The Eloquent is translated to a Where
clause and is embedded inside the MATCH
statement in cypher. Let's do a more advanced search on just the movie node.
graphContext.Movies
.Match(x =>
x
.Where(y => y.Year, ComparisonOperator.LessThan, 2000)
.WhereNotIn(y => y.Id, [20, 16, 17])
)
The above code will be translated to
MATCH (l0:Movie WHERE l0.Year < 2000 AND NOT l0.Id IN [20, 16, 17])
Now this filtering can be also applied to a target node which we are matching the movie with. For example:
graphContext.Movies
.Match(
x => x
.Where(y => y.Year, ComparisonOperator.LessThan, 2000)
.WhereNotIn(y => y.Id, [20, 16, 17])
)
.WithRelation(x => x.Audiences,
x => x
.Where(y => y.Age, ComparisonOperator.GreaterThan, 18)
)
Will be translated to
MATCH(l0:Movie WHERE l0.Year < 2000 AND NOT l0.Id IN [20, 16, 17])<-[r1:WATCHES]-(l1:Person WHERE l1.Age > 18)
The query above means, give me all Movies, created before year 2000
which their ids is not in the given list
, also the movie must be watched by Audiences
older than 18
years old.
Before you execute your query, you may still need to get the nodes which have a specific path. In this case, the NodeQuery
will provide the WithRelation
method.
This method can be used without and Eloquent
or you can filter out the nodes which are being reached out through this relation. When the WithRelation
method is called, it will look into the NodeConfiguration
, to get the Label
for this relation and if it is not defined, it will throw an exception.
The NodeQuery
at the moment only support a limited number of querying actions and all the queries will return the values for the node type the match is started with.
For example, when the query looks like the following, it will match all the movies with the given path and will return the count of the movies.
MATCH(l0:Movie WHERE l0.Year < 2000 AND NOT l0.Id IN [20, 16, 17])<-[r1:WATCHES]-(l1:Person WHERE l1.Age > 18)
This method will check against the database if the returning query result is empty or not. Please note that the query will run with executing the query and the calculated result will return from database.
This method as explained in the above example will return the count of distincted found nodes.
This method will return the first found node based on the given path and will map it to the type of nodes in the NodeSet<TNode>
.
This method will return a list of found nodes based on the given path and will map it to the type of nodes in the NodeSet<TNode>
.
There are limited amount commands which can be executed on the node(s) which matched with the expected path/query.
This method will except an UpdateSet
builder. An UpdateSet
will tell this method what you want to update. For example you can update:
- Specific properties which are defined in the C# object
- Custom properties which doesn't exist in the C# object
- The complete Node by passing an instance of the node with the newest values you want to set in the database.
This method will perform an update with the same steps of UpdateAsync
, but it will return the updated nodes from the database.
In case you have two nodes already and now you want to make a relation between them. Let's say that you have created a Movie before and also imported some people and now want to connect some of those people to the created movie. For example
graphContext
.Movies
.Match(x => x.Where(y => y.Title, "The Matrix"))
.ConnectAsync(x => x.Actors, x => x.Where(y => y.Name, "Keanu Reeves"));
The code above will be translated to
MATCH(l0:Movie WHERE l0.Title = 'The Matrix')
MATCH(l1:Person WHERE l1.Name = 'Keanu Reeves')
CREATE (l0)<-[:ACTED_IN]-(l1)
This method will remove the relation between your matched node the query is started with and the last WithRelation
. For example
graphContext
.Movies
.Match(x => x.Where(y => y.Title, "The Matrix"))
.WithRelation(x => x.Director)
.WithRelation(x => x.Actors)
.DisconnectAsync();
Now the code above will check if there is any movie with name The Matrix
and has a Director
then will remove all of the connections it has to any actors.
It will be translated to this:
MATCH(l0:Movie WHERE l0.Title = 'The Matrix')
MATCH (l0)<-[r1:DIRECTED]-(l1:Person)
MATCH (l0)<-[r2:ACTED_IN]-(l2:Person)
DELETE r2
Thi method can be used to SoftDelete a queried node. It will set ArchivedAt
to timestamp()
in neo4j. Now other services like kafka connectors can decide to remove the archived nodes from other services, such as Elasticsearch
. To hard delete the node, later a process can run for example to HardDelete nodes which are deleted x
days ago.
Usage:
await graphContext
.Movies
.Match(x => x.Where(y => y.Id, "<some-id>"))
.ArchiveAsync()
This will be translated to:
MATCH(l0:Movie WHERE l0.Id = '<some-id>') SET l0.ArchivedAt=timestamp() return l0
As you see, it will return all archived nodes.
This feature lets you query nodes and their relations without having the objects available explicitly in C#. For example after v1.1.0 it is possible to:
graphContext.Anonymous("Movie")
.Match(x => x.Where("Id", "<some-id>"))
.FirstOrDefault();
As you see, you need to pass the property names and the label as string. The Anonymous
method also accepts a NodeConfiguration
which you can define on the fly based on your generic configuration.
⚠️ IMPORTANT: Please check this cypher injection warning out.