Create node and relationship given parent node - neo4j

I am creating a word tree but when I execute this cypher query:
word = "MATCH {} MERGE {}-[:contains]->(w:WORD {{name:'{}'}}) RETURN w"
.format(parent_node, parent_node, locality[i])
where parent_node has a type Node
It throws this error:
py2neo.cypher.error.statement.InvalidSyntax: Can't create `n8823` with properties or labels here. It already exists in this context
formatted query looks like this:
'MATCH (n8823:HEAD {name:"sanjay"}) MERGE (n8823:HEAD {name:"sanjay"})-[:contains]->(w:WORD {name:\'colony\'}) RETURN w'

The formatted query is broken and won't work, but I also don't see how that could be what the formatted query actually looks like. When you do your string format you pass the same parameter (parent_node) twice so the final string should repeat whatever that parameter looks like. It doesn't, and instead has two different patterns for the match and merge clauses.
Your query should look something like
MATCH (n8823:Head {name: "sanjay"})
MERGE (n8823)-[:CONTAINS]->(w:Word {name: "colony"})
RETURN w
It's probably a bad idea to do string formatting on a Node object. Better to either use property values from your node object in a Cypher query to match the right node (and only the variable that you bind the matched node to in the merge clause) or use the methods of the node object to do the merge.

Although the MERGE clause is able to bind identifiers (like n8823), Cypher unfortunately does not allow MERGE to re-bind an identifier that had already been bound -- even if it would not actually change the binding. (On the other hand, the MATCH clause does allow "rebinding" to the same binding.) Simply re-using a bound identifier is OK, though.
So, the workaround is to change your Cypher query to re-use the bound identifier. Also, the recommended way to dynamically specify query data without changing the overall structure of a query is to use "query parameters". For py2neo, code along these lines should work for you (note that the parent_name variable would contain a name string, like "sanjay"):
from py2neo import Graph
graph = Graph()
cypher = graph.cypher
results = cypher.execute(
"MATCH (foo:{name:{a}}) MERGE (foo)-[:contains]->(w:WORD {{name:'{b}'}}) RETURN w",
a=parent_name, b=locality[i])

Related

Neo4j Subgraph Projection using a string inside string query

Working on a project, I was trying to reduce the number of variables to make something easier to visualize for creating embeddings and checking if they work.
I realized there was a projection and a subprojection. I can definitely create a new neo4j graph, but that seems like a slow solution.
so just following the tutorial, they have
CALL gds.graph.project(
'apps_undir',
['App', 'Genre']
{Genre_Category: {orientation: 'UNDIRECTED'}}
)
then something like
CALL gds.beta.graph.project.subgraph(
'subapps',
'apps_undir',
"n:App OR (n:Genre AND n.name = 'Action' OR n.name = 'RPG')",
'*'
)
I realize this isn't python, but it's the idea I'm trying to express. With the string query as 'n:App OR (n:Genre AND n.name = Action OR n.name = RPG)' I get the error:
Failed to invoke procedure gds.beta.graph.project.subgraph: Caused by: org.neo4j.gds.beta.filter.expression.SemanticErrors: Semantic errors while parsing expression:
Invalid variable `Action`. Only `n` is allowed for nodes
Invalid variable `RPG`. Only `n` is allowed for nodes
Unknown property `name`.
Unknown property `name`.
the error produced is
"Neo.ClientError.Statement.SyntaxError
Invalid input 'subgraph': expected"
As subgraph is only in beta functionality isn't great, but all node names apparently need to be n,
for the actual subgraph, and performing an embedding on that
if it helps, this was taken from a steam database scrape from 2016 and a couple csv values are below:
appid;Genre
8890;RPG
8890;Strategy
10530;Action
10530;RPG
15540;Indie
15560;Action
15620;Strategy
There are a couple of problems with your workflow. When you project a graph in GDS with the following command, it doesn't include any node properties by default.
CALL gds.graph.project(
'apps_undir',
['App', 'Genre']
{Genre_Category: {orientation: 'UNDIRECTED'}}
)
There are ways to include node properties in the graph projections, however string format is not supported. Therefore, you cannot project the name property which appears to be a string. To achieve what you want to do you should probably use Cypher projection.
CALL gds.graph.project.cypher('subapps',
'MATCH (n) WHERE n:App OR (n:Genre AND n.name IN ["Action", "RPG"]) RETURN id(n) AS id',
'MATCH (s)-[:Genre_Category]-(t) RETURN id(s) AS source, id(t) AS target',
{validateRelationship:false})
A couple of pointers for Cypher projection. To define the relationships I have used (s)-[:Genre_Category]-(t) pattern. Notice the lack of relationship direction. By avoiding the relationship direction definition, the relationships will be projected as "undirected". You need to include the validateRelationship parameter since you perform node filtering in the node projection, but not in the relationship projection.

