Neo4j rb finding connectivity between 2 nodes - ruby-on-rails

I am using neo4j.rb in my rails application.
I already have two nodes n1 and n2 retrieved from the database.
Now i need to check if they have a path between them, i was thinking of using cypher queries using Neo4j::Session.query
But since i already have the two nodes i do not want to retrieve them again inside the query,(does it affect performance?) is there a way to do it?
I know i can use query1 = n1.query_as(:node1) and use it as the node identifier but how can i introduce n2 within the same query object so that i can check connectivity between them.
I want something equivalent to the query
RETURN
CASE
WHEN node1-[*..6]-node2
THEN 'Connected within 6 nodes'
ELSE 'Not connected within 6'
END
Where i already have node1 and node2.
Is a way to do this and also can this be done without using the CYPHER DSL?

Here you go!
n1.query_as(:n1).match_nodes(n2: n2).match('n1-[*1..6]-n2').count
If you want to avoid the Cypher DSL I think you can do that with associations. As a starter example to traverse one level of relationships you can do this:
class N1Class
include Neo4j::ActiveNode
has_many :out, :test_association, type: :TEST_ASSOCIATION, model_class: :N2Class
end
n1.test_association.include?(n2)
That will test if they are directly connected via the test_association association. You can even specify false for your type to ignore the direction and false for the model_class to ignore the target labels.
To get the variable length you can do this:
n1.test_association(nil, nil, rel_length: 1..6).include?(n2)

Related

Create doesn't make all nodes and relationships appear

I just downloaded and installed Neo4J. Now I'm working with a simple csv that is looking like that:
So first I'm using this to merge the nodes for that file:
LOAD CSV WITH HEADERS FROM 'file:///Athletes.csv' AS line
MERGE(Rank:rank{rang: line.Rank})
MERGE(Name:name{nom: line.Name})
MERGE(Sport:sport{sport: line.Sport})
MERGE(Nation:nation{pays: line.Nation})
MERGE(Gender: gender{genre: line.Gender})
MERGE(BirthDate:birthDate{dateDeNaissance: line.BirthDate})
MERGE(BirthPlace: birthplace{lieuDeNaissance: line.BirthPlace})
MERGE(Height: height{taille: line.Height})
MERGE(Pay: pay{salaire: line.Pay})
and this to create some constraint for that file:
CREATE CONSTRAINT ON(name:Name) ASSERT name.nom IS UNIQUE
CREATE CONSTRAINT ON(rank:Rank) ASSERT rank.rang IS UNIQUE
Then I want to display to which country the athletes live to. For that I use:
Create(name)-[:WORK_AT]->(nation)
But I have have that appear:
I would like to know why I have that please.
I thank in advance anyone that takes time to help me.
Several issues come to mind:
If your CREATE clause is part of your first query: since the CREATE clause uses the variable names name and nation, and your MERGE clauses use Name and Nation (which have different casing) -- the CREATE clause would just create new nodes instead of using the Name and Nation nodes.
If your CREATE clause is NOT part of your first query: your CREATE clause would just create new nodes (since variable names, even assuming they had the same casing, are local to a query and are not stored in the DB).
Solution: You can add this clause to the end of the first query:
CREATE (Name)-[:WORK_AT]->(Nation)
Yes, Agree with #cybersam, it's the case sensitive issue of 'name' and 'nation' variables.
My suggesttion:
MERGE (Name)-[:WORK_AT]->(Nation)
I see that you're using MERGE for nodes, so just in case any values of Name or Nation duplicated, you should use MERGE instead of CREATE.

how do I use neography to find the relationship type and nodes for a given node?

