Combining related information within a cypher query - neo4j

I have created a knowledge with the nodes and relationships pictured. Each person has any number of jobs and skills connected to them and each Job and Skill can have any number of People connected to them. I would like to be able to search for a particular job (e.g. Security Architect) and return a list of all the people who have been employed_as that job and all of the skills that each person is skilled_in. I have created a query hich retrieves these results, however a new line in the query is created for each skill, duplicating the person details each time. This is the query I have which retrieves those results.
MATCH (j:Job {job_title: "Security Architect"})<-[p_rel:employed_as]-(p:Person)-[skilled_in]->(s:Skill) return p,s,p_rel
Is it possible to create a query that returns all of the skill nodes connected to a person as a single list with the details of that person?

Since you need all skills in single line, you can collect all the skills per person.
MATCH (j:Job {job_title: "Security Architect"})<-[p_rel:employed_as]-(p:Person)
-[skilled_in]->(s:Skill)
RETURN p,p_rel, collect(s) as skills_per_person

Related

Is there a way to preform calculations in a cypher query?

Is there a way to order by a calculated value based on relationships, rather than a label? For reference, I have a database containing users and skills. If applicable, each user node has a relationship with a skill node. Each skill has a specific value tied to it that represents how important that skill is. If a user wants to find similar users, what I am currently doing matching all distinct users with similar skills. What I want to do is sum up the values contained in each skill node that I'm looking for for a particular user, and sort from greatest to least. For example, if I'm looking for people that like to swim, run, and bike if Billy likes to swim and run I would take the values stored in each similar skill and sum them to use as the property to sort by. Is this possible in purely cypher, or would I have to return the list of results and then calculate/sort outside of cypher? If anyone has any other advice on how to better structure the database that would also be helpful.
This is pretty easy in Cypher and is for sure documented in a lot of places.
Here a couple of examples :
Finding users like Bob, based on similar skills, order by sum of skill importance on the skill node :
MATCH (n:User {name: 'Bob'})-[:HAS_SKILL]->(skill)<-[:HAS_SKILL]-(otherUser)
RETURN otherUser.name AS name, sum(skill.score) AS score
ORDER BY score DESC
In some graph models, each user can be associated a score to each skill, in which case the score would be on the relationship between the user and the skill, you can then sum up those as well :
MATCH (n:User {name: 'Bob'})-[r1:HAS_SKILL]->(skill)<-[r2:HAS_SKILL]-(otherUser)
RETURN otherUser.name AS name, sum(r1.score + r2.score) AS score
ORDER BY score DESC

Neo4J query to find same data link to different nodes

Following is what I created in Neo4j:
Nodes: Customer Names, Customer Address and Customer Contact
Linked these nodes based on common relationships between all three.
I can see all three nodes linked in Neo4j. Contact contain email and phone numbers so some cases customer name node is connected to email address, phone number and address.
In my learning curve I am asked to show how many same contacts are used by different customer names also how many same address used by different customer names. Based on my little experience I tried few queries but couldnt reach to results.
Tried following query -
start n=node(*)
match n-[:CONTACT_AT]-()
return distinct n
CONTACT_AT is the relationship between customer name and Contact (email, phone) node.
Your question does not provide enough information about your data model. To save time, I will assume that it looks something like this (without showing all the properties):
(a:Address)<-[:ADDRESS_AT]-(p:Person {name: '...'})-[:CONTACT_AT]->(c:Contact)
With this model, this is how you'd get all the names of the people who have the same Contact:
MATCH (person:Person)-[:CONTACT_AT]->(contact:Contact)
RETURN contact, COLLECT(person.name) AS names;
And this is how you'd get all the names of the people who have the same Address:
MATCH (person:Person)-[:ADDRESS_AT]->(address:Address)
RETURN address, COLLECT(person.name) AS names;

Is there any tool for showing relationships between two entities

I'm looking for a tool that could fit to the next task.
For example, user selects in interface entity University and types in some id-s for searching it and gets the result of universities list related to his request, then he does the same with entity Person and at last he types the maximum relationship length. The result of his request is some graph of relationships for example.
(: Person) -[: IS_BROTHER] ->(: Person) -[: IS_STUDENT] ->(: University)
or he might get several results that fits relationship length
I'm not very experienced with neo4j and don't know if there is any tool to fit this task. Or any other tool not related to neo4j would be fine, but I doubt that sql works fine with relationship search. Thanks.
Edited
I'm loking for user friendly tool that will generate this request without user knowing chypher language at all
Here is a Cypher query that returns all paths that are at most 5 relationships deep between any Person whose ID is in a given list and any University whose ID is in another list:
MATCH path=(p:Person)-[*..5]->(u:University)
WHERE ID(p) IN [1,22,333] AND ID(u) IN [2,444,192,678]
RETURN path;
You could use the neo4j Browser to see the paths.