Cypher Access Nodes of Variable Length Path

I have the following problem:
I have a MATCH clause like this one:
MACTH (A)-[*1..]->(B)
Now, if this pattern matches, I need to somehow duplicate the -[*1..]-> part and replace all node in it tat have a specific type with nodes of another type.
for example if I had this graph:
(X1:A)-[:r]->(X2:B)-[:r]->(X3:E)-[:r]->(X4:A)
I might want to get this graph after applying a rule MACTH (A:A)-[*1..]->(B:A) something something
(X1:A)-[:r]->(X2:B)-[:r]->(X3:E)-[:r]->(X4:A) , (X1)-[:r]->(X5:C)-[:r]->(X3)-[:r]->(X4)
by duplicating the path between (A) and (B) and replacing all nodes of type B with type C
But how can I access the -[*1..]-> part of the query?
Try this
Match (p:Person)-[:RELATED*1..]->(e)
with collect(distinct(id(e))) as ids Match (c:Country) where id(c) in ids
remove c:Country set c:NewLabel
return *
on data
Create (p:Person)-[:RELATED]->(b:Person)-[:RELATED]->(c:Client)-[:RELATED]->(e:Country) return *

Find a graph node by a field, update all other fields

I have a Neo4J graph database where I want to store users and relationships between them.
I want to be able to update a User node that I find them by GUID with data contained in a .Net User object. Ideally I'd like to know how to do that in Neo4JClient but even plain Cypher query would do.
Ideally I'd like to use the whole object, not knowing what properties have been modified, and replace all of them - including array properties - unlike the example below that knows PhoneNumber is to be updated
Something like this:
MATCH (n:`User` {Id:'24d03ce7-8d23-4dc3-a13b-cffc0c7ce0d8'})
MERGE (n {PhoneNumber: '123-123-1234'})
RETURN n
The problem with the code above is that MERGE redefines the n
and I get this error:
n already declared (line 2, column 8) "MERGE (n {PhoneNumber: '123-123-1234'})" ^
If all you want to do is completely replace all the properties of existing nodes, do not use MERGE. You should just use MATCH, and SET all the properties. Something like this:
MATCH (n:`User` {Id:'24d03ce7-8d23-4dc3-a13b-cffc0c7ce0d8'})
SET n = {PhoneNumber: '123-123-1234', Age: 32}
RETURN n;
On the other hand, if you want to create a new node iff one with the specified Id does not yet exist, and you also want to completely replace all the properties of the new or existing node, you can do this:
MERGE (n:`User` {Id:'24d03ce7-8d23-4dc3-a13b-cffc0c7ce0d8'})
SET n = {PhoneNumber: '123-123-1234', Age: 32}
RETURN n;
Note: in the above queries, all the existing properties of n would be deleted before the new properties are added. Also, the map assigned to n in the SET clause can be passed to the query as a parameter (so no hardcoding is needed).

MATCH prior to MERGE in single Cypher query confuses ON MATCH and ON CREATE

