Lambda expression in WHERE clause not working as expected - neo4jclient

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.

Related

Returning multi value in dynamic query using neo4j client

Following the question I asked: Build a dynamic query using neo4j client
I got an answer about how can I return value dynamically using string only.
When I'm trying to use the syntax to return multi values from the query it failed,
I tried the following query:
var resQuery2 = WebApiConfig.GraphClient.Cypher
.Match("(movie:Movie {title:{title}})")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.WithParam("title", title)
.Return(() => Return.As<string>("movie, collect([person.name, head(split(lower(type(r)), '_')), r.roles])"));
I'm getting the following error:
The deserializer is running in single column mode, but the response
included multiple columns which indicates a projection instead. If
using the fluent Cypher interface, use the overload of Return that
takes a lambda or object instead of single string. (The overload with
a single string is for an identity, not raw query text: we can't map
the columns back out if you just supply raw query text.)
Is it possible to return multiple nodes using only strings?
We can't get an output like in the question you asked previously - this is due to the fact that you are asking for a Node (the movie) and a Collection of strings (the collect) and they have no common properties, or even styles of property.
Firstly, let's look at the painful way to do this:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => Return.As<string>("{movie:movie, roles:collect([person.name, head(split(lower(type(r)), '_')), r.roles])}"));
var results = q.Results;
Here we take the query items (movie, r, person) and create a type with them the {} around the results, and cast that to a string.
This will give you a horrible string with the Node data around the movie and then a collection of the roles:
foreach (var m in results)
{
//This is going to be painful to navigate/use
dynamic d = JsonConvert.DeserializeObject<dynamic>(m);
Console.WriteLine(d.movie);
Console.WriteLine(d.roles);
}
You'd be a lot better off doing something like:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => new
{
Movie = Return.As<Node<string>>("movie"),
Roles = Return.As<IEnumerable<string>>("collect([person.name, head(split(lower(type(r)), '_')), r.roles])")
});
var res = q.Results;
You could either JsonConvert.DeserializeObject<dynamic>() the Movie node, at your leisure, or write a strongly typed class.
In terms of a 'dynamic' object, I don't know how you were wanting to interact with the collect part of the return statement, if this doesn't help, you might need to update the question to show a usage expectation.

How to use START with Cypher / Neo4j 2.0