I have been trying to use neography for the following basic use case, but can't seem to get it to work:
For a given node, tell me all the associated relationships for that node.
For a given node and a specific relationship, return the node or nodes in that relationships?
I followed the examples from here: https://maxdemarzi.com/2012/01/04/getting-started-with-ruby-and-neo4j/
I tried the following code:
def create_person(name)
Neography::Node.create("name" => name)
end
johnathan = create_person('Johnathan')
mark = create_person('Mark')
phil = create_person('Phil')
mary = create_person('Mary')
luke = create_person('Luke')
johnathan.both(:friends) << mark
First, I want to see the associated relationships that are incoming. My expectation is to see relationship with type :friends:
johnathan.incoming
=> #<Neography::NodeTraverser:0x0000000133f1c0 #from=#<Neography::Node name="Johnathan">, #order="depth first", #uniqueness="none", #relationships=[{"type"=>"", "direction"=>"in"}]>
I tried relationships:
2.2.1 :060 > johnathan.incoming.relationships
=> [{"type"=>"", "direction"=>"in"}]
My expectation would be to see "type"=>":friends" but I don't.
However, when I try the following, I do, but it doesn't work for my use case since I want to know what the relationships are without knowing in advance what they are:
2.2.1 :061 > johnathan.incoming(:friends).relationships
=> [{"type"=>"friends", "direction"=>"in"}]
Second use case is to actually retrieve the nodes, which does work.
Question:
How can I get the types of relationships associated for any given node?
I think I am close to figuring it out:
johnathan.rels.map{|n| n}.first.rel_type
=> "friends"
You are right, almost there. The documentation for this is at the bottom of https://github.com/maxdemarzi/neography/wiki/Phase-2-Node-relationships#retrieval-by-type but basically:
n1 = johnathan
n1.rels # Get node relationships
n1.rels(:friends) # Get friends relationships
n1.rels(:friends).outgoing # Get outgoing friends relationships
n1.rels(:friends).incoming # Get incoming friends relationships
n1.rels(:friends, :work) # Get friends and work relationships
n1.rels(:friends, :work).outgoing # Get outgoing friends and work relationships
There is no way to get just what are all the relationship types connected to me as far as I know, but that would be a good improvement in the Neo4j REST API.
The functionality exists in the Java API, see https://neo4j.com/docs/java-reference/current/javadocs/org/neo4j/graphdb/Node.html#getRelationshipTypes--

How to create a relation between two node having same node name in neo4j

I want to create a friends relation between abcd node and vbnm node having same node-name - Student
neo4j graph database visualization
I execute the following query, It doesn't show me any error but this query doesn't create any relation
match(Student:Stu),(Student:Stu)where Student.name="abcd" AND Student.name="vbnm" create(Student)-[fr:friends]->(Student)
You need use different variable name:
match(Student1:Stu),(Student2:Stu)
where Student1.name="abcd" AND
Student2.name="vbnm"
create(Student1)-[fr:friends]->(Student2)
I think you are confused by the syntax a little bit. Let me give you an example of a MATCH query syntax.
MATCH (variable1:Label),(variable2:Label) where variable1.foo = variable2.foo
You mixed label and variable in your query and each entity should have its own variable (variable1 and variable2) so you can interact with them.
So in your case the optimal query looks something like:
MATCH (s1:Student),(s2:Student ) where s1.name="abcd" AND s2.name="vbnm"
CREATE (s1)-[:friends]->(s2)
Note that you do not need to assign a variable to [:friends] relationship as you do not interact with it later in the same query.

Find path in Neo4j with directed edges

