neo4j query for nullable property - neo4j

I am using spring-neo4j. I have an entity called User with properties (username, firstName, lastName) and firstName and lastName are optional properties.
Now I want to implement a user search query which will search across all three properties.
#Query(value = "start user=node:__types__(className='com.xxx.entity.User') where user.username =~ {0} or user.firstName =~ {0} or user.lastName =~ {0} return user")
List<User> searchByName(String keyword);
The query fails saying that:
The property 'firstName' does not exist on Node[21].
However, if I search only on username, it gives me the result. I tried using the ? operator for nullable properties:
#Query(value = "start user=node:__types__(className='com.xxx.entity.User') where user.username =~ {0} or user.firstName? =~ {0} or user.lastName? =~ {0} return user")
List<User> searchByName(String keyword);
But this will fetch me all the nodes where firstName or lastName are missing.
Any idea how do I implement this query?

Would
start user=node:__types__(className='com.xxx.entity.User')
where user.username =~ {0}
or (has(user.firstName) and user.firstName =~ {0})
or (has(user.lastName) and user.lastName =~ {0})
return user
work?
Edit:
There are many reasons for a query not to return data. If you want help with that, share sample data and the parameters you are passing with your query. Use Neo4j console to share data and put the link and parameter examples in your question.
Until then, this might be helpful:
Use ! instead of ?.
[--verbose]
In Neo4j 1.x you can indeed use the ? and ! syntax, but you are using them wrong. ? defaults to true when the property is missing, which means your query matches the nodes you want plus all nodes that don't have a firstName or that don't have a lastName. ! will default to false when the property is missing and therefore exclude those nodes. n.prop! = val is equivalent to the has(n.prop) and n.prop=val I used above. See Neo4j stable docs.
In Neo4j 2.0+ the !? syntax is removed. Non-existing properties default to null. Therefore n.prop=val will evaluate to false exactly when has(n.prop) does(*). This means that your original query would work in 2.0–the query would resolve a missing property as a non-match and it would neither break nor include all nodes that don't have firstName or lastName(**). See Neo4j milestone docs. You might as well use syntax that won't break when SpringDataNeo4j moves to Neo4j 2.0, so use has(n.prop) and n.prop=val which works on either version.
Also please see How to cypher query a neo4j DB with WHERE clause on sparse property.
(*)(except it won't if val=null)
(**)(unless you pass null as a parameter)

Related

Find path in Neo4j with directed edges

This is my first attempt at Neo4j, please excuse me if I am missing something very trivial.
Here is my problem:
Consider the graph as created in the following Neo4j console example:
http://console.neo4j.org/?id=y13kbv
We have following nodes in this example:
(Person {memberId, memberName, membershipDate})
(Email {value, badFlag})
(AccountNumber {value, badFlag})
We could potentially have more nodes capturing features related to a Person like creditCard, billAddress, shipAddress, etc.
All of these nodes will be the same as Email and AccountNumber nodes:
(creditCard {value, badFlag}), (billAddress {value, badFlag}),etc.
With the graph populated as seen in the Neo4j console example, assume that we add one more Person to the graph as follows:
(p7:Person {memberId:'18' , memberName:'John', membershipDate:'12/2/2015'}),
(email6:Email {value: 'john#gmail.com', badFlag:'false'}),
(a2)-[b13:BELONGS_TO]->(p7),
(email6)-[b14:BELONGS_TO]->(p7)
When we add this new person to the system, the use case is that we have to check if there exists a path from features of the new Person ("email6" and "a2" nodes) to any other node in the system where the "badFlag=true", in this case node (a1 {value:1234, badFlag:true}).
Here, the resultant path would be (email6)-[BELONGS_TO]->(p7)<-[BELONGS_TO]-(a2)-[BELONGS_TO]->(p6)<-[BELONGS_TO]-(email5)-[BELONGS_TO]->(p5)<-[BELONGS_TO]-(a1:{badFlag:true})
I tried something like this:
MATCH (newEmail:Email{value:'john#gmail.com'})-[:BELONGS_TO]->(p7)-[*]-(badPerson)<-[:BELONGS_TO]-(badFeature{badFlag:'true'}) RETURN badPerson, badFeature;
which seems to work when there is only one level of chaining, but it doesn't work when the path could be longer like in the case of Neo4j console example.
I need help with the Cypher query that will help me solve this problem.
I will eventually be doing this operation using Neo4j's Java API using my application. What could be the right way to go about doing this using Java API?
You had a typo in you query. PART_OF should be BELONGS_TO. This should work for you:
MATCH (newEmail:Email {value:'john#gmail.com'})-[:BELONGS_TO]->(p7)-[*]-(badPerson)<-[:BELONGS_TO]-(badFeature {badFlag:'true'})
RETURN badPerson, badFeature;
Aside: You seem to use string values for all properties. I'd replace the string values 'true' and 'false' with the boolean values true and false. Likewise, values that are always numeric should just use integer or float values.

Case Insensitive Search with Neo4jClient

Just an quick and easy one, I need to be able to search our database minus the case sensitivity, I know how to do it, just not with the Neo4jClient. Here's the code:
client.Cypher
.Match("(person:Person)")
.Where((Person person) => person.Email == search)
where 'search' is a parameter of type string that is passed to the method. I have read that using =~ '(?i)text' works, but that doesn't allow me to pass in the parameter, and I have tried this:
client.Cypher
.Match("(person:Person)")
.Where((Person person) => person.Email =~ "(?i){terms}")
.WithParam("terms",search)
But it doesn't like this.
I would like to be able to search without case, and if possible at the same time, using LIKE (or ILIKE as it seems to be for pattern matching).
Thanks
EDIT & ANSWER
The final code ended up as this:
return client.Cypher
.Match("(person:Person)")
.Where("person.Email =~ {terms}")
.OrWhere("person.Name =~ {terms}")
.WithParam("terms", "(?ui).*" + search + ".*")
.Return<Person>("person").Results.ToList();
Which does exactly what I want it to.
Also took the advice of a lowercase field with the value in, we already have one in the account so that logon names are not case sensitive, I am going to do this on the email and name fields, seems better than using toLower() (either in Cypher or in C#)
So thank to #Stefan Armbruster for his help.
You cannot have partial parameters. Instead add (?i) to the parameter value:
query: person.Email =~ term
parameter: term = "(?i)<myvalue"
Note 1: You need to use (?ui) for gracefully dealing with non-ascii case sensitivity (e.g. German umlauts).
Note 2: the =~ operator is not backed by an index, so the query above will touch every Person node and apply the regex to the property value. In Neo4j 2.3 there will be a index backed LIKE which supports string prefix matches.
If you want to use index based case insensitive search, the recommended approach is to store the property value converted to lower case (Cypher has a toLower function) and then do a exact match on the lower cased search value.

How to write dynamic search query for Spring-Data-Neo4j

I am implementing search on node attributes. Suppose I have a millions node of User with attributes name, location, income.
n:user (name:"abc",location:"xyz", income:"123")
n:user (name:"def",location:"ghj", income:"1877")
I want to search User either with name or location or income or the combination of attributes like name and location etc. I can perform this search with simple cypher query
#Query("MATCH(n:user{location:{xyz},}) RETURN n") or
#Query("MATCH(n:userr{location:{xyz},name:{abc}}) RETURN n")
But i don't want to do like that, i just want to pass the search parameter and create search query at runtime with the requested parameter. In neo4template class, got this method query(String statement, Map<String,Object> params) Now i am clueless, any detailed answer would be appreciated, as i am new to neo4j.
Unfortunately, parameter maps cannot be used in MATCH patterns.
So I believe you cannot use universal query like:
#Query("MATCH(n:user{0}) RETURN n")
public List<User> complexFind(Map<String, Object> props);
I'd suggest building query string dynamically, using string concatenation, and executing it later on with smth like that:
neo4template.query(YourQueryStringBuilder.build(search_params), other_params);

Unable to apply skip, limit and order clauses in Neo4j using rest cypher

I am using Neo4j 2.1.5 using dependency neo4j-rest-graphdb:2.0.1 in Grails and I query my DB using
RestAPI restapi = new RestAPIFacade("http://localhost:7474/db/data")
RestCypherQueryEngine rcqer=new RestCypherQueryEngine(restapi)
I created a parameter list
Map<String, String> paramVal = new HashMap<>(); paramVal.put('limit', 10); paramVal.put('skip', 0); paramVal.put('order', 'name');
My query looks like this
String query = "match (c:MyLabel) return id(c) as Id, c.name as name skip ${param}.skip limit ${param}.limit;
Execute the query like this
result = rcqer.query(query,org.neo4j.helpers.collection.MapUtil.map("param",paramVal))"
This shows error Invalid input '.': expected whitespace, Limit, LOAD CSV,.....
However works perfect when the parameters are used only in where clause.
Also the result is not in order while using only for order by clause, and works without any error.
Please let me know what is missing or if there is a limitation there.
Thanks in advance.
I don't use that particular part of the library but I would expect that you do not need to reference param directly as that is what the {} syntax is for (no dollar), this would make your query:
String query = "match (c:MyLabel) return id(c) as Id, c.name as name skip {skip} limit {limit}

Lambda expression in WHERE clause not working as expected

I'm new to Neo4j and trying to do a simple Cypher query using a lambda expression in the where clause but for some reason I can't seem to figure out why this isn't working.
Looks like:
class HealthNode {
public string Name{get;set;}
//Other Stuff
}
string Name = "Foobar";
var query = client
.Cypher
.Start(new { n = Neo4jClient.Cypher.All.Nodes })
.Where((HealthNode n) => n.Name == Name)
.Return<HealthNode>("n");
If I dump the Text and Parameters I'm getting:
START n=node(*)
WHERE (n.Name! = {p0})
RETURN n
//P0 Foobar
When I execute this, I of course get:
Cypher does not support != for inequality comparisons. Use <> instead
Why in the world is an extra Exclamation point to the name of the variable?
The ! means that the result will be false if the property doesn't exist. So, if you have more than one type in the graph, and that other type doesn't have a 'Name' property, neo4j won't bother matching.
See Neo4J Documentation for more info.
As to getting the != warning, are you changing the query at all when you paste it? Reformatting it? As I get the same warning if I do:
WHERE (n.Name != {p0})
but don't get any warning, and a successful completion if I use:
WHERE (n.Name! = {p0})
I think I found the cause of the problem here:
There was a change made to the 2.0 parser that implements NULL IF by default (instead of returning an error on a missing property) and removes the ! and ? operators since they no longer do anything.
neo4j pull request 1014
I suspect this will break a lot of things and not just Neo4J Client.
Fixed in Neo4jClient 1.0.0.625 and above, when talking to Neo2j 2.0.

Resources