Neo4j Repository - Writing query with a dynamic where clause - neo4j

I am relatively new to Neo4j, and have a doubt regarding writing of dynamic queries in Neo4j with spring.
From what I have read, the queries are annotated with #Query parameter in an interface that extends the GraphRepository class, and the dynamic parameters are supplied as argument.
But my requirement is such that I have to dynamically generate the number of where clauses.
For example,
#Query("match n where n.__type__='com.connectme.domain.Person' and n.age > {0} return n.id)
public List<Object> getPeopleWithAge(Integer age);//
My query can also change wherein age can also be less than some value, in which case, the query can become :
#Query("match n where n.__type__='com.connectme.domain.Person' and n.age > {0} and n.age <{1} return n.id)
public List<Object> getPeopleWithAge(Integer age1, Integer age2);//
In similar way, many clauses around the age parameter can lead to variation in where clauses.
How can i dynamically handle this as currently I am only aware of this annotated way of executing queries.
Can I override and write my own custom queries ?

You can write your own custom query logic. First you create an extra interface containing the custom query method, so you get two repository interfaces
public interface YourRepository extends GraphRepository<SomeClass> implements YourRepositoryExtension{
//inferred queries, annotated queries
}
public interface YourRepositoryExtension {
EndResult<SomeClass> customQuery();
Iterable<SomeClass> customTraversal();
}
Then you make an implementation:
#Repository
public class YourRepositoryImpl implements YourRepositoryExtension {
#Autowired
private YourRepository yourRepository;
#Override
public EndResult<SomeClass> customQuery(){
//your query logic, using yourRepository to make cypher calls.
return yourRepository.query("START n.. etc.. RETURN n", <optional params>);
}
#Override
public Iterable<SomeClass> customTraversal(){
SomeClass startNode = yourRepository.findOne(123);
return yourRepository.findAllByTraversal(startNode, <some custom traversal>);
}
}

Related

I want to add a Relationship between two already existing nodes in Neo4j - how to do that using Spring Data Neo4jRepository?

The cypher query to perform the above task would be as follows:
MATCH (a:Buyer), (b:Seller)
MERGE (a) -[:BUY {quantity: 150}]-> (b);
I want the equivalent Neo4jRepository function or some equivalent code that can serve the same above purpose. Please post the answer if you know some solution.
[Updates]
I have posted an answer below. But I am also expecting some other kind of solutions to this purpose. Please feel free to post alternative solutions as answers.
One Possible Way
If we have two Nodes/Entities as Buyer and Stock as POJO classes in SpringBoot Code, and if we are trying to add a relationship called [:HAS] between two such nodes then we can do the following.
#Node("Stock")
class Stock
{
#Id #GeneratedValue(...)
private Long id;
/* Other attributes here --- */
}
#Node("Buyer")
class Buyer
{
#Id #GeneratedValue(...)
private Long id;
/* Other Attribute Variables Here --- */
#Relationship(type="HAS")
private List<Stock> stockList;
public List<Stock> getStockList()
{
return stockList;
}
public void setStockList(List stockList)
{
this.stockList = stockList;
}
}
So we can do this to create the desired relationship.
buyer.getStockList().add(newStock);
buyerRepo.save(buyer);
Here buyerRepo is the object of a Repository that implements Neo4jRepository<Buyer, Long>

Spring Data Query into Elastic search for exact match

