How do we generate a cypher query by using Java and APOC for Neo4j? - 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.

Related

Can Cypher do phonetic text search with only a part of the text, without using elastic search?

Say I have a job as financial administrator (j:Job {name: 'financial administrator'}).
Many people use different titles for a 'financial administrator'. Therefore, I want abovementioned job as a hit, even if people type only 'financial' or 'administrator' and their input has typos (like: 'fynancial').
CONTAINS only gives results when the match is 100% - so without typos.
Thanks a lot!
First, you could try fuzzy matching with a full text index and see if it solves the issue.
An example would be:
Set up the index-
CALL db.index.fulltext.createNodeIndex('jobs', ['Job'], ['name'], {})
Query the index with fuzzy matching (note the ~)
CALL db.index.fulltext.queryNodes('jobs', 'fynancial~')
If you want to go further and use Lucene's phonetic searches, then you could write a little Java code to register a custom analyzer.
Include the lucene-analyzers-phonetic dependency like so:
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-phonetic</artifactId>
<version>8.5.1</version>
</dependency>
Then create a custom analyzer:
#ServiceProvider
public class PhoneticAnalyzer extends AnalyzerProvider {
public PhoneticAnalyzer() {
super("phonetic");
}
#Override
public Analyzer createAnalyzer() {
return new Analyzer() {
#Override
protected TokenStreamComponents createComponents(String s) {
Tokenizer tokenizer = new StandardTokenizer();
TokenStream stream = new DoubleMetaphoneFilter(tokenizer, 6, true);
return new TokenStreamComponents(tokenizer, stream);
}
};
}
}
I used the DoubleMetaphoneFilter but you can experiment with others.
Package it as a jar, and put it into Neo4j's plugin directory along with the Lucene phonetic jar and restart the server.
Then, create a full text index using this analyzer:
CALL db.index.fulltext.createNodeIndex('jobs', ['Job'], ['name'], {analyzer:'phonetic'})
Querying the index looks the same:
CALL db.index.fulltext.queryNodes('jobs', 'fynancial')
It took a while, this is how I solved my question.
MATCH (a)-[:IS]->(hs)
UNWIND a.naam AS namelist
CALL apoc.text.phonetic(namelist) YIELD value
WITH value AS search_str, SPLIT('INPUT FROM DATABASE', ' ') AS input, a
CALL apoc.text.phonetic(input) YIELD value
WITH value AS match_str, search_str, a
WHERE search_str CONTAINS match_str OR search_str = match_str
RETURN DISTINCT a.naam, label(a)

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

Can I write a Neo4j Plugin to intercept and modify CYPHER queries

In my system, I would like to intercept and change Cypher queries as they come in, one alternative is to modify them before sending them from my middle layer to the graph - but is there a way to have a plugin do the conversion for me in the graph itself?
I'd like to do some of the following:
If someone identifying themselves as members of group A, imagine I'd like to change their request from:
MATCH(f:Film)-[r:REVIEWED_BY]-(u:User {id:"1337"})
to:
MATCH(p:Product)-[p:PURCHASED_BY]-(u:User {id:"1337"})
Is something like this possible? Or do I have to write the traversals in Java directly to achieve this?
Of course you can. You can do ANYTHING in Neo4j. Just grab the cypher string in an unmanaged extension that receives a post request, alter it any way you want, execute it with the graphdb.execute method and return the result as normal.
#POST
#Path("/batch")
public Response alterCypher(String body, #Context GraphDatabaseService db) throws IOException, InterruptedException {
ArrayList<Result> results = new ArrayList<>();
// Validate our input or exit right away
HashMap input = Validators.getValidCypherStatements(body);
ArrayList<HashMap> statements = (ArrayList<HashMap>)input.get("statements");
for (HashMap statement : statements) {
// write the alterQuery method to change the queries.
String alteredQuery = alterQuery((String)statement.get("statement"));
Result result = db.execute(alteredQuery, (Map)statement.getOrDefault("parameters", new HashMap<>()));
results.add(result);
}
// or go the results and return them however you want
// see https://github.com/dmontag/neo4j-unmanaged-extension-template/blob/master/src/main/java/org/neo4j/example/unmanagedextension/MyService.java#L36
return Response.ok().build();
}
At this time it's not possible to extend or modify Cypher queries.
If you need that I recommend you to use Transaction Event API - http://graphaware.com/neo4j/transactions/2014/07/11/neo4j-transaction-event-api.html
With that you should be able to change what query returns.

Neo4j Repository - Writing query with a dynamic where clause

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>);
}
}

Map cypher query result to domain object

I just started to use Neo4j with Spring Data and I'm not able to recover graph objects and convert them back to domain objects. I must say I have no previous experience in that kind of databases.
In that way, I'm using Spring Data repositories. For standard queries the repository code is auto-generated but I would like to define some custom methods, so I created my custom repository as explained here.
For example, I would like to be able to update a certain property value (currentValue property in this case) from a given edge between two certain nodes (searchByUserName is a previously defined index in my node entity which represents a user). I'm using the query method from the Neo4j template in my custom repository implementation as follows:
public class TwitterUserRepositoryImpl implements TwitterUserRepositoryCustom{
#Autowired
private Neo4jOperations neo4jTemplate;
public void updateRelationshipValueByUserName(
String userAUserName, String userBUserName, double value){
HashedMap params = new HashedMap();
params.put("userAUserName", userAUserName);
params.put("userBUserName", userBUserName);
params.put("value", value);
String query = "START x=node:searchByUserName(userName = {userAUserName}), " +
"y=node:searchByUserName(userName = {userBUserName})" +
" MATCH (x)-[r:FOLLOWS]->(y)" +
" SET r.currentValue = {value}" +
" RETURN r";
Result<Map<String, Object>> relationships = neo4jTemplate.query(query, params);
/* let's try to recover the relationship entity and do some more stuff */
}
The cypher query returns an "edge" between two users, where its relationship type is "FOLLOWS", simulating a Twitter users network. I have no idea how to convert this QueryResult object back to my RelationshipEntity object. Is that possible?
Just use the result-dsl: http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#d5e1118
relationships.to(MyRelationshipEntity.class)
will return you a Result<MyRelationshipEntity> which is an Iterable

Resources