c# NEO4J v3 can't create relationship - neo4j

I have a c# project that I'm working with NEO4J 2.3.2 , after updating to version 3 I start to see that my system always fail creating relationships . thus is my code
View userView = new View { parent = parent, timestamp = currentTime };
WebApiConfig.GraphClient.Cypher
.Match("(user123:BaseUser{guid: '" + isAuto + "'})", "(y:YoutubeItem{videoId: '" + itemid + "'})")
.CreateUnique("user123-[r:VIEW]->y")
.Set("r = {userView}")
.WithParam("userView", userView)
.ExecuteWithoutResults();
and this is the exception
"SyntaxException: Parentheses are required to identify nodes in patterns, i.e. (user123) (line 2, column 15 (offset: 127))\n\"CREATE UNIQUE user123-[r:VIEW]->y\r\"\n ^"
and when i go back to the old version everything is working well, what should i do?

Cypher now enforces the requirement that nodes must be surrounded by parentheses.
So, in your query, the CreateUnique line needs to look like this:
.CreateUnique("(user123)-[r:VIEW]->(y)")
By the way, you should be using parameters for injecting the isAuto and itemId values. You are already doing that with userView.

Related

Getting node/edge creation/removal statistics

I am running a Python (3.8) script which uses pip library neo4j 4.0.0 to interact with a community edition neo4j 4.1.1 server.
I am running many queries which use MERGE to update or create if nodes and relationships don't exist.
So far this is working well, as the database is getting the data as intended.
From my script's side, I would like however to know how many nodes and edges were created in each query.
The issue is that in these Cypher queries that I send to the database from my script, I call MERGE more than one time and also use APOC procedures (though the APOC ones are just for updating labels, they don't create entities).
Here is an example of a query:
comment_field_names: List[str] = list(threads[0].keys())
cypher_single_properties: List[str] = []
for c in comment_field_names:
cypher_single_properties.append("trd.{0} = {1}.{0}".format(c, "trd_var"))
cypher_property_string: str = ", ".join(cypher_single_properties)
with driver.session() as session:
crt_stmt = ("UNWIND $threads AS trd_var "
"MERGE (trd:Thread {thread_id:trd_var.thread_id}) "
"ON CREATE SET PY_REPLACE "
"ON MATCH SET PY_REPLACE "
"WITH trd AS trd "
"CALL apoc.create.addLabels(trd, [\"Comment\"]) YIELD node "
"WITH trd as trd "
"MERGE (trd)-[r:THREAD_IN]->(Domain {domain_id:trd.domain_id}) "
"ON CREATE SET r.created_utc = trd.created_utc "
"ON MATCH SET r.created_utc = trd.created_utc "
"RETURN distinct 'done' ")
crt_params = {"threads": threads}
# Insert the individual properties we recorded earlier.
crt_stmt = crt_stmt.replace("PY_REPLACE", cypher_property_string)
run_res = session.run(crt_stmt, crt_params)
This works fine and the nodes get created with the properties passed from the threads Dict which is passed through the variable crt_params to UNWIND.
However, the Result instance in run_res does not have any ResultSummary inside it with a SummaryCounters instance for me to access statistics of created nodes and relations.
I suspect this is because of:
"RETURN distinct 'done' "
However, I am not sure if this is the reason.
Hoping someone may be able to help me set up my queries so that, no matter the number of MERGE operations I perform, I get the statistics for the whole query that was sent in crt_stmt.
Thank you very much.
When using the earlier neo4j version, you could write n = result.summary().counters.nodes_created, but from 4.0 the summary() method does not exist.
Now I found from https://neo4j.com/docs/api/python-driver/current/breaking_changes.html that Result.summary() has been replaced with Result.consume(), this behaviour is to consume all remaining records in the buffer and returns the ResultSummary.
You can get all counters by counters = run_res.consume().counters

neo4j cypher create relation between two nodes based on an attribute String value

I have a Node with Label Experiment with an attribute called ExperimentName.
This ExperimentName is based on the concatenation of 3 different variables
"Condition (ExperimentProfile1) Dose"
Example :
Control diet (MOA77) LD
Control gavage(MOA66) HD
I have another Node called ExperimentMapper
it has 3 attributes :
- Condition
- ExperimentProfile
- Dose
I would like to create a Relation between Node Experiment and Node ExperimentMapper when experimentName is the results of the 3 attributes assembled.
I have tried to use Regex but the query was extremely slow and took forever..
Any help?
This is my cypher but it is taking forever despite me creating indexes
MATCH (mxpExperiment:MxpExperiment) OPTIONAL MATCH (otuExperimentMapper:OtuExperimentMapper)
WHERE mxpExperiment.name CONTAINS otuExperimentMapper.Condition
AND mxpExperiment.name CONTAINS otuExperimentMapper.Experiment
AND mxpExperiment.name CONTAINS otuExperimentMapper.dose
CREATE (mxpExperiment)-[:OTU_EXPERIMENT_MAPPER]->(otuExperimentMapper)
RETURN mxpExperiment, otuExperimentMapper
I think that you need to go from the side of the Experiment Mapper.
First you need to create an index:
CREATE INDEX ON :MxpExperiment(name)
Then the query can be as follows:
MATCH (otuExperimentMapper:OtuExperimentMapper)
WITH otuExperimentMapper,
otuExperimentMapper.Condition + ' (' +
otuExperimentMapper.Experiment + ') ' +
otuExperimentMapper.dose AS name
MATCH (mxpExperiment:MxpExperiment) WHERE mxpExperiment.name = name
MERGE (mxpExperiment)-[:OTU_EXPERIMENT_MAPPER]->(otuExperimentMapper)
RETURN mxpExperiment, otuExperimentMapper

Two values as DISTINCT in Neo4j query

I am sending a query to Neo4j database and I want to return only the items that have two numbers - tsneX and tsneY as a distinct 'point'. So, tsneX can be equal to tsneX, but the second number tsneY should not in that case. Here is my query:
MATCH (c:Cell)-[ex:EXPRESSES]->(g:Gene { geneName: "' + geneName + '" })
RETURN ex.expr, c.tsneX, c.tsneY;
So, I want smth like
MATCH (c:Cell)-[ex:EXPRESSES]->(g:Gene { geneName: "' + geneName + '" })
WITH DISTINCT (c.tsneX, c.tsneY) AS point
RETURN ex.expr, point;
Example:
ex.expr c.tsneX c.tsneY
1. 4 1.2 1.2
2. 5 2.1 3.3
3. 1 1.2 1.2
One of them - 1st or 3rd need to be dropped since their tsneX and tsneY coordinates are equal respectively to each other. So, I would want only 1st and 2nd to be returned but 3rd to be dropped since ex.expr is higher in the 1st one.
Any suggestions would be greatly appreciated.
So you want the pair of [c.tsneX, c.tsneY], and in the case there's more results with the same point you only want the higheset ex.expr. This should do the trick:
MATCH (c:Cell)-[ex:EXPRESSES]->(g:Gene { geneName: "' + geneName + '" })
RETURN [c.tsneX, c.tsneY] AS point, max(ex.expr) as expr
If you want the point as an object rather than a list, you can instead do:
{x:c.tsneX, y:c.tsneY} AS point
In either case, the max(ex.expr) aggregation function will ensure that the remaining non-aggregation value, point, is distinct.

Neo4j spatial withinDistance only returns one node

I am using the spatial server plugin for Neo4j 2.0 and manage to add Users and Cities with their geo properties lat/lon to a spatial index "geom". Unfortunately I cannot get the syntax right to get them back via Neo4jClient :( What I want is basically:
Translate the cypher query START n=node:geom('withinDistance:[60.0,15.0, 100.0]') RETURN n; to Neo4jClient syntax so I can get all the users within a given distance from a specified point.
Even more helpful would be if it is possible to return the nodes with their respective distance to the point?
Is there any way to get the nearest user or city from a given point without specify a distance?
UPDATE
After some trial and error I have solved question 1 and the problem communicating with Neo4j spatial through Neo4jClient. Below Neo4jClient query returns 1 user but only the nearest one even though the database contains 2 users who should be returned. I have also tried plain cypher through the web interface without any luck. Have I completely misunderstood what withinDistance is supposed to do? :) Is there really no one who can give a little insight to question 2 and 3 above? It would be very much appreciated!
var queryString = string.Format("withinDistance:[" + latitude + ", " + longitude + ", " + distance + "]");
var graphResults = graphClient.Cypher
.Start(new { user = Node.ByIndexQuery("geom", queryString) })
.Return((user) => new
{
EntityList = user.CollectAsDistinct<UserEntity>()
}).Results;
The client won't let you using the fluent system, the closest you could get would be something like:
var geoQuery = client.Cypher
.Start( new{n = Node.ByIndexLookup("geom", "withindistance", "[60.0,15.0, 100.0]")})
.Return(n => n.As<????>());
but that generates cypher like:
START n=node:`geom`(withindistance = [60.0,15.0, 100.0]) RETURN n
which wouldn't work, which unfortunately means you have two options:
Get the code and create a pull request adding this in
Go dirty and use the IRawGraphClient interface. Now this is VERY frowned upon, and I wouldn't normally suggest it, but I don't see you having much choice if you want to use the client as-is. To do this you need to do something like: (sorry Tatham)
((IRawGraphClient)client).ExecuteGetCypherResults<Node<string>>(new CypherQuery("START n=node:geom('withinDistance:[60.0,15.0, 100.0]') RETURN n", null, CypherResultMode.Projection));
I don't know the spatial system, so you'll have to wait for someone who does know it to get back to you for the other questions - and I have no idea what is returned (hence the Node<string> return type, but if you get that worked out, you should change that to a proper POCO.
After some trial and error and help from the experts in the Neo4j google group all my problems are now solved :)
Neo4jClient can be used to query withinDistance as below. Unfortunately withinDistance couldn't handle attaching parameters in the normal way so you would probably want to check your latitude, longitude and distance before using them. Also those metrics have to be doubles in order for the query to work.
var queryString = string.Format("withinDistance:[" + latitude + ", " + longitude + ", " + distance + "]");
var graphResults = graphClient.Cypher
.Start(new { city = Node.ByIndexQuery("geom", queryString) })
.Where("city:City")
.Return((city) => new
{
Entity = city.As<CityEntity>()
})
.Limit(1)
.Results;
Cypher cannot be used to return distance, you have to calculate it yourself. Obviously you should be able to use REST http://localhost:7474/db/data/index/node/geom?query=withinDistance:[60.0,15.0,100.0]&ordering=score to get the score (distance) but I didn't get that working and I want to user cypher.
No there isn't but limit the result to 1 as in the query above and you will be fine.
A last note regarding this subject is that you should not add your nodes to the spatial layer just the spatial index. I had a lot of problems and strange exceptions before figure this one out.

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

Resources