Set degree of Nodes - neo4j

I have a graph in which I am keeping the degree of a node as a property called "degree" in the node.
What I need is when I create an edge between two nodes, I need to increment the degree of the two nodes.
For creating unique edges I am using "CREATE UNIQUE" for the edges. So if I need to increment the property "degree" of the corresponding nodes, I need to use "ON CREATE" and "ON MATCH" as it is for "MERGE".
But I can't use the ON CREATE and ON MATCH with CREATE UNIQUE. So whats the proper way of using ON CREATE and ON MATCH with CREATE UNIQUE?
This is the way I am trying:
MATCH (n1:PER {Node_Id:"X"}), (n2:PER {Node_Id:"Y"}) WHERE n1.Node_Id<>n2.Node_Id CREATE UNIQUE (n1)-[r:PER_PER {Doc_Id:"st_new", Event_Class:"EC_1", Event_Instance:"EI_1"}]-(n2) ON CREATE SET n1.degree = n1.degree + 1, n2.degree = n2.degree + 1

Not sure why you want to store the degree as a property. Neo4j has a getDegree() function on API level, see http://neo4j.com/docs/stable/javadocs/org/neo4j/graphdb/Node.html#getDegree(). Cypher is not yet using this everywhere possible but for some patterns it already does.
Nevertheless to answer your question: just use MERGE instead of CREATE UNIQUE for eventually establishing the relationship:
MATCH (n1:PER {Node_Id:"X"}), (n2:PER {Node_Id:"Y"})
WHERE n1.Node_Id<>n2.Node_Id
MERGE (n1)-[r:PER_PER {Doc_Id:"st_new", Event_Class:"EC_1", Event_Instance:"EI_1"}]-(n2)
ON CREATE SET n1.degree = n1.degree + 1, n2.degree = n2.degree + 1

If you do have a good reason to store node degrees as a property, please have a look at one of our modules called RelCount, which has been build exactly for what you need to do. It is described in detail in my thesis.
However, as Stefan points out, have a go with getDegree() first and only if that isn't fast enough, or you need to get degree based on some relationship property values as well, use RelCount.

Related

Add Integer Number to existing Values - Neo4j

