How to create queries using runtime managed labels using Neo4j-OGM? - neo4j

Simple question: I'm using Neo4-OGM (with Quarkus) to interact with my Neo4J DB (latest version).
I have an entity "Contact" and I added the #Labels to be able to manage extra labels at runtime.
#NodeEntity
public class Contact {
#Id
#GeneratedValue(strategy = UuidStrategy.class)
private String identifier;
// some properties and relations...
#Labels
private List<String> labels;
}
This will work fine.
But now, I would like to querying my DB using the methods loadAll with Filters instead of writing by myself a cypher query.
Unfortunately, I cannot see how I could get any equivalent of the following cypher query:
MATCH (n:`Contact`:`Label_added_in_labels`) RETURN n
Is it supported? Or I will have to write the cypher by myself? (That's fine but I don't want to write them if it's not needed).

The Filter in Neo4j-OGM are property based and sadly cannot help you with this.
But you could use the Neo4j CypherDSL if you do not want to write your own statements.
For this you can add the following dependency to your project
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher-dsl</artifactId>
<version>2021.3.0</version> // <- currently the latest version
</dependency>
and use it for example like this in combination with a Neo4j-OGM Session:
Node node = Cypher.node("Contact", "Label_added_in_labels");
Statement statement = Cypher.match(node.named("n")).returning(node).build();
Iterable<User> contacts = session.query(Contact.class, statement.getCypher(), Collections.emptyMap());

Related

How can I map a query result to domain objects?

I'm developping a program that uses the neo4j-ogm library directly (aka I don't use any Spring component) and my Neo4J DB has this sorts of relations:
PARAMETER<-[:HAS_PARAMETER]-TASK-[:HAS_STEP]->STEP-[:HAS_PARAMETER]->PARAMETER
STEP-[:HAS_STEP]->STEP
PARAMETER-[:INITIALIZES]->PARAMETER
I coded all my domain classes (PARAMETER, TASK and STEP).
I write a query like (with a session.query method call):
MATCH (:TASK)-[r*]->() return r
Can I directly map the result from my query to domain objects?
EDIT:
To be sharper, I've got this class Task
#NodeEntity
class Task {
#RelationShip(type = "HAS_STEP")
Set<Step> steps;
#RelationShip(type = "HAS_PARAMETER")
Set<Parameter> parameters;
}
I wish fill a Task instance (with steps and parameters) and each step be filled too.
You can use session.query() to return a org.neo4j.ogm.model.Result that contains the results. This is supported only in Neo4j OGM 2.0.1.
Returning a path is not supported, so you must return the nodes and relationships that comprise the path e.g.
MATCH p=(t:TASK)-[r*]->() return t, nodes(p), rels(p)
Then, you can access t from the Result and it will be a hydrated Task. Alternatively, you can access the nodes from the Result for all hydrated entities in the path.
More examples are in the test here: https://github.com/neo4j/neo4j-ogm/blob/2.0/core/src/test/java/org/neo4j/ogm/persistence/session/capability/QueryCapabilityTest.java
BTW the blog post that Christophe references is still valid for the OGM functionality as well if you need to understand what can be mapped.
Yes you can, there is a complete blog post about it :
http://graphaware.com/neo4j/2016/04/06/mapping-query-entities-sdn.html

Cypher 1.9.9, START by both relationship and node index

My Neo4j 1.9.9 entities are stored using Spring Data Neo4j. However, because many derived queries from repository methods are wrong, I've been forced to use directly Cypher
Basically, I have two classes:
#NodeEntity
public class RecommenderMashup {
#Indexed(indexType = IndexType.SIMPLE, indexName = "recommenderMashupIds")
private String mashupId;
}
#RelationshipEntity(type = "MASHUP_TO_MASHUP_SIMILARITY")
public class MashupToMashupSimilarity {
#StartNode
private RecommenderMashup mashupFrom;
#EndNode
private RecommenderMashup mashupTo;
}
In addition to the indexes directly provided, as you know, Spring Data Neo4j adds two other indexes: __types__ for nodes and __rel_types__ for relationship; both of them have className as their key.
So, I've tried the query below to get all the MashupToMashupSimilarity objects related to a specific node
START `mashupFrom`=node:`recommenderMashupIds`(`mashupId`='5367575248633856'),
`mashupTo`=node:__types__(className="package.RecommenderMashup"),
`mashupToMashupSimilarity`=rel:__rel_types__(className="package.MashupToMashupSimilarity")
MATCH `mashupFrom`-[:`mashupToMashupSimilarity`]->`mashupTo`
RETURN `mashupToMashupSimilarity`;
However, I always got empty results. I suspect that this is due to the fact that the START clause contains both nodes and relationships. Is this possible? Otherwise, what could be the problem here?
Additional infos
The suspect came from the fact that
START `mashupToMashupSimilarity`=rel:__rel_types__(className='package.MashupToMashupSimilarity')
RETURN `mashupToMashupSimilarity`;
and
START `mashup`=node:__types__(className="package.RecommenderMashup")
RETURN `mashup`;
and other similar queries always return the right results.
The only working alternative at this point is
START `mashupFrom`=node:`recommenderMashupIds`(`mashupId`='6006582764634112'),
`mashupTo`=node:__types__(className="package.RecommenderMashup")
MATCH `mashupFrom`-[`similarity`:MASHUP_TO_MASHUP_SIMILARITY]->`mashupTo`
RETURN `similarity`;
both I don't know how it works in terms of performance (the indexes should be faster). Also, I'm curious what I've been doing wrong.
Did you try to run your queries in the neo4j-browser or shell? did they work there?
This query is also wrong,
START `mashupFrom`=node:`recommenderMashupIds`(`mashupId`='5367575248633856'),
`mashupTo`=node:__types__(className="package.RecommenderMashup"),
`mashupToMashupSimilarity`=rel:__rel_types__(className="package.MashupToMashupSimilarity")
MATCH `mashupFrom`-[:`mashupToMashupSimilarity`]->`mashupTo`
RETURN `mashupToMashupSimilarity`;
you use mashupToMashupSimilarity as identifier for the relationship,
but then you use it wrongly as relationship-type:
-[:mashupToMashupSimilarity]->
it should be: -[mashupToMashupSimilarity]->
but of course better, skip the rel-index check and use -[similarity:MASHUP_TO_MASHUP_SIMILARITY]->
And you can just leave of the relationship-index lookup which doesn't make sense at all, as you should already filter with the relationship-type.
Update: Don't use index lookups for type check
START mashupFrom=node:recommenderMashupIds(mashupId='5367575248633856')
MATCH (mashupFrom)-[mashupToMashupSimilarity:MASHUP_TO_MASHUP_SIMILARITY]->(mashupTo)
WHERE mashupTo.__type__ = 'package.RecommenderMashup'
RETURN mashupToMashupSimilarity;
As the relationship-type is already restricting, I think you don't even need the type-check on the target node.