Searching nodes and properties of nodes

I am trying to create a search function for my meetup app which uses Neo4j as the database. Is there a way to search both nodes (Topic, Department, and Title, getting the people that are attached to them) and properties of nodes (first name, last name, username, bio).
The Person node has a relationship to a Title node (via IS_TITLED) and a relationship to Department node (via EMPLOYED_BY) and relationship to Topic nodes (via INTEREST_OF or SKILL_OF)
Also I would like to make sure that the results are distinct for each person so if the person puts in the title of a person and a department and it gets 2 matches, then the person only returns once.
Your question is very broad, but here is an example query that:
Finds all people employed by the Finance department and have the titled "Clerk".
Ensures they are distinct people.
Returns their first name, last name, username, and bio.
MATCH (d:Department)<-[:EMPLOYED_BY]-(p:Person)-[:IS_TITLED]->(t:Title)
WHERE d.name = "Finance" AND t.name = "Clerk"
WITH DISTINCT p
RETURN p.fname AS firstname, p.lname AS lastname, p.username AS username, p.bio AS bio;
Actually I wasn't looking for an entire application. My final solution was to add,update, and remove documents in ElasticSearch when my nodes where added, update, and removed. Then I use ElasticSearch to find results and return a list of node id's. Then I wrote my Cypher query to pull information using IN for the returned id's to produce the results. It seems to work perfectly. Since I couldn't find an integrated solution for syncing Neo4j and ElasticSearch, I use both libraries in my application and just perform the appropriate action on ElasticSearch when the nodes were effected.

Is this concept applicable for a graph database?

I have been reading about Graph databases and want to know if this type of structure is applicable to it:
Company > Has user Accounts > Accounts send out facebook posts (which are available to all users)
Up to here - I think this makes sense - yes it would be a good use of Graph. A post has a relationship to any accounts and you can find out the direction both ways - posts for a company and which posts were sent by which users or companies.
However
Users get added and deleted on a daily basis and I need a record store of how many there were at a given time
Accounts are getting results for each post (likes/friends) which I need to store on a daily basis
I need to find out how many likes a company received (on any given day)
I also need to find out how many likes a user received
I need to find out how many likes a user received per post
You would need to store Likes as a group and then date-value - can you even have "sub" properties?
I struggle at this point unless you are storing lots of date-value property lists per node. Is that the way you would do it? If I wanted to find out the later 2 points for example would it be as efficient as a RDBMS?
Here is a very simple example of a Graph data model that seems to cover your stated use cases. (Since nodes can have multiple labels, all Company and User nodes are also Entity nodes -- to simplify the model.)
(:Company:Entity {id:100})-[:HAS_USER]->(:User:Entity {id: 200})
(:Entity)-[:SENT]->(:Post {date: 123, msg: "I like cats!"})
(:Entity)-[:LIKES {date: 234}]->(:Post)
Your use cases:
Users get added and deleted on a daily basis and I need a record store of how many there were at a given time.
How to count all users:
MATCH (u:User)
RETURN COUNT(*);
How to count a company's users:
MATCH (c:Company {id:100})-[:HAS_USER]->(u:User)
RETURN COUNT(*);
I need to find out how many likes a company received (on any given day)
MATCH (c:Company {id: 100})-[:SENT]->(p:Post)<-[:LIKES {date:234}]-()
RETURN COUNT(*)
I also need to find out how many likes a user received
MATCH (u:User {id:200})-[:SENT]->(p:Post)<-[:LIKES]-()
RETURN COUNT(*);
I need to find out how many likes a user received per post
MATCH (u:User {id:200})-[:SENT]->(p:Post)<-[:LIKES]-()
RETURN p, COUNT(*)
You would need to store Likes as a group and then date-value - can you even have "sub" properties?
You do not need to explicitly group likes by date (if that is what you mean). Such "groupings" can be easily obtained by the appropriate query (e.g., in #2 above).

Resources