Using Neo4j.
I would like to add a integer number to values already existing in properties of several relationships that I call this way:
MATCH x=(()-[y]->(s:SOL{PRB:"Taking time"})) SET y.points=+2
But it doesn't add anything, just replace by 2 the value I want to incremente.
To achieve this use
SET y.points = y.points + 2
From your original question it looks like you were trying to use the Addition Assignment operator which exists in lots of languages (e.g. python, type/javascript, C#, etc.). However, in cypher += is a little different and is designed to do this in a way which allows you to add or update properties to or on entire nodes or relationships based on a mapping.
If you had a parameter like the below (copy this into the neo4j browser to create a param).
:param someMapping: {a:1, b:2}
The query below would create a property b on the node with value 2, and set the value of property a on that node to 1.
MATCH (n:SomeLabel) WHERE n.a = 0
SET n+= $someMapping
RETURN n

Neo4j: Maintaining node counts

I'm investigating the use of Neo4j to detect potentially fraudulent card transactions in near real time. I receive details of a customer and a card they've just used from our on-line systems. What I'm trying to do here is create new nodes for the customer and card if they don't exist, then establish the relationship between them.
Whenever the customer uses the card I want to set the time the card was last used, in addition, if this is the first time this customer-->card relationship has been seen, update totals of the number of cards the customer is associated with and the number of customers associated with the card.
The Cypher below seems to work, however I think it will re-evaluate the counts every time the relationship is seen, not just on the create. Is it possible to use the ON MATCH and ON CREATE in this statement to limit the unnecessary processing?
MERGE (c:customers {customer_id:"12345678"})
MERGE (a:cards {card_hash:"45uIic..."})
MERGE (c)-[r:has_card]->(a)
set r.last_transaction = "30-NOV-2016 07:58:42"
set a.card_ct = size(()-[:has_card]->(a))
set c.card_count = size((c)-[:has_card]->())
I'm running this from Python (using py2neo), I also want to return something back that will allow me to kick off a bespoke dijkstra based search of the neighborhood. Any ideas how I'd return some variable based on whether this was a new or existing relationship?
There is no need you to even have the card_ct or card_count properties.
Since neo4j 2.1, getting a count of the number of relationships of a specific type from a node is very efficient. So, every time you need a count, just use SIZE(()-[:has_card]->(node)) or SIZE((node)-[:has_card]->()).
How about something like this. Create a counter on a MATCH and if the counter is greater than zero then it is an existing relationship. Otherwise it is a new relationship.
MERGE (c:customers {customer_id:"12345678"})
MERGE (a:cards {card_hash:"45uIic..."})
MERGE (c)-[r:has_card]->(a)
ON MATCH SET r.num = coalesce(r.num, 0) + 1
set r.last_transaction = "30-NOV-2016 07:58:42"
set a.card_ct = size(()-[:has_card]->(a))
set c.card_count = size((c)-[:has_card]->())
RETURN
CASE
WHEN r.num > 0 THEN false
ELSE true
END as new_relationship
Here's the Cypher I've ended up with, thanks to Dave Bennett for his suggestion. I also realised that I don't need to initiate any further analysis if only 1 customer is associated with 1 card so I've excluded this as well.
MERGE (c:customers {customer_id:"12345678"})
MERGE (a:cards {card_hash:"BFgn..."})
MERGE (c)-[r:has_card]->(a)
ON CREATE SET a.card_scheme = "VISA DEBIT"
, a.card_ct = size(()-[:has_card]->(a))
, c.card_count = size((c)-[:has_card]->())
ON MATCH SET r.ind = 1
SET r.last_transaction = "06-Dec-2016 11:19:13"
RETURN CASE WHEN exists(r.ind)
AND a.card_ct + c.card_count > 2
THEN false
ELSE true END as new_relationship

How to use Create Unique with Cypher

My target is to create node + set new property to it in case not exists
if it's exist I just want to update it's property
Tried this:
MATCH (user:C9 {userId:'44'})
CREATE UNIQUE (user{timestamp:'1111'})
RETURN user
*in case the node with the property userId=44 already existed I just want to set it's property into 1111 else just create it and set it.
error I am getting:
user already declared (line 2, column 16 (offset: 46))
"CREATE UNIQUE (user{timestamp:'1111'})"
should I switch to Merge or?
thanks.
Yes, you should use the MERGE statement.
MERGE (user:C9 {userId:'44'})
// you can set some initial properties when the node is created if required
//ON CREATE SET user.propertykey = 'propertyvalue'
ON MATCH SET user.timestamp = '1111'
RETURN user
You mention unique constraints - I assume you have one set up. You definitely should do to prevent duplicate nodes being created. It will also create a schema index to improve the performance of your node lookup. If you do not yet have a unique constraint then it can be created like so
CREATE CONSTRAINT ON (u:C9) ASSERT u.userId IS UNIQUE
See the Neo4j MERGE documentation.
Finally, to understand what is going on in your query let's have a quick look line by line.
MATCH (user:C9 { userId:'44' })
This matches the node with label :C9 that has a userId property with value 44 and assigns it the identifier user.
CREATE UNIQUE (user{timestamp:'1111'})
This line is simply trying to create a new node with no label and a property timestamp with value '1111'. The exception you are seeing is a result of you using the same user identifier that has already been used in the first line. However, this is not a supported way to use CREATE UNIQUE as it requires a match first and will then create bits of the pattern that does not exist. The upside of this is that it is stopping this unwanted node (user{timestamp:'1111'}) being created in the graph.
RETURN user
This line is pretty self explanatory and is not being reached.
EDIT
There seems to be some confusion surrounding CREATE UNIQUE and when it should be used. This query
CREATE UNIQUE (user:C9 {timestamp:'1111'})
will fail with the message
This pattern is not supported for CREATE UNIQUE
To use CREATE UNIQUE you would first match an existing node and then use that to create a unique pattern in the graph. So to create a relationship from user to a second node you would use
MATCH (user:C9 { userId: '44' }
CREATE UNIQUE (user)-[r:FOO]-(bar)
RETURN r
If there is no relationships of type FOO from user then a new node will be created to represent bar and relationship of type :FOO will be created between them. Conversely, if the MATCH statement does not make a match then no nodes or relationships will be created.

NEO4J WEB ADMIN INTERFACE SOME QUESTIONS

I apologize now for my bad English. I'm Italian
I'm just using Neo4j for the thesis, but I still have doubts about the multi use.
1) I have created from the web interface, two nodes. I realized that Neo4j has given these indices 0 and 1 (for research). Now suppose that I was wrong and I have to delete the node with index 1 .. Once deleted do I create a new one and the system puts index 2.
Practically now the first node with index 0 and the second node with index 2 But I want the second node still has index 1 (basically I want to use the index of the first, as I do?)
2) The same problem with the relationship between two nodes. if I'm wrong to create it, the gate and I create another, I lose the index of the one deleted.
3) If I have to create a relationship between 2 nodes with the double arrow, as I do.
I saw that every arrow must have a label, so if I create a relationship between 1 and 2, and a relationship between 2 and 1, you get the double arrow, but with two labels and does not suit me. Thank you for your help
sorry for my very bad English
You should really try to use your own IDs or unique identifier for your nodes, then you can disregard the internal node IDs all together.
If you begin with this Cypher statement in a new database (you only have to set it once),
CREATE CONSTRAINT ON (node:MyNodeLabel) ASSERT node.myid IS UNIQUE
then you can create nodes and relationship like this,
CREATE (a:MyNodeLabel { myid : 0 })
CREATE (b:MyNodeLabel { myid : 1 })
CREATE (a)-[r:RELTYPE]->(b)
or if you do not write the create statements in the same transaction,
CREATE (:MyNodeLabel { myid : 2 })
CREATE (:MyNodeLabel { myid : 3 })
then later,
MATCH (a:MyNodeLabel { myid : 2 }), (b:MyNodeLabel { myid : 3 })
CREATE (a)-[r:RELTYPE]->(b)
or create two nodes and a relationship at the same time
MERGE (:MyNodeLabel { myid : 4 })-[r:RELTYPE]->(:MyNodeLabel { myid : 5 })
You can of course change MyNodeLabel and myid to any identifier you like.
The problem you have with the relationship labels is purely visual or do I misunderstand you?
You know that you can traverse relationships in any direction so maybe you do not need two relationships?
Here is the documentation for Cypher if you have missed it, http://docs.neo4j.org/chunked/stable/.
ok. sorry. I return at home today.... Another question. when you create a relationship with a label on the arrow. how do sometime in the future to change the label
without delete the relationship? is possible?
Q ok. sorry. I return at home today.... Another question. when you create a relationship with a label on the arrow. how do sometime in the future to change the label without delete the relationship? is possible?
ANS Yes,
You can do that easily you can server for the node and remove label only from the node it will not impact the relation ship , but i will suggest you to assing another one if that was the only label on the node so you can group it properly.
match(n:User{Id:1})
remove n:User set n:DeletedUser
return n

