I just got started on Neo & tried to look for prior questions on this topic. I need help to rename one of the property keys.
I created the following node:
CREATE (Commerce:Category {title:' Commerce', Property:'Category', Owner:'Magic Pie', Manager:'Simple Simon'})
Now want to rename title to name. Is there a way to do it? I don't want to delete the node as there are 100's of nodes with the property "title".
Yes, you want to SET a new property name with the value of the old property title. And then REMOVE the old property title. Something like this...
MATCH (c:Category)
WHERE c.name IS NULL
SET c.name = c.title
REMOVE c.title
If you have MANY nodes, it is advisable to perform the operation in smaller batches. Here is an example of limiting the operation to 10k at a time.
MATCH (c:Category)
WHERE c.name IS NULL
WITH c
LIMIT 10000
SET c.name = c.title
REMOVE c.title
another solution would be using APOC functions:
MATCH (n) WHERE ID(n) = 1234
WITH *, collect(n) AS nodes // nodes must be converted into a collection first
CALL apoc.refactor.rename.nodeProperty("oldKey ", "newKey", nodes)
// rename doesn't work if the key has strings ^ postfixed in it
YIELD batches, total, timeTaken, committedOperations
RETURN *
in the case you accidentially added strings at the end (it's possible during create) the property cannot be accessed via:
SET n.newKey = n.oldKey
REMOVE n.oldKey
then you must use:
SET n.newKey = n.`oldKey `
REMOVE n.`oldKey `
this works
Related
I'm using Apoc.load.jdbc to get data from Oracle Database and create row from it, here is the code:
call apoc.load.driver('oracle.jdbc.driver.OracleDriver')
WITH "jdbc:oracle:thin:#10.82.14.170:1521/ORACLE" as url
CALL apoc.load.jdbc(url,"select * from Patients",[],{credentials:{user:'KCB',password:'123'}}) YIELD row
Create (p:Person) set p=row
return p
That code work fine but I want to check row property before create it. Such as:
If (row.ID!=p.ID)
{
set p=row
}
Else{Not set}
How can I do that with my code? Thanks a lot!
As #TomažBratanič mentions in his answer, your desired conditional check makes no sense. That is, unless you also replace your CREATE clause.
Your query uses CREATE to always create a new p with no properties. So row.ID <> p.ID will always be true, and you'd always be executing the SET clause.
However, I believe your real intention is to avoid changing an existing Person node (and to avoid creating a duplicate Person node for the same person). So, below is a query that uses MERGE and ON CREATE to do that. I assume that people have unique ID values.
CALL apoc.load.driver('oracle.jdbc.driver.OracleDriver')
CALL apoc.load.jdbc(
"jdbc:oracle:thin:#10.82.14.170:1521/ORACLE",
"select * from Patients",[],{credentials:{user:'KCB',password:'123'}}
) YIELD row
MERGE (p:Person {ID: row.ID})
ON CREATE SET p = row
RETURN p
Also, you should consider creating an index (or uniqueness constraint) on :Person(ID) to optimize the lookup of existing Person nodes.
You can use a CASE statement to achieve this:
call apoc.load.driver('oracle.jdbc.driver.OracleDriver')
WITH "jdbc:oracle:thin:#10.82.14.170:1521/ORACLE" as url
CALL apoc.load.jdbc(url,"select * from Patients",[],{credentials:{user:'KCB',password:'123'}}) YIELD row
Create (p:Person) set p = CASE WHEN row.ID <> p.id THEN row ELSE null END
return p
However, this statement does not make sense, because you always create a new Person, so the row.ID will never be the same as p.id.
My problem is, after I insert several nodes with uri properties of the type:
"http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#Red"
Later, I want to extract the part after hashtag (which is "Red" for this case), so I use the split function and create property name with value tail(records):
MATCH (n) WITH split (n.uri, '#') AS records, n
WHERE head(records) = 'http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine'
SET n.name = tail(records)
after successfully creating the property name with appropriate name for a set of nodes, I check (everything perfect for now):
Match (n) Return keys(n)
I create a label (concept) for all the nodes:
MATCH (n) SET n :concept RETURN n
Later trying to access the value of property "name":
Match (n{name: 'Red'}) RETURN n
or
Match (n:concept{name: 'Red'}) RETURN n
I am getting the empty response (obviously it is not connected to label created, as even before I couldn't access it). I would appreciate your help. Thank you!
The TAIL(x) function returns a list (of all values in x after the first), not a scalar value. Therefore, the name in your example would have the value ["Red"], not "Red".
Instead of:
SET n.name = tail(records)
your Cypher code should have used the following (assuming your uri values always embed a "#"):
SET n.name = records[1]
Initial setup of the sample database is provided link to console
There are various cases and within each case, there are performers(with properties id and name). This is the continuation of problems defined problem statement and solution to unique node creation
The solution in the second link is (credits to Christophe Willemsen
)
MATCH (n:Performer)
WITH collect(DISTINCT (n.name)) AS names
UNWIND names as name
MERGE (nn:NewUniqueNode {name:name})
WITH names
MATCH (c:Case)
MATCH (p1)-[r:RELATES_TO]->(p2)<-[:RELATES]-(c)-[:RELATES]->(p1)
WITH r
ORDER BY r.length
MATCH (nn1:NewUniqueNode {name:startNode(r).name})
MATCH (nn2:NewUniqueNode {name:endNode(r).name})
MERGE (nn1)-[rf:FINAL_RESULT]->(nn2)
SET rf.strength = CASE WHEN rf.strength IS NULL THEN r.value ELSE rf.strength + r.value END
This solution achieved what was asked for.
But I need to achieve something like this.
foreach (Case.id in the database)
{
foreach(distinct value of r.Length)
{
//update value property of node normal
normal.value=normal.value+0.5^(r.Length-2)
//create new nodes and add the result as their relationship or merge it to existing one
MATCH (nn1:NewUniqueNode {name:startNode(r).name})
MATCH (nn2:NewUniqueNode {name:endNode(r).name})
MERGE (nn1)-[rf:FINAL_RESULT]->(nn2)
//
rf.strength=rf.strength + r.value*0.5^(r.Length-2);
}
}
The problem is to track the change in the case and then the r.Length property. How can it be achieved in Cypher?
I will not redo the last part, where setting strengths.
One thing though, in your console link, there is only one Normal node, so why do you need to iterate over each case, you can just match distinct relationships lengths.
By the way for the first part :
MATCH (n:Case)
MATCH (n)-[:RELATES]->()-[r:RELATES_TO]->()<-[:RELATES]-(n)
WITH collect(DISTINCT (r.length)) AS lengths
MATCH (normal:Normal)
UNWIND lengths AS l
SET normal.value = normal.value +(0.5^l)
RETURN normal.value
Explanations :
Match the cases
Foreach case, match the RELATES_TO relationships of Performers for that Case
collect distinct lengths
Match the Normal node, iterate the the distinct lengths collection and setting the proper value on the normal node
I have a problem in querying for two nodes with one similar property, one different property and having the same label. For one property, the property is the same between both nodes, i.e. both have a property called "Name" and both have the same value ("Data Storage"). For the other property though called "Note", they have different values. Both share the same label called "Issue". When I use the query below, I get both nodes.
match (n:Issue) where n.name="Data Storage" return n;
However, when I query with the following query...
match (n:Issue) where n.name="Data Storage" and n.note="xxxx" return n;
...it only works for one of the nodes and not for the other. I've tried creating the node which doesn't query and it seems to work fine. But I also did it with a different label. Is this some bug around not being able to query a node having the same label and sharing at least one common property?
match (n:Issue) where n.name="Data Storage" and n.note="xxxx" return n;
will match all nodes with label Issue, the value of the "name" property = "Data Storage" AND the value of the "note" property = "xxxx".
As you've described the 2 nodes, the value of the note property is different on each. The one matching xxxx is the only one that can be returned.
What is the goal of this query?
If this was your real question.
Query a node having the same label and sharing at least one common property?
you can try this for each property and do a union over all
MATCH (n:Issue)
WITH n.name, collect(n) as nodes, count(*) as cnt
WHERE cnt > 1
RETURN nodes
or this will be less performant
MATCH (n:Issue)
WITH n
MATCH (m:Issue)
WHERE m<>n AND (n.name = m.name OR n.note = m.note)
RETURN n,m
I have a relatively large set of nodes, and I want to find all pairs of nodes that have matching property values, but I don't know or care in advance what the property value is. This is basically an attempt to find duplicate nodes, but I can limit the definition of a duplicate to two or more nodes that have the same property value.
Any ideas how to proceed? Not finding any starting points in the neo4j docs. I'm on 1.8.2 community edition.
EDIT
Sorry for not being clear in the initial question, but I'm talking about doing this through Cypher.
Cypher to count values on a property, returning a collection of nodes as well:
start n=node(*)
where has(n.prop)
with n.prop as prop, collect(n) as nodelist, count(*) as count
where count > 1
return prop, nodelist, count;
Example on console: http://console.neo4j.org/r/k2s7aa
You can also do an index scan with the property like so (to avoid looking at nodes that don't have this property):
start n=node:node_auto_index('prop:*') ...
2.0 Cypher with a label Label:
match (n:Label)
with n.prop as prop, collect(n) as nodelist, count(*) as count
where count > 1
return prop, nodelist, count;
Update for 3.x: has was replaced by exists.
You can try this one who does which I think does whatever you want.
START n=node(*), m=node(*)
WHERE
HAS(n.name) AND HAS (m.name) AND
n.name=m.name AND
ID(n) <ID(m)
RETURN n, m
http://console.neo4j.org/?id=xe6wmt
Both nodes should have a name property. name should be equal for both nodes and we only want one pair of the two possibilites which we get via the id comparison. Not sure about performance - please test.
What about the following approach:
use getAllNodes to get an Iterable over all nodes.
using getPropertyKeys and getProperty(key) build up a java.util.Map containing all properties for a node. Calculate the map's hashCode()
build up a global Map using the hashCode as key and a set of node.getId() as values
This should give you the candidates for being duplicate. Be aware of the hashCode() semantics, there might be nodes with different properties mapping to the same hashCode.
Neo4j 3.1.1
HAS is no longer supported in Cypher, please use EXISTS instead.
If you want to find nodes with specific property, the Cyper is as follows:
MATCH (n:NodeLabel) where has(n.NodeProperty) return n
With Neo4j 3.3.4 you can simply do the following:
MATCH (n) where EXISTS(n.propertyName) return n
Simply change propertyName to whatever property you are looking to find.
The best/easiest option is to do something like a local Map. If you did something like this, you could create code like this:
GlobalGraphOperations ggo = GlobalGraphOperations.at(db);
Map<Object, Node> duplicateMap = new HashMap<Object, Node>();
for (Node node : ggo.getAllNodes()) {
Object propertyValue = node.getProperty("property");
Node existingNode = duplicateMap.get(propertyValue);
if (existingNode == null) {
duplicateMap.put(propertyValue, node);
} else {
System.out.println("Duplicate Node. First Node: " + existingNode + ", Second Node: " + node);
}
}
This would print out a list. If you needed to do more, like remove these nodes, you could do something in the else.
Do you know the property name? Will this be multiple properties, or just duplicates of a single name/value pair? If you are doing multiple properties, just create a map for each property you have.
You can also use an index on that property. Then for a given value retrieve all the nodes. The advantage is that you can also query for approximations of the value.