This is my first attempt at Neo4j, please excuse me if I am missing something very trivial.
Here is my problem:
Consider the graph as created in the following Neo4j console example:
http://console.neo4j.org/?id=y13kbv
We have following nodes in this example:
(Person {memberId, memberName, membershipDate})
(Email {value, badFlag})
(AccountNumber {value, badFlag})
We could potentially have more nodes capturing features related to a Person like creditCard, billAddress, shipAddress, etc.
All of these nodes will be the same as Email and AccountNumber nodes:
(creditCard {value, badFlag}), (billAddress {value, badFlag}),etc.
With the graph populated as seen in the Neo4j console example, assume that we add one more Person to the graph as follows:
(p7:Person {memberId:'18' , memberName:'John', membershipDate:'12/2/2015'}),
(email6:Email {value: 'john#gmail.com', badFlag:'false'}),
(a2)-[b13:BELONGS_TO]->(p7),
(email6)-[b14:BELONGS_TO]->(p7)
When we add this new person to the system, the use case is that we have to check if there exists a path from features of the new Person ("email6" and "a2" nodes) to any other node in the system where the "badFlag=true", in this case node (a1 {value:1234, badFlag:true}).
Here, the resultant path would be (email6)-[BELONGS_TO]->(p7)<-[BELONGS_TO]-(a2)-[BELONGS_TO]->(p6)<-[BELONGS_TO]-(email5)-[BELONGS_TO]->(p5)<-[BELONGS_TO]-(a1:{badFlag:true})
I tried something like this:
MATCH (newEmail:Email{value:'john#gmail.com'})-[:BELONGS_TO]->(p7)-[*]-(badPerson)<-[:BELONGS_TO]-(badFeature{badFlag:'true'}) RETURN badPerson, badFeature;
which seems to work when there is only one level of chaining, but it doesn't work when the path could be longer like in the case of Neo4j console example.
I need help with the Cypher query that will help me solve this problem.
I will eventually be doing this operation using Neo4j's Java API using my application. What could be the right way to go about doing this using Java API?
You had a typo in you query. PART_OF should be BELONGS_TO. This should work for you:
MATCH (newEmail:Email {value:'john#gmail.com'})-[:BELONGS_TO]->(p7)-[*]-(badPerson)<-[:BELONGS_TO]-(badFeature {badFlag:'true'})
RETURN badPerson, badFeature;
Aside: You seem to use string values for all properties. I'd replace the string values 'true' and 'false' with the boolean values true and false. Likewise, values that are always numeric should just use integer or float values.

py2neo return number of nodes and relationships created

I need to create a python function such that it adds nodes and relationship to a graph and returns the number of created nodes and relationships.
I have added the nodes and relationship using graph.cypher.execute().
arr_len = len(dic_st[story_id]['PER'])
for j in dic_st[story_id]['PER']:
graph.cypher.execute("MERGE (n:PER {name:{name}})",name = j[0].upper()) #creating the nodes of PER in the story
print j[0]
for j in range(0,arr_len):
for k in range(j+1,arr_len):
graph.cypher.execute("MATCH (p1:PER {name:{name1}}), (p2:PER {name:{name2}}) WHERE upper(p1.name)<>upper(p2.name) CREATE UNIQUE (p1)-[r:in_same_doc {st_id:{st_id}}]-(p2)", name1=dic_st[story_id]['PER'][j][0].upper(),name2=dic_st[story_id]['PER'][k][0].upper(),st_id=story_id) #linking the edges for PER nodes
What I need is to return the number of new nodes and relationships created.
What I get to know from the neo4j documentation is that there is something called "ON CREATE" and "ON MATCH" for MERGE in cypher, but thats not being very useful.
The browser interface for neo4j do actually shows the number of nodes and relationship updated. This is what I need to return, but I am not getting quite the way for it to access it.
Any help please.
In case you need the exact counts of properties either created or updated then you have use "Match" with "Create" or "Match" with "Set" and then count the size of results. Merge may not return which ones are updated and which ones are created.
When you post your query against the Cypher endpoint of the neo4j REST API without using py2neo, you can include the argument "includeStats": true in your post request to get the node/relationship statistics. See this question for an example.
As far as I can tell, py2neo currently does not support additional parameters for the Cypher query (even though it is using the same API endpoints under the hood).
In Python, you could do something like this (using the requests and json packages):
import requests
import json
payload = {
"statements": [{
"statement": "CREATE (t:Test) RETURN t",
"includeStats": True
}]
}
r = requests.post('http://your_server_host:7474/db/data/transaction/commit',
data=json.dumps(payload))
print(r.text)
The response will include statistics about the number of nodes created etc.
{
"stats":{
"contains_updates":true,
"nodes_created":1,
"nodes_deleted":0,
"properties_set":1,
"relationships_created":0,
"relationship_deleted":0,
"labels_added":1,
"labels_removed":0,
"indexes_added":0,
"indexes_removed":0,
"constraints_added":0,
"constraints_removed":0
}
}
After executing your query using x = session.run(...) you can use x.summary.counters to get the statistics noted in Martin Perusse's answer. See the documentation here.
In older versions the counters are available as a "private" field under x._summary.counters.

Resources