CREATE UNIQUE with labels and properties

I'm using Neo4j 2.0.0-M06. Just learning Cypher and reading the docs. In my mind this query would work, but I should be so lucky...
I'm importing tweets to a mysql-database, and from there importing them to neo4j. If a tweet is already existing in the Neo4j database, it should be updated.
My query:
MATCH (y:Tweet:Socialmedia) WHERE
HAS (y.tweet_id) AND y.tweet_id = '123'
CREATE UNIQUE (n:Tweet:Socialmedia {
body : 'This is a tweet', tweet_id : '123', tweet_userid : '321', tweet_username : 'example'
} )
Neo4j says: This pattern is not supported for CREATE UNIQUE
The database is currently empty on nodes with the matching labels, so there are no tweets what so ever in the Neo4j database.
What is the correct query?
You want to use MERGE for this query, along with a unique constraint.
CREATE CONSTRAINT on (t:Tweet) ASSERT t.tweet_id IS UNIQUE;
MERGE (t:Tweet {tweet_id:'123'})
ON CREATE
SET t:SocialMedia,
t.body = 'This is a tweet',
t.tweet_userid = '321',
t.tweet_username = 'example';
This will use an index to lookup the tweet by id, and do nothing if the tweet exists, otherwise it will set those properties.
I would like to point that one can use a combination of
CREATE CONSTRAINT and then a normal
CREATE (without UNIQUE)
This is for cases where one expects a unique node and wants to throw an exception if the node unexpectedly exists. (Far cheaper than looking for the node before creating it).
Also note that MERGE seems to take more CPU cycles than a CREATE. (It also takes more CPU cycles even if an exception is thrown)
An alternative scenario covering CREATE CONSTRAINT, CREATE and MERGE (though admittedly not the primary purpose of this post).

Resources