Incorrect sort order with Neo4jClient Cypher query - neo4jclient

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)

Related

How to set "resultDataContents" in Neo4jrb?

I want to visualise data from Neo4j with the frontend-library D3.js in an Rails application, using Neo4jrb. For example I could use the following query to get my graph data.
query = "MATCH path = (a)-[b]->(c) RETURN path"
result = Neo4j::Session.current.query(query)
But this query is not giving me the exact data I want.
According to the Neo4j data visualisation guide there is a possibility to set the parameter resultDataContents to "graph". (
Neo4j documentation for "resultDataContents")
This is exactly what I need for my application. Is there any possibility to set this parameter in Neo4jrb, or another idea how to achieve such a result?
Unfortunately not currently. The neo4j-core gem (which the neo4j gem uses) was build to abstract away the REST format. The "graph" format returns data in a different way.
You have a couple of options. You could make the JSON queries yourself or you could retrieve the nodes and relationships from the queries that you perform and then build your own nodes/relationships structure which is returned. This might be more future-proof anyway if you ever want to switch to Bolt.
A way that you might do this in your case:
query = "MATCH path = (a)-[b]->(c) RETURN nodes(path) AS nodes, rels(path) AS rels"
result = Neo4j::Session.current.query(query)
response = {nodes: [], rels: []}
result.each do |row|
response[:nodes].concat(row.nodes)
response[:rels].concat(row.rels)
end
response[:nodes].uniq!
response[:rels].uniq!

How to get total number of db-hits from Cypher query within a Java code?

I am trying to get total number of db-hits from my Cypher query. For some reason I always get 0 when calling this:
String query = "PROFILE MATCH (a)-[r]-(b)-[p]-(c)-[q]-(a) RETURN a,b,c";
Result result = database.execute(query);
while (result.hasNext()) {
result.next();
}
System.out.println(result.getExecutionPlanDescription().getProfilerStatistics().getDbHits());
The database seems to be ok. Is there something wrong about the way of reaching such value?
ExecutionPlanDescription is a tree like structure. Most likely the top element does not directly hit the database by itself, e.g. a projection.
So you need to write a recursive function using ExecutionPlanDescription.getChildren() to drill to the individual parts of the query plan. E.g. if one of the children (or sub*-children) is a plan of type Expand you can use plan.getProfilerStatistics().getDbHits().

Too much time importing data and creating nodes

