Based on the model here, I am trying to find the closest meeting room based on the proximity of the rooms in this model. I wanted to the results like this,
+-------+----------+--+
| Room | Distance | |
+-------+----------+--+
| room1 | 3 | |
| room2 | 3 | |
| room3 | 4 | |
| room4 | 4 | |
+-------+----------+--+
My model:
I have tried this query:
MATCH (p:Person {name:"test"})-[r*2..]->(f:Floor)<-[:ROOM_LOCATED_IN_FLOOR]-(r:Room)
RETURN p, f, r
which just gives the meeting rooms the person is located. But I wanted to traverse through other rooms in different floors.
Here a sample data for testing:
CREATE (p:Person)
CREATE (d:Desk)
CREATE (f1:Floor)
CREATE (f2:Floor)
CREATE (r1:Room {name : 'room1'})
CREATE (r2:Room {name : 'room2'})
CREATE (r3:Room {name : 'room3'})
CREATE (r4:Room {name : 'room4'})
CREATE (p)-[:SEATED_AT]->(d)-[:LOCATED_IN]->(f1)-[:HAS_NEXT]->(f2)
CREATE (f1)<-[:PART_OF]-(r1)
CREATE (f1)<-[:PART_OF]-(r2)
CREATE (f2)<-[:PART_OF]-(r3)
CREATE (f2)<-[:PART_OF]-(r4)
Then, you can get the desired result with size() and relationships() functions:
MATCH p = (:Person)-[*]-(r:Room)
RETURN r.name as Room, size( relationships(p) ) as Distance
ORDER BY Distance
The output will be:
╒═══════╤══════════╕
│"Room" │"Distance"│
╞═══════╪══════════╡
│"room1"│3 │
├───────┼──────────┤
│"room2"│3 │
├───────┼──────────┤
│"room3"│4 │
├───────┼──────────┤
│"room4"│4 │
└───────┴──────────┘
Related
I'm importing a dataset of the following structure into Neo4j:
| teacher | student | period |
|:---------------:|---------|:------:|
| Mr. Smith | Michael | 1 |
| Mrs. Oliver | Michael | 2 |
| Mrs. Roth | Michael | 3 |
| Mrs. Oliver | Michael | 4 |
| Mrs. Oliver | Susan | 1 |
| Mrs. Roth | Susan | 2 |
My goal is to create a graph where a teacher "sends" students from one period to the next, showing the flow of students between teachers. The above graph for instance, would look like this:
Using words, my logic looks like this:
Generate a unique node for every teacher
For each student, create a relationship connecting the earliest period to the next earliest period, until the latest period is reached.
My code so far completes the first step:
LOAD CSV WITH HEADERS FROM 'file:///neo_sample.csv' AS row // loads local file
MERGE(a:teacher {teacher: row.teacher}) // used merge instead of create to produce unique teacher nodes.
Here is how you can produce your illustrated graph.
Assuming your CSV file looks like this:
teacher;student;period
Mr. Smith;Michael;1
Mrs. Oliver;Michael;2
Mrs. Roth;Michael;3
Mrs. Oliver;Michael;4
Mrs. Oliver;Susan;1
Mrs. Roth;Susan;2
then this query should work:
LOAD CSV WITH HEADERS FROM 'file:///neo_sample.csv' AS row FIELDTERMINATOR ';'
WITH row.teacher AS t, row.student AS s, row.period AS p
ORDER BY p
WITH s, COLLECT({t:t, p:p}) AS data
FOREACH(i IN RANGE(0, SIZE(data)-2) |
MERGE(a:Teacher {name: data[i].t})
MERGE(b:Teacher {name: data[i+1].t})
MERGE (a)-[:SENDS {student: s, period: data[i].p}]->(b)
)
I am new to Neo4j/Cypher and I am having some trouble grouping by relationship properties.
Firstly a toy example:
CREATE (A1:Worker {ref:"A1"})
CREATE (A2:Worker {ref:"A2"})
CREATE (B1:Worker {ref:"B1"})
CREATE (B2:Worker {ref:"B2"})
CREATE (A1)-[:StreamsTo {type:"stream1"}]->(B1)
CREATE (A1)-[:StreamsTo {type:"stream2"}]->(B1)
CREATE (A1)-[:StreamsTo {type:"stream1"}]->(B2)
CREATE (A1)-[:StreamsTo {type:"stream2"}]->(B2)
CREATE (A2)-[:StreamsTo {type:"stream1"}]->(B1)
CREATE (A2)-[:StreamsTo {type:"stream1"}]->(B2)
This creates a graph with 4 worker nodes, where the A nodes are connected to the B nodes by relationships that can have different values for the "type" property. In this case A1 is connected to the B's by 2 different types of streams and A2 only by 1 type:
What I want to be able to do is to count the number of outgoing relationships from each source node but have them grouped by the various values of the "type" property in the relationship to get something like this:
+--------+-------------+---------------+
| Worker | StreamType | OutgoingCount |
+--------+-------------+---------------+
| A1 | stream1 | 2 |
+--------+-------------+---------------+
| A1 | stream2 | 2 |
+--------+-------------+---------------+
| A2 | stream1 | 2 |
+--------+-------------+---------------+
So far I can get the total outgoing and number of distinct outgoing types:
MATCH (source:Worker)-[st:StreamsTo]->(:Worker)
RETURN source.ref as Source,
COUNT(st) as TotalOutgoing,
COUNT(distinct st.type) as NumberOfTypes;
Any hints would be helpful.
So it turns out to be trivial! I had not understood that what you return along with the COUNT() function performs the group by:
MATCH (source:Worker)-[st:StreamsTo]->(:Worker)
RETURN source.ref as Worker,
st.type as StreamType,
COUNT(st) as OutgoingCount;
I've got a problem with simple matching.
For example,
I have some node
start startNode = node(0)
It has a relationship with another one. One of the relationship's properties is idOfThirdNode with id(thirdNode).
I found out that start point = node( ) get only digits as arguments and any toInt(rel.idOfThirdNode) is not available at all, as other match(point:_Node) where id(point) = rel.idOfThirdNode
Find node by property is not a problem. But it isn't possible to set new duplicate id-property.
Have this problem any decision or only saving this property in model and begining of new matching with this property like id?
Edit:
Earlier I have had in result of such action:
start startNode = node({0})
optional match startNode-[r:REL]-(relNode: _Node)
return distinct startNode, id(r) as linkId, id(relNode) as nodeId,
r.idOfthirdNode as point
beautiful table with nulls in some fields
______________________________________
| StartNode| linkId | nodeId | point |
--------------------------------------
| startNode| 1 | 2 | null |
| info | | | |
-------------------------------------
| startNode| 3 | 4 | 5 |
| info | | | |
But now this "where" make disabled all null matching
start startNode = node({0})
optional match startNode-[r:REL]-(relNode: _Node), (pointNode:_Node)
where id(pointNode) = r.idOfthirdNode
return distinct startNode, id(r) as linkId, id(relNode) as nodeId,
collect({pointNode.name, id:id(pointNode)}) as point
and I get only second line.
You should be able to do something like this:
MATCH (point:_Node), (node:Label)
WHERE ID(point) = node.idOfThirdNode
RETURN *
But I've never actually seen that done because relationships are so much better than foreign keys
This should work for you:
START startNode = node(0)
MATCH (startNode)-[rel]->(secondNode), (thirdNode:_Node)
WHERE ID(thirdNode) = rel.idOfThirdNode
RETURN startNode, secondNode, thirdNode
I have the following neo4j database:
http://console.neo4j.org/?id=gkkmha
I then run the following query:
MATCH (person:Person)-[:plays]->(instrument:Instrument {name: 'Drums'})
OPTIONAL MATCH (band:Band { name: 'bandname' })-[:genre]->(genre:Genre)<-[:likes]-(person)
OPTIONAL MATCH (band)-[:influenced]->(influence:Influence)<-[:influenced]-(person)
RETURN person.name, COLLECT(genre.name) as matched_genres, COLLECT (influence.name) as matched_influences, (count(genre)/4.0) as score
ORDER BY score DESC
I want to be able to find musicians who play the specified instrument but also have similar genre matches and influences to the band. So far I've got it working for matching genres and returning a list of those genres, but I can't make it do the same for influences. I want it to return a list of matching influences as well.
Ideally it'd also get the total number of genres and influences the band is associated with (though this is just a nice to have).
Current output:
+-----------------------------------------------------------------+
| person.name | matched_genres | matched_influences | score |
+-----------------------------------------------------------------+
| "Robert Smith" | ["Soul","Motown"] | [] | 0.5 |
| "Alex Smith" | ["Soul"] | [] | 0.25 |
| "Mr Drummer" | [] | [] | 0.0 |
+-----------------------------------------------------------------+
3 rows
54 ms
Any thoughts?
i believe you got there a typo, instead of :influenced try :Influenced
MATCH (person:Person)-[:plays]->(instrument:Instrument { name: 'Drums' })
OPTIONAL
MATCH (band:Band { name: 'bandname' })-[:genre]->(genre:Genre)<-[:likes]-(person)
OPTIONAL
MATCH (band)-[:Influenced]->(influence:Influence)<-[:Influenced]-(person)
RETURN person.name, COLLECT(genre.name) AS matched_genres, COLLECT(influence.name) AS matched_influences,(count(genre)/4.0) AS score
ORDER BY score DESC
I have recently begun using Neo4j and am struggling to understand how things work. I am trying to create relationships between nodes that I created earlier in my script. The cypher query that I found looks like it should work, but I don't know how to get the id's to replace the #'s
START a= node(#), b= node(#)
CREATE UNIQUE a-[r:POSTED]->b
RETURN r
If you want to use plain cypher, the documentation has a lot of usage examples.
When you create nodes you can return them (or just their ids by returning id(a)), like this:
CREATE (a {name:'john doe'}) RETURN a
This way you can keep the id around to add relationships.
If you want to attach relationships later, you should not use the internal id of the nodes to reference them from external system. They can for example be re-used if you delete and create nodes.
You can either search for a node by scanning over all and filtering using WHERE or add an index to your database, e.g. if you add an auto_index on name:
START n = node:node_auto_index(name='john doe')
and continue from there. Neo4j 2.0 will support index lookup transparently so that MATCH and WHERE should be as efficient.
If you are using python, you can also take a look at py2neo which provides you with a more pythonic interface while using cypher and the REST interface to communicate with the server.
This could be what you are looking for:
START n = node(*) , x = node(*)
Where x<>n
CREATE UNIQUE n-[r:POSTED]->x
RETURN r
It will create POSTED relationship between all the nodes like this
+-----------------------+
| r |
+-----------------------+
| (0)-[10:POSTED]->(1) |
| (0)-[10:POSTED]->(2) |
| (0)-[10:POSTED]->(3) |
| (1)-[10:POSTED]->(0) |
| (1)-[10:POSTED]->(2) |
| (1)-[10:POSTED]->(3) |
| (2)-[10:POSTED]->(0) |
| (2)-[10:POSTED]->(1) |
| (2)-[10:POSTED]->(3) |
| (3)-[10:POSTED]->(0) |
| (3)-[10:POSTED]->(1) |
| (3)-[10:POSTED]->(2) |
And if you don't want a relation between the reference node(0) and the other nodes, you can make the query like this
START n = node(*), x = node(*)
WHERE x<>n AND id(n)<>0 AND id(x)<>0
CREATE UNIQUE n-[r:POSTED]->x
RETURN r
and the result will be like that:
+-----------------------+
| r |
+-----------------------+
| (1)-[10:POSTED]->(2) |
| (1)-[10:POSTED]->(3) |
| (2)-[10:POSTED]->(1) |
| (2)-[10:POSTED]->(3) |
| (3)-[10:POSTED]->(1) |
| (3)-[10:POSTED]->(2) |
On the client side using Javascript I post the cypher query:
start n = node(*) WHERE n.name = '" + a.name + "' return n
and then parse the id number from response "self" in the form of:
server_url:7474/db/data/node/node_id
After hours of trying to figure this out, I finally found what I was looking for. I was struggling with how nodes were getting returned and found that
userId=person[0][0][0].id
would return what I wanted. Thanks for all your help though!
Using py2neo, the way I've found that is really useful is to use the remote module.
from py2neo import Graph, remote
graph = Graph()
graph.run('CREATE (a)-[r:POSTED]-(b)')
a = graph.run('MATCH (a)-[r]-(b) RETURN a').evaluate()
a_id = remote(a)._id
b = graph.run('MATCH (a)-[r]-(b) WHERE ID(a) = {num} RETURN b', num=a_id).evaluate()
b_id = remote(b)._id
graph.run('MATCH (a)-[r]-(b) WHERE ID(a)={num1} AND ID(b)={num2} CREATE (a)-[x:UPDATED]-(b)', num1=a_id, num2=b_id)
The remote function takes in a py2neo Node object and has an _id attribute that you can use to return the current ID number from the graph database.