Grails: query or criteria against a string/value pairs map property

Grails gives the possibility of creating simple string/value map properties section "Maps of Objects", first paragraph.
I was wondering, is there a way to later query the domain class (using Gorm dynamic finders, criterias or HQL) using the map property as part of the query (i.e adding a condition for the key X to have the value Y)?
After playing with it a bit and almost give up, I discovered the map syntax to (surprisingly) work in HQL. Assuming the class looks like:
class SomeClass {
Map pairKeyProperty
}
You can build queries that look like the following:
select * from SomeClass sc where sc.pairKeyProperty['someKey'] = 'someValue' and sc.pairKeyProperty['someOtherKey'] = 'someOtherValue'
Pretty neat! I still would prefer to use criterias as they are much cleaner to compose, but they seem to not support the same syntax (or I couldn't find it).
I created a sample app in GitHub:
https://github.com/deigote/grails-simple-map-of-string-value-pairs
It can be visisted at:
http://grails-map-of-string-pairs.herokuapp.com/
The form above uses a cross join. To enforce an inner join use
join sc.pairKeyProperty pk1 on index(pk1) = 'someKey'
where 'someValue' in elements(pk1)

Why does spring-data require START in a Cypher query?

I have User type in neo4j database with a 'registered' property that stores the timestamp (Long) when user joined the site. I want to find how many users have registered before a given date. I defined a query method on the Spring-data Graph repository interface:
#Query("MATCH user=node:User WHERE user.registered < {0} RETURN count(*)")
def countUsersBefore(registered: java.lang.Long): java.lang.Long
I see in the Neo4j manual a lot of queries that just start with MATCH, but Spring-data doesn't seem to like it and requires a START. In my case I don't have an obvious node from where I can start, since my query is not following any relationships, it's just a plain count-where combination.
How can I fix this query? Do I need an index on the 'registered' property?
If you want to use this syntax you have to use Spring Data Neo4j 3.0-M01 which works with Neo4j 2.0.0-M06.
You also need that to be able to use labels.
But better wait for the next milestone version of SDN 3.0 which will work with Neo4j 2.0.0 final.
Update:
If you use the SDN types index:
START user=node:__types__(className="org.example.User")
WHERE user.registered < {0}
RETURN count(*)
or in a repository this derived method should work:
public interface UserRepository extends GraphRepository<User> {
int countByRegisteredLessThan(int value);
}
Instead of MATCH user=node:User..., you want MATCH (user:User)...

Update relationship / payload with the Neo4jClient

I am new to Neo4j and Neo4jClient. I am trying to update an existing relationship. Here is how I created the relationship.
var item2RefAddedBefore = _graphClient.CreateRelationship((NodeReference<Item>)item2Ref,
new AddedBefore(item1Ref, new Payload() { Frequency = 1 }));
For this particular use case, I would like to update the Payload whenever the Nodes and relationship already exist. I am using Cypher mostly with the Neo4jClient.
Appreciate any help!
Use this IGraphClient signature:
void Update<TRelationshipData>(RelationshipReference<TRelationshipData> relationshipReference, Action<TRelationshipData> updateCallback)
where TRelationshipData : class, new();
Like this:
graphClient.Update(
(RelationshipReference<Payload>)item2RefAddedBefore,
p => { p.Foo = "Bar"; });
Update: The syntax is a little awkward right now, where CreateRelationship only returns a RelationshipReference instead of a RelationshipReference<TData> but Update requires the latter, so you need to explicitly cast it. To be honest, we probably won't fix this any time soon as all of the investment for both Neo4j and Neo4jClient is going towards doing mutations via Cypher instead.

Resources