I am learning elasticsearch with spring data so can someone help me understand better what elasticsearch query is doing here. I am trying to return back only a set of results based off of a certain value, in this case env. It seems to me that this JPQL query, is not making a difference to only return what I ask for. I have also used an #Query with no difference.
-- here is part of my repository class
public interface MyFormRepo extends ElasticsearchRepository<MyForm, String> {
//??? these function calls are not effecting my return
#Query("{\"bool\": {\"must\": [{\"match\": {\"env\": \"?0\"}}]}}")
Page<MyForm> getAllByEnv(String env, Pageable pageable);
Page<MyForm> findAllByEnv(String env, Pageable pageable);
-- Here is part of my entity class
#Document(indexName = "my_form")
public class MyForm {
#Id
private String id;
#Field(type = Text)
private String schema;
#Field(type = Long)
private long version;
#Field(type = Text)
private String env;
Here is what I understand. Elasticsearch has this concept called Fuzziness (https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#fuzziness) so in it's searches which is based on Levenshtein distance (https://en.wikipedia.org/wiki/Levenshtein_distance). Spring data does not allow us to modify this out of the box and is considered Fuzziness.AUTO by default. https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.misc. As for the queries neither of them will do anything different. Both findByAllEnv and getAllBeEnv has a fuziness.AUTO, as for the #Query I found a good reason stated at this site: What is difference between match and bool must match query in Elasticsearch. What I ended up finding is that I must implemented a custom repo for that I have found this example/explaination How to query Elastic with Spring-data-elastic.

How do we generate a cypher query by using Java and APOC for Neo4j?

I am trying to create my own procedure in Java in order to use it for Neo4j.I wanted to know how we can execute Cypher code in Java ?
I tried to use graphDB.execute() function but it doesn't work.
I just want to execute a basic code in Java by using Neo4j libraries.
Example of a basic code I want to execute:
[EDIT]
public class Test
{
#Context public GraphDatabaseService graphDb;
#UserFunction
public Result test() {
Result result = graphDb.execute("MATCH (n:Actor)\n" +
"RETURN n.name AS name\n" +
"UNION ALL MATCH (n:Movie)\n" +
"RETURN n.title AS name", new HashMap<String, Object>());
return result;
}
}
If you want to display nodes (as in the graphical result view in the browser), then you have to return the nodes themselves (and/or relationships and/or paths), not the properties alone (names and titles). You'll also need this to be a procedure, not a function. Procedures can yield streams of nodes, functions can only return single values.
Change this to a procedure, and change your return type to be something like Stream<NodeResult> where NodeResult is a POJO that has a public Node field.
You'll need to change your return accordingly.

How to make node changes in a TransactionEventHandler that are returned within the same CREATE query

I am trying to implement a plugin for neo4j to add an autoincrement ID using GraphAware library. To this end, I've written the following classes:
public class ModuleBootstrapper implements RuntimeModuleBootstrapper
{
public RuntimeModule bootstrapModule(String moduleId, Map<String, String> config, GraphDatabaseService database)
{
return new MyModule(moduleId, config, database);
}
}
And:
public class MyModule extends BaseTxDrivenModule<Void>
{
int counter = 0;
public Void beforeCommit(ImprovedTransactionData transactionData)
throws DeliberateTransactionRollbackException
{
if (transactionData.mutationsOccurred()) {
for (Node newNode : transactionData.getAllCreatedNodes()) {
newNode.setProperty("id", counter++);
}
}
}
}
And for the testing I can execute:
CREATE (n);
And then:
MATCH (n) RETURN n;
And I can see the effect of my plugin as some id property added to the node. But when I run:
CREATE (n) RETURN n;
The returned node does not have the mentioned id property but again, when I match the node in a separate query, I see the things have worked out just fine. It's just that in the CREATE query, the returned node infos are the ones before my plugin modified them.
The questions are; why is that? Didn't I modify the nodes through my plugin within the transaction? Shouldn't the returned nodes be showing the modifications I've made on them? Is there any way I can make this happen?
While you're still within the transaction, the Cypher result is already computed and there is no clean way to add additional informations to it.
I guess a feature request on the neo4j repository could be cool but in total honesty this would require a serious change into the neo4j core codebase.
BTW, the incremental ID is already implemented in the graphaware-uuid plugin : https://github.com/graphaware/neo4j-uuid#specifying-the-generator-through-configuration

Spring Data Neo4j 4 very slow for large hierarchical queries

I am currently using SpringDataNeo4j–4.1.0.M1 and have a use case where I need to get a sub-graph (actually a tree) and return it in an hierarchical structure similar to this (these sub-graphs are usually around 500 nodes in size and have an unspecified depth):
<Pathway dbId="1" displayName="Name1">
<Pathway dbId="2" displayName="Name2">
<Pathway dbId="3" displayName="Name3">
<Reaction dbId="4" displayName="Name4" />
<Reaction dbId="5" displayName="Name5" />
<Pathway dbId="6" displayName="Name6">
<Reaction dbId="7" displayName="Name7" />
</Pathway>
</Pathway>
...
The data model:
#NodeEntity
public abstract class Event {
#GraphId
private Long id;
private Long dbId;
private String displayName;
... other relationships and properties
}
#NodeEntity
public class Pathway extends Event {
#Relationship(type="hasEvent", direction = Relationship.OUTGOING)
private List<Event> hasEvent;
... other relationships and properties
}
#NodeEntity
public class Reaction extends Event {
... other relationships and properties
}
To solve this I have created the following query:
String query = "Match (n:Pathway{dbId:123456})-[r:hasEvent*]->(m:Event) Return n,r,m";
Result result = neo4jTemplate.query(query,Collections.<~>emptyMap());
return result.iterator().next().get("n");
This is working and returns the result as expected. The only problem is the speed. While testing the query using the Neo4j-Browser I get a result within 50-100ms. Using SDN it takes over 2 seconds for Spring to map the result to objects.
The question now is: Is there any way I can speed up this query in Spring or is there a better solution where I can return this hierarchical structure in Cypher using something like nested Collections(since I only need the names and ids and not the objects themselves)?

Resources