i have recently started with neo4j and graph databases.
I am using this Api to make the persistence of my model. I have everything done and working but my problems comes related to efficiency.
So first of all i will talk about the scenary. I have a couple of xml documents which translates to some nodes and relations between the, as i already read that this API still not support a batch insertion, i am creating the nodes and relations once a time.
This is the code i am using for creating a node:
var newEntry = new EntryNode { hash = incremento++.ToString() };
var result = client.Cypher
.Merge("(entry:EntryNode {hash: {_hash} })")
.OnCreate()
.Set("entry = {newEntry}")
.WithParams(new
{
_hash = newEntry.hash,
newEntry
})
.Return(entry => new
{
EntryNode = entry.As<Node<EntryNode>>()
});
As i get it takes time to create all the nodes, i do not understand why the time it takes to create one increments so fats. I have made some tests and am stuck at the point where creating an EntryNode the setence takes 0,2 seconds to resolve, but once it has reached 500 it has incremented to ~2 seconds.
I have also created an index on EntryNode(hash) manually on the console before inserting any data, and made test with both versions, with and without index.
Am i doing something wrong? is this time normal?
EDITED:
#Tatham
Thanks for the answer, really helped. Now i am using the foreach statement in the neo4jclient to create 1000 nodes in just 2 seconds.
On a related topic, now that i create the nodes this way i wanted to also create relationships. This is the code i am trying right now, but got some errors.
client.Cypher
.Match("(e:EntryNode)")
.Match("(p:EntryPointerNode)")
.ForEach("(n in {set} | " +
"FOREACH (e in (CASE WHEN e.hash = n.EntryHash THEN [e] END) " +
"FOREACH (p in pointers (CASE WHEN p.hash = n.PointerHash THEN [p] END) "+
"MERGE ((p)-[r:PointerToEntry]->(ee)) )))")
.WithParam("set", nodesSet)
.ExecuteWithoutResults();
What i want it to do is, given a list of pairs of strings, get the nodes (which are uniques) with the string value as the property "hash" and create a relationship between them. I have tried a couple of variants to do this query but i dont seem to find the solution.
Is this possible?
This approach is going to be very slow because you do a separate HTTP call to Neo4j for every node you are inserting. Each call is then a transaction. Finally, you are also returning the node back, which is probably a waste.
There are two options for doing this in batches instead.
From https://stackoverflow.com/a/21865110/211747, you can do something like this, where you pass in a set of objects and then FOREACH through them in Cypher. This means one, larger, HTTP call to Neo4j and then executing in a single transaction on the DB:
FOREACH (n in {set} | MERGE (c:Label {Id : n.Id}) SET c = n)
http://docs.neo4j.org/chunked/stable/query-foreach.html
The other option, coming soon, is that you will be able to write something like this in Cypher:
LOAD CSV WITH HEADERS FROM 'file://c:/temp/input.csv' AS n
MERGE (c:Label { Id : n.Id })
SET c = n
https://github.com/davidegrohmann/neo4j/blob/2.1-fix-resource-failure-load-csv/community/cypher/cypher/src/test/scala/org/neo4j/cypher/LoadCsvAcceptanceTest.scala

Dynamically return from Neo4jclient

I know how to dynamically create my MATCH and WHERE clauses, and in my situation it's unfortunately something I've got to do... SO, I have something like (and this is a contrived example):
var query = client
.Cypher
.Match("(u1:User)-[:LINKED]->(w:Web)","(u2:User)-[:LINKED]->(w:Web)","(u3:User)-[:LINKED]->(w:Web)")
.Where("u1.User.Id = {u1Param}").WithParam("u1Param", 1)
.AndWhere("u2.User.Id = {u2Param}").WithParam("u2Param", 2)
.AndWhere("u3.User.Id = {u3Param}").WithParam("u3Param", 3);
The Match and Where are dynamically generated based on user input, and this generates the correct Cypher.
MATCH
(u1:User)-[:LINKED]->(w:Web), (u2:User)-[:LINKED]->(w:Web), (u3:User)-[:LINKED]->(w:Web)
WHERE
u1.User.Id = 1
AND u2.User.Id = 2
AND u3.User.Id = 3
On the server (in Cypher) I can do something like:
RETURN u1, u2, u3
and I'll get the 3 nodes I'm looking for. My problem is the general way of Returning data via the neo4jclient is via an Expression, so, to replicate I would do:
query.Return(
(u1, u2, u3) =>
{
U1 = u1.As<User>(),
U2 = u2.As<User>(),
U3 = u3.As<User>()
});
I don't know that there will only be '3' responses, there could be many more, or indeed less. Any ideas on how to achieve this? Either as is, or in a totally different way, I'm open to all options.
I'd like to something like:
query.Return<User>("u1", "u2", "u3"); //returning IEnumerable<User>
or maybe:
query.Return<User>("u1").AndReturn("u2").AndReturn("u3");
I know I can revert for these queries to calling the db directly through HttpClient or some such, but I'd rather avoid that if I can. To be honest I'd be happy with parsing them all to dynamic, but that's not possible at present (or is it??? :)).
Neo4jClient doesn't have any good solutions for dynamic return shapes like this.
In this contrived example at least, can you just flatten the nodes into a single list?
.Return<IEnumerable<User>>("[u1, u2, u3]")
You can easily tell which is which with the Id property that you're using in the WHERE clause but would also be available in C#.

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

Resources