I've run into this on Neo4j 2.1.5. I have a query which I'm issuing from Node.js using the Neo4j REST API. The point of this query is to be able to create or update a given Node and set its state (including labels and properties) to some known state. The MATCH and REMOVE clause prior to the WITH is to work around the fact that there's no direct way to remove all of a Node's labels nor is there a way to update a Node's labels with a given set of labels. You have to explicitly remove the labels you don't want and add the labels you do want. And there's no way to remove labels in the MERGE clause.
A somewhat simplified version of the query looks like:
MATCH (m {name:'Brian'})
REMOVE m:l1:l2
WITH m
MERGE (n {name:'Brian'})
ON MATCH SET n={mprops} ON CREATE SET n={cprops}
RETURN n
where mprops = {updated:true, created:false} and cprops = {updated:false, created:true}. I do this so that in a single Cypher query I can remove all of the Node's existing labels and set new labels using the ON MATCH clause. The problem is that including the initial MATCH seems to confuse the ON MATCH vs ON CREATE logic.
Assuming the Brian Node already exists, the result of this query should show that n.created = false and n.updated = true. However, I get the opposite result, n.created=true, n.updated=false. If I remove the initial MATCH (and WITH) clause and execute only the MERGE clause, the results are as expected. So somehow, the inclusion of the MATCH clause causes the MERGE clause to think that a CREATE vs MATCH is happening.
I realize this is a weird use of the WITH clause, but it did seem like it would work around the limitation in manipulating labels. And Cypher thinks that it's valid Cypher. I'm assuming this is just a bug and an edge case, but I wanted to get others insights and possible alternatives before I report it.
I realize that I could have created a transaction and issued the MATCH and MERGE as separate queries within that transaction, but there are reasons that this does not work well in the design of the API I'm writing.
Thanks!
If you prefix your query with MATCH it will never execute if there is no existing ('Brian') node.
You also override all properties with your SET n = {param} you should use SET n += {param}
MERGE (n:Label { name:'Brian' })
ON MATCH SET n += {create :false,update:true }
ON CREATE SET n += {create :true,update:false }
REMOVE n:WrongLabel
RETURN n
I don't see why your query would not work, but the issues brought up by #FrobberOfBits are valid.
However, logically, your example query is equivalent to this one:
MATCH (m {name:'Brian'})
REMOVE m:l1:l2
SET m={mprops}
RETURN m
This query is simpler, avoids the use of MERGE entirely, and may avoid whatever issue you are seeing. Does this represent what you were trying to do?

Neo4J Arrays in MATCH query

The intention of my Query is to mark similar words.
CREATE CONSTRAINT ON (n:Word) ASSERT n.title IS UNIQUE
MATCH (n) WHERE ID(n)={id}
MERGE (o:Word{title:{title}})
WITH n,o MERGE n-[r:SIMILAR{location:'{location}'}]->o
RETURN ID(o)
n is a existing Word. I want to create the relationsship & the other Word (o) if they don't exist yet.
The Problem with this query is, that it works for one title, but if I use a Array with titles the title of the Word o is the whole Array.
Can you suggest me another Query that does the same and/or a way to pass multiple values to title.
I'm using the Neography Gem on Rails e.g. the REST API
To use individual values in a parameter array you can use FOREACH, something like
MATCH (n)
WHERE ID (n) = {id}
FOREACH (t IN {title} |
MERGE (o:Word {title:t})
MERGE n-[:SIMILAR]->o
)
If you want to pass location also as a parameter (it is actually a string literal in your current query), such that merge operations for n should happen for each title, location pair in a parameter array, you can try
FOREACH (map IN {maps} |
MERGE (o:Word {title:map.title})
MERGE n-[:SIMILAR {location:map.location}]->o
)
with a parameter that looks something like
{
"maps": [
{
"title":"neography",
"location":"1.."
},{
"title":"coreography",
"location":"3.."
}
]
}
Other suggestions:
It's usually not great to look up nodes by internal id from parameter. In some cases when chaining queries it may be fine, but in most cases label index lookup would be better: MATCH (n:Word {title:"geography"})
If you are not using the transactional cypher endpoint, give it a shot. You can then make one or more calls with one or more queries in each call within one transaction. Performance improves and you may find you don't need to send the more complex parameter object, but can send many simple queries.

Resources