I am trying example provided in Graph Databases book (PDF page 51-52)with Neo4j 2.0.1 (latest). It appears that I cannot just copy paste the code sample from the book (I guess the syntax is no longer valid).
START bob=node:user(username='Bob'),
charlie=node:user(username='Charlie')
MATCH (bob)-[e:EMAILED]->(charlie)
RETURN e
Got #=> Index `user` does not exist.
So, I tried without 'user'
START bob=node(username='Bob'),
charlie=node(username='Charlie')
MATCH (bob)-[e:EMAILED]->(charlie)
RETURN e
Got #=> Invalid input 'u': expected whitespace, an unsigned integer, a parameter or '*'
Tried this but didn't work
START bob=node({username:'Bob'}),
(charlie=node({username:'Charlie'})
MATCH (bob)-[e:EMAILED]->(charlie)
RETURN e
Got #=> Invalid input ':': expected an identifier character, whitespace or '}'
I want to use START then MATCH to achieve this. Would appreciate little bit of direction to get started.
From version 2.0 syntax has changed.
http://docs.neo4j.org/chunked/stable/query-match.html
Your first query should look like this.
MATCH (bob {username:'Bob'})-[e:EMAILED]->(charlie {username:'Charlie'})
RETURN e
The query does not work out of the box because you'll need to create the user index first. This can't be done with Cypher though, see the documentation for more info. Your syntax is still valid, but Lucene indexes are considered legacy. Schema indexes replace them, but they are not fully mature yet (e.g. no wildcard searches, IN support, ...).
You'll want to use labels as well, in your case a User label. The query can be refactored to:
MATCH (b:User { username:'Bob' })-[e:EMAILED]->(c:User { username:'Charlie' })
RETURN e
For good performance, add a schema index on the username property as well:
CREATE INDEX ON :User(username)
Start is optional, as noted above. Given that it's listed under the "deprecated" section in the Cypher 2.0 refcard, I would try to avoid using it going forward just for safety purposes.
However, the refcard does state that you can prepend your Cypher query with "CYPHER 1.9" (without the quotes) in order to make explicit use of the older syntax.

Neo4j - traversal to find specific connected component

Using neo4j 1.9.4, I'm trying to find the connected components (all reachable nodes) from a starting node where the relationship has a certain attribute ('since') and this attribute has a defined integer value, e.g. 20130101.
My initial approach was using a cypher query, but I got the feeling that this query loops to infinity if there is a loop within the graph? At least if I do not restrict the path length and restricting the length is not what I want to do.
So meanwhile I started using a traversal. Using neo4jphp a traversal looks like that:
$traversal->setOrder(Everyman\Neo4j\Traversal::OrderBreadthFirst)
->setPruneEvaluator(Everyman\Neo4j\Traversal::PruneNone)
->setReturnFilter(Everyman\Neo4j\Traversal::ReturnAll)
->setUniqueness(Everyman\Neo4j\Traversal::UniquenessNodeGlobal);
What I think I need is something like this:
->setPruneEvaluator('javascript', "position.RELATIONSHIP().getProperty('since').EQUALS(20130101)")
Obviously, RELATIONSHIP and EQUALS seem to be wrong.
I adopted this from the example https://github.com/jadell/neo4jphp/wiki/Traversals, where the following valid and working pruneElevater is set:
->setPruneEvaluator('javascript', "position.endNode().getProperty('name').toLowerCase().contains('t')")
I'm absolutely not familiar with JavasScript, so I can't figure out how to do that. Additionally, how can I make sure the traversal does not result in an error if there is a relationship that does not have the property "since"?
If I can achieve the same using a cypher query I would accept that, too.
EDIT: By the way, my approach using cypher was this:
START n=node({start_node}) MATCH p = n-[*]-m WHERE ALL(x IN RELATIONSHIPS(p) WHERE HAS(x.since) AND x.since = 20130101) RETURN DISTINCT m
EDIT2: Trying the suggested cypher query from ulkas give me the following error:
Invalid query
string matching regex ``(``|[^`])*`' expected but `*' found
Think we should have better error message here? Help us by sending this query to cypher#neo4j.org.
Thank you, the Neo4j Team.
"START n=node(40317) MATCH p = n-[r:*..]-m WHERE has(r.since) AND r.since = 20130101 RETURN DISTINCT m"
^
EDIT3: The suggestion of LameCode looked really promising, but still it returns an error:
Fatal error: Uncaught exception 'Everyman\Neo4j\Exception' with message 'Unable to execute traversal [400]: Headers: Array ( [Content-Length] => 5183 [Content-Type] => application/json; charset=UTF-8 [Access-Control-Allow-Origin] => * [Server] => Jetty(6.1.25) ) Body: Array ( [message] => Failed to execute script, see nested exception. [exception] => EvaluationException [fullname] => org.neo4j.server.rest.domain.EvaluationException [stacktrace] => Array ( [0] => org.neo4j.server.scripting.javascript.JavascriptExecutor.execute(JavascriptExecutor.java:118) [1] => org.neo4j.server.rest.domain.EvaluatorFactory$ScriptedEvaluator.evalPosition(EvaluatorFactory.java:140) [2] => org.neo4j.server.rest.domain.EvaluatorFactory$ScriptedPruneEvaluator.evaluate(EvaluatorFactory.java:161) [3] => org.neo4j.graphdb.traversal.Evaluator$AsPathEvaluator.evaluate(Evaluator.java:69) [4] => org.neo4j.kernel.impl.traversal.TraverserIterator.eva in /var/www/vendor/everyman/neo4jphp/lib/Everyman/Neo4j/Command.php on line 116
And I used the following pruneEvaluator:
->setPruneEvaluator('javascript', "position.lastRelationship().hasProperty('since') && position.lastRelationship().getProperty('since') == 20130101")
When changing from lastRelationship() to endNode() it at least doesn't return me an error, despite I am wondering about the many results it returns, as none of the nodes has exactly this since attribute?! So it seems even then, the prune evaluator does not get to work. I expected it to stop at each endNode if has no since property or if it is unqual the given date? What am I doing wrong, any ideas?
In regards to the Traverser that you are using. The javascript prune evaluator 'position' variable is a Path object. See: http://components.neo4j.org/neo4j/1.9.4/apidocs/org/neo4j/graphdb/Path.html
Those methods should be available to you.
Use lastRelationship() (because all the former relationships will have come through the prune evaluator already).
The Relationship object inherits from Property Container and that has a hasProperty() method.
setPruneEvaluator('javascript', "position.lastRelationship().hasProperty('since') && position.lastRelationship().getProperty('since') == 20130101")
I'm not sure if you need to use the Equals method or not since it's javascript.

FullText Search / IndexLookup using Neo4jClient

I am trying to execute the following Cypher statement
"START b=node:customer_full_text_idx('ID:"ASHLAND"') return b
I am using this method
var results = _graphClient.QueryIndex<Customer>(Base.INDEX_CUSTOMER_FULL_TEXT, IndexFor.Node, "ID:" + "ASHLAND");
This method throws Lucene exceptions sometimes. This issue is documented at https://bitbucket.org/Readify/neo4jclient/issue/54/spaces-in-search-text-while-searching-for. The QueryIndex is deprecated and I tried recommended Syntax
I tried using recommend Cypher
var results = _graphClient
.Cypher
.Start(new { n = Node.ByIndexLookup(Base.INDEX_CUSTOMER_FULL_TEXT, "ID", "ASHLAND") })
.Return<Customer>("n")
.Results;
But above statement does not return any results. I think the problem is that above syntax is not designed for FullText and it inserts '=' in the Cypher start. Whereas it expects a ":" between ID and Name. Or may be I am missing something obvious.
Please share any examples using Neo4jClient using the .Start Query. TIA.
Use Node.ByIndexQuery instead of Node.ByIndexLookup.
(You were using a query before, then swapped to a lookup in the new syntax.)
This is a lookup: http://docs.neo4j.org/chunked/snapshot/query-start.html#start-node-by-index-lookup
This is a query: http://docs.neo4j.org/chunked/snapshot/query-start.html#start-node-by-index-query

Incorrect sort order with Neo4jClient Cypher query

I have the following Neo4jClient code
var queryItem = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("myindex", "Name", sku),
})
.Match("p = n-[r:Relationship]->ci")
.With("ci , r")
.Return((ci, r) => new
{
N = ci.Node<Item>(),
R = r.As<RelationshipInstance<Payload>>()
})
.Limit(5)
.Results
.OrderByDescending(u => u.R.Data.Frequency);
The query is executing fine but the results are not sorted correctly (i.e. in descending order). Here is the Payload class as well.
Please let me know if you see something wrong with my code. TIA.
You're doing the sorting after the .Results call. This means that you're doing it back in .NET, not on Neo4j. Neo4j is returning any 5 results, because the Cypher query doesn't contain a sort instruction.
Change the last three lines to:
.OrderByDescending("r.Frequency")
.Limit(5)
.Results;
As a general debugging tip, Neo4jClient does two things:
It helps you construct Cypher queries using the fluent interface.
It executes these queries for you. This is a fairly dumb process: we send the text to Neo4j, and it gives the objects back.
The execution is obviously working, so you need to work out why the queries are different.
Read the doco at http://hg.readify.net/neo4jclient/wiki/cypher (we write it for a reason)
Read the "Debugging" section on that page which tells you how to get the query text
Compare the query text with what you expected to be run
Resolve the difference (or report an issue at http://hg.readify.net/neo4jclient/issues/new if it's a